在 Django 中建模异构多对多关系的最佳方法是啥?

Posted

技术标签:

【中文标题】在 Django 中建模异构多对多关系的最佳方法是啥?【英文标题】:What is the best way to model a heterogenous many-to-many relationship in Django?在 Django 中建模异构多对多关系的最佳方法是什么? 【发布时间】:2012-04-25 04:17:39 【问题描述】:

我已经搜索了一段时间,但似乎找不到与此相关的现有问题(尽管可能是不知道术语的问题)。

我是 Django 新手,并且一直在尝试采用随着时间的推移应该非常可扩展的设计,并使其与 Django 的 ORM 一起使用。本质上,它是一系列使用共享联结表的多对多关系。

设计是一个通用的游戏制作系统,上面写着“如果你满足[要求],你可以使用[成本]作为材料来创造[奖励]。”这允许使用相同系统从任意数量的商店出售物品,并且通用性足以支持广泛的机制 - 我在过去看到它成功使用过。

Django 不支持共享同一个联结表的多个 M2M 关系(显然是因为它无法计算出反向关系),所以我似乎有这些选择:

让它创建自己的联结表,最终有六个或更多,或者 使用联结表的外键代替内置的 MTM 关系。

第一个选项有点混乱,因为我知道我最终必须在联结表中添加额外的字段。第二个选项效果很好。不幸的是,由于没有从联结表 BACK 到其他每个表的外键,我一直在与管理系统作斗争,以让它做我想做的事。

以下是受影响的型号:

class Craft(models.Model):
    name        = models.CharField(max_length=30)
    description = models.CharField(max_length=300, blank=True)
    cost        = models.ForeignKey('Container', related_name="craft_cost")
    reward      = models.ForeignKey('Container', related_name="craft_reward")
    require     = models.ForeignKey('Container', related_name="craft_require")

class ShopContent(models.Model):
    shopId      = models.ForeignKey(Shop)
    cost        = models.ForeignKey('Container', related_name="shop_cost")
    reward      = models.ForeignKey('Container', related_name="shop_reward")
    require     = models.ForeignKey('Container', related_name="shop_require")
    description = models.CharField(max_length=300)

class Container(models.Model):
    name        = models.CharField(max_length=30)

class ContainerContent(models.Model):
    containerId = models.ForeignKey(Container, verbose_name="Container")
    itemId      = models.ForeignKey(Item, verbose_name="Item")
    itemMin     = models.PositiveSmallIntegerField(verbose_name=u"min amount")
    itemMax     = models.PositiveSmallIntegerField(verbose_name=u"max amount")
    weight      = models.PositiveSmallIntegerField(null=True, blank=True)
    optionGroup = models.PositiveSmallIntegerField(null=True, blank=True,
                                                   verbose_name=u"option group")

有没有一种更简单、可能很明显的方法来让它发挥作用?我正在尝试允许对 Craft 编辑界面上每个相关列的 ContainerContent 信息进行内联编辑。

【问题讨论】:

【参考方案1】:

听起来您有一种“交易”,它具有名称、描述和类型,并定义了成本、奖励和要求。您应该将其定义为单个模型,而不是多个模型(ShopContentCraft 等)。

class Transaction(models.Model):
    TYPE_CHOICES = (('Craft', 0),
                    ('Purchase', 1),
                   )
    name        = models.CharField(max_length=30)
    description = models.CharField(max_length=300, blank=True)
    cost        = models.ForeignKey('Container')
    reward      = models.ForeignKey('Container')
    require     = models.ForeignKey('Container')
    type        = models.IntegerField(choices = TYPE_CHOICES)

现在Shop 等可以有一个ManyToManyFieldTransaction

无论您是否使用此特定模型,costrewardrequire 关系都应该在一个地方 -- 如上所述,或者在 OneToOneCraftShopContent 的关系中等等。正如你所猜想的,你不应该有一大堆复杂的多对多 through 表,它们都完全相同。


您在帖子底部提到您是

尝试允许在Craft 编辑界面上的每个相关列中对ContainerContent 信息进行内联编辑。

如果您要对多个级别的关系进行建模,并使用管理应用程序,则需要应用某种 nested inline 补丁,或使用某种链接方案,例如我在最近的问题中使用的那种, How do I add a link from the Django admin page of one object to the admin page of a related object?

【讨论】:

另一个版本的链接:***.com/questions/10115137/… 嵌套的内联信息真的很有用。不过,我编辑了问题以更好地显示关系问题 - 多个表将通过这个表。不过,如果我最终这样做,它会减少三分之一所需的桌子数量! @ThreeHams 好的,如果你有多次相同的关系,你只需要建模一次。要么像我的示例那样泛化为单个模型,要么使用显式 OneToOne 字段,或使用带有子类化的隐式 OneToOne 字段。 事务更改实际上效果很好。希望它能够经受住未来的变化,但它从设计中删除了两个表和三个复杂的关系。谢谢您的帮助!就内联编辑而言,我已经意识到我计划的 CMS 的范围远远超出了内置管理界面。考虑到我不断遇到的限制,最好使用现有的作为指导,从头开始构建我的。【参考方案2】:

我是smelling 这里有些东西太复杂了,但我可能错了。作为开始, 这更好吗? (ContainerContent后面会想办法)

class Cost(models.Model):
    name        = models.CharField(max_length=30)

class Reward(models.Model):
    name        = models.CharField(max_length=30)

class Require(models.Model):
    name        = models.CharField(max_length=30)

class Craft(models.Model):
    name        = models.CharField(max_length=30)
    description = models.CharField(max_length=300, blank=True)
    cost        = models.ForeignKey(Cost)
    reward      = models.ForeignKey(Reward)
    require     = models.ForeignKey(Require)

class Shop(models.Model):
    name        = models.CharField(max_length=30)
    crafts      = models.ManyToMany(Craft, blank=True)

【讨论】:

以上是关于在 Django 中建模异构多对多关系的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

使用 ORMLite 实现多对多关系的最佳方法是啥?

CoreData:如何建模循环多对多关系

如何在 django 中处理未保存的多对多关系?

在关系数据库中对相同实体之间的多个多对多关系进行建模

DW中尺寸之间的多对多关系-更好的建模?

如何建模多对多关系?