【问题标题】: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 信息进行内联编辑。

【问题讨论】:

    标签: django django-admin many-to-many junction-table


    【解决方案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?

    【讨论】:

    • 另一个版本的链接:stackoverflow.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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-10-26
      • 2021-10-17
      • 1970-01-01
      • 2021-02-16
      • 2023-03-09
      • 1970-01-01
      • 2012-02-28
      相关资源
      最近更新 更多