在Django Admin中更好地表示多对多关系的方法
我遇到了一个独特的问题,想在Django的后台管理界面中处理。
我的模型结构如下...
class Product(models.Model):
name = models.CharField(max_length = 100)
base_price = models.DecimalField(max_digits = 5, decimal_places = 2)
def __unicode__(self):
return self.name
class Country(models.Model):
name = models.CharField(max_length = 2)
base_price = models.DecimalField(max_digits = 5, decimal_places = 2)
def __unicode__(self):
return self.name
class CountryProduct(models.Model):
country = models.ForeignKey(Country)
product = models.ForeignKey(Product)
overriden_price = models.DecimalField(max_digits = 5, decimal_places = 2)
class Meta:
unique_together = (("country", "product"),)
如上所示,产品和国家之间是多对多的关系……我想为特定的国家和产品提供一个后台界面,以便可以修改基础价格。
一种可能的界面设计如下,这里的短横线(-)表示默认价格,而数字则表示特定国家和产品的修改价格。
countries -> | US | UK
products | |
---------------------------
Product1 | - | 10
Product2 | 5 | 7
但是我不知道该怎么做……
我愿意考虑其他方法(包括改变模型结构),只要能满足需求……任何建议对我来说都非常有帮助……
提前谢谢你们 :)
3 个回答
Django的管理后台没有直接提供你需要的功能。
不过,你可以自己创建一个自定义的视图,来实现这个功能。你可以在admin.ModelAdmin类中添加额外的视图,这样就能满足你的需求了。
这可能是一个很糟糕的设计。你的数据库表应该包含正确的价格。
你的应用现在需要做两件事。首先,它必须从其他地方获取一个默认价格(这个价格不在这个表里),其次,它还需要从这个表里获取一个覆盖价格,并把这两条信息结合起来。
你不能轻易地让SQL与你展示的那种表格配合使用。
你也不能轻松地让Django的管理后台与这样的表格一起工作。你可以尝试创建一个表格模板,但这个模板是针对这种多对多关系的,所以你还得定制Django的管理视图,让它在一个多对多表上使用你的模板,而其他表则使用普通的默认模板。
要创建这个表格,你必须先获取所有的国家和产品。然后你需要创建一个合适的列表的列表。接着,你可以写自己的模板来展示这些数据。当国家数量超过12个时,这个表格会变得非常宽,几乎没什么用。但对于前几个国家,你可以让它正常工作。
你需要创建自己的模板和视图函数来完成这个任务。
编辑
“我愿意考虑其他方法(包括改变模型结构),只要它能满足要求。”
哪个要求呢?是那个需要两次查询才能找到价格的糟糕设计吗?这是必须的吗?
还是那个非常复杂的表格布局?这是必须的吗?
现在不清楚“要求”是什么,所以无法提出任何替代方案。我们只能说:
一个分别查询基础价格和覆盖价格的SQL设计会更慢且更复杂。
一个从“动态默认值”加载的单一值的SQL设计,用户可以选择是否更改,这要简单得多。这可以通过
initial
参数来实现。http://docs.djangoproject.com/en/dev/ref/forms/fields/#initialSQL不容易将多行数据转换为表格结构。这需要复杂的SQL(超出了ORM的能力)或在视图函数中进行Python处理。
Django的管理后台根本不支持表格结构。
我找到了答案,这里是我对自己问题的解答……让我和你分享一下……我把模型改成了下面这样……
class Product(models.Model):
name = models.CharField(max_length = 100)
base_price = models.DecimalField(max_digits = 5, decimal_places = 2)
def __unicode__(self):
return self.name
class Country(models.Model):
name = models.CharField(max_length = 2)
base_price = models.DecimalField(max_digits = 5, decimal_places = 2)
products = models.ManyToManyField(Product, through = 'CountryProduct')
def __unicode__(self):
return self.name
class CountryProduct(models.Model):
country = models.ForeignKey(Country)
product = models.ForeignKey(Product)
overriden_price = models.DecimalField(max_digits = 5, decimal_places = 2)
class Meta:
unique_together = (("country", "product"),)
class CountryProductInline(admin.TabularInline):
model = CountryProduct
class CountryAdmin(admin.ModelAdmin):
inlines = [CountryProductInline]
class ProductAdmin(admin.ModelAdmin):
inlines = [CountryProductInline]
虽然这不是我最初想的那种方式,但结果却给了我更好的解决方案……