Django 复杂关系

Posted

技术标签:

【中文标题】Django 复杂关系【英文标题】:Django complex relationship 【发布时间】:2016-01-14 10:47:00 【问题描述】:

我有以下型号:

class A(models.Model):
    title = models.CharField(max_length=30)

class B(models.Model):
    title = models.CharField(max_length=255)
    a = models.ManyToManyField(A)

我现在想要一个模型 C,它的字段具有 A 和 B 的多个唯一组合,例如 ManyToMany 字段。我发现可以使用将 ForeignKey 映射到两个模型 GenericForeignKey。我试过 GenericForeignKey 如下:

class C(models.Model):
    title = models.CharField()
    property1 = models.CharField()
    a = models.ManyToManyField(A)
    b = models.ManyToManyField(B)
    a_b = GenericForeignKey(store, category)

但是,这似乎不起作用。有人可以指出我正确的方向吗?我是 Django 和 SQL 的新手。

编辑:阐明组合的要求。

Edit2:当前工作模型:

我创建了以下模型作为可行的解决方案:

class A(models.Model):
    title = models.CharField(max_length=30)

class B(models.Model):
    title = models.CharField(max_length=255)
    a = models.ManyToManyField(A)

class C(models.Model):
    title = models.CharField()
    property1 = models.CharField()

class A_B_C(models.Model):
    a = models.ForeignKey(A)
    b = models.ForeignKey(B)
    c = models.ForeignKey(C)
    class Meta:
        unique_together = ('a', 'b', 'c')

我避免在关系中显式创建和维护这个模型。我想知道这个解决方案是否有效或者是否有另一种 Django 方式来做事?

【问题讨论】:

【参考方案1】:

当使用ManyToManyField 时,Django 会隐式地为您创建中间表。在您的示例中,您将获得包含(id, a_id, b_id) 列的附加表。但是您可以显式定义它并在B 模型上的字段a 上设置through 选项,然后为此中间模型定义unique_together

class A(models.Model):
    title = models.CharField(max_length=30)

class B(models.Model):
    title = models.CharField(max_length=255)
    a = models.ManyToManyField(A, through='C')

class C(models.Model):
    a = models.ForeignKey(A, on_delete=models.CASCADE)
    b = models.ForeignKey(B, on_delete=models.CASCADE)

    class Meta:
        unique_together = ("a", "b")

注意:如果我没记错的话,你不能直接在ManyToManyFiled 上设置 unique_together。

【讨论】:

这行得通,但我也需要 a、b 的多种组合,就像在 ManyToMany 类型关系中一样。这里可以吗? 这是一个显式的多对多,但既然你写了 “我现在想要一个模型 C,它的字段是 A 和 B 的唯一组合。”它添加了一个唯一的约束。 上面写的代码,假设aN是A的实例,bN是B的实例,你可以有即:(a1-b1,a2-b1,a3-b1,a1-b2,a2- b2、a2-b3、a3-b1、a3-b2、a3-b3) 关系,这当然允许反向查找(感谢 Django ORM),即(b1-a1、b1-a2、b1-a3 等) .但是由于 unique_together,你不能拥有 (a1-b1, a1-b2, a2-b1, a2-b2, a1-b2)。 根据您的最后评论,我需要 a1-b1、a1-b2 可以映射到 c1。现在,c1 可以有 a1-b1 或 a1-b2。我已改进问题以包含此评论。 嗯,这是您在问题编辑中包含的解决方案之一。虽然由于您的 C 模型非常简单,但另一种方法是将 C 中的字段直接包含到 A_B_C,这是数据模型的非规范化,它会在查询时为您节省“连接”,但会占用更多存储空间,所以在此之前如果您真的需要,最好进行基准测试。【参考方案2】:

这不是 GenericForeignKey 的工作。相反,您需要定义unique_together Meta 属性:

class C(models.Model):
    ...
    class Meta
        unique_together = ('a', 'b')

【讨论】:

我试了一下,这里不允许使用 ManyToManyField。 试图了解你在这里真正需要什么。 A 和 B 已经通过 M2M 链接,为什么还需要两个额外的 M2M?你能在那种关系上寻找custom through table吗?【参考方案3】:

供将来参考:

我创建了以下模型作为可行的解决方案:

class A(models.Model):
    title = models.CharField(max_length=30)

class B(models.Model):
    title = models.CharField(max_length=255)
    a = models.ManyToManyField(A)

class C(models.Model):
    title = models.CharField()
    property1 = models.CharField()

class A_B_C(models.Model):
    a = models.ForeignKey(A)
    b = models.ForeignKey(B)
    c = models.ForeignKey(C)

    class Meta:
        unique_together = ('a', 'b', 'c')

【讨论】:

以上是关于Django 复杂关系的主要内容,如果未能解决你的问题,请参考以下文章

在 django 中获取具有多对多关系的复杂查询集

Django 中的复杂表单小部件

django基础之ORM

django中的跨表查询梳理

石墨烯和 Django 关于关系

pytchon自动化运维:Django Model基础