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

时间:2012-04-12 19:00:28

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

我已经搜索了一段时间,但似乎无法找到现有的问题(尽管这可能是一个不了解术语的问题)。

我是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信息。

2 个答案:

答案 0 :(得分:3)

听起来你有一种“交易”,它有一个名称,描述和类型,并定义了成本,奖励和要求。您应该将其定义为单个模型,而不是多个模型(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关系都应位于一个位置 - 如上所述,或OneToOne关系中使用CraftShopContent等。正如您所猜测的那样,您不应该拥有一大堆复杂的多对多through表,这些表都非常相同。


你在帖子的底部提到你是

  

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

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

答案 1 :(得分:1)

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)