在Django Admin中更好地表示多对多关系的方法

5 投票
3 回答
3274 浏览
提问于 2025-04-15 19:42

我遇到了一个独特的问题,想在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 个回答

0

Django的管理后台没有直接提供你需要的功能。

不过,你可以自己创建一个自定义的视图,来实现这个功能。你可以在admin.ModelAdmin类中添加额外的视图,这样就能满足你的需求了。

0

这可能是一个很糟糕的设计。你的数据库表应该包含正确的价格。

你的应用现在需要做两件事。首先,它必须从其他地方获取一个默认价格(这个价格不在这个表里),其次,它还需要从这个表里获取一个覆盖价格,并把这两条信息结合起来。

你不能轻易地让SQL与你展示的那种表格配合使用。

你也不能轻松地让Django的管理后台与这样的表格一起工作。你可以尝试创建一个表格模板,但这个模板是针对这种多对多关系的,所以你还得定制Django的管理视图,让它在一个多对多表上使用你的模板,而其他表则使用普通的默认模板。

要创建这个表格,你必须先获取所有的国家和产品。然后你需要创建一个合适的列表的列表。接着,你可以写自己的模板来展示这些数据。当国家数量超过12个时,这个表格会变得非常宽,几乎没什么用。但对于前几个国家,你可以让它正常工作。

你需要创建自己的模板和视图函数来完成这个任务。

编辑

“我愿意考虑其他方法(包括改变模型结构),只要它能满足要求。”

哪个要求呢?是那个需要两次查询才能找到价格的糟糕设计吗?这是必须的吗?

还是那个非常复杂的表格布局?这是必须的吗?

现在不清楚“要求”是什么,所以无法提出任何替代方案。我们只能说:

  1. 一个分别查询基础价格和覆盖价格的SQL设计会更慢且更复杂。

  2. 一个从“动态默认值”加载的单一值的SQL设计,用户可以选择是否更改,这要简单得多。这可以通过initial参数来实现。http://docs.djangoproject.com/en/dev/ref/forms/fields/#initial

  3. SQL不容易将多行数据转换为表格结构。这需要复杂的SQL(超出了ORM的能力)或在视图函数中进行Python处理。

  4. Django的管理后台根本不支持表格结构。

3

我找到了答案,这里是我对自己问题的解答……让我和你分享一下……我把模型改成了下面这样……

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]

虽然这不是我最初想的那种方式,但结果却给了我更好的解决方案……

撰写回答