如何在Django中创建多对一关系?

时间:2015-06-27 15:42:14

标签: python django database relationship many-to-one

问题如下:如何在django模型中创建多对一关系?

基本上,我有两个模型:文章和漫画,我希望有一个评论模型,它将与文章和漫画有关系,但不是两者都有。 因此,如果Comment对象与Article对象有关系,那么它就不会与Comic对象建立关系。

我目前正在按照以下方式进行,但不起作用:

class Article(models.Model):
    #other fields
class Comic(models.Model):
    #other fields
class Comment(models.Model):
    article = models.ForeignKey(Article)
    comic = models.ForeignKey(Comic)

我真的很感激一些帮助。

2 个答案:

答案 0 :(得分:1)

这很棘手。我认为有几种方法可以对此进行建模。

使用您当前的方式,您可以在应用程序中强制执行唯一性约束。

class Comment(models.Model):
    article = models.ForeignKey(Article)
    comic = models.ForeignKey(Comic)

    def save(self, *args, **kwargs):
        # assert that there is either comic OR article but not both
        super(Comment, self).save(*args, **kwargs)

通过这种方式,如果添加另一个希望Comment引用的模型,会发生什么?您必须在save方法中手动添加新类型的条件并执行迁移。

Django提供了GenericForeignKey字段,允许您引用Comment中的任何模型。 https://docs.djangoproject.com/en/1.8/ref/contrib/contenttypes/#generic-relations

这将允许您创建从Comment到Article或Comic的通用引用,并且由于它只是一个字段,因此默认情况下是互斥的。我发现查询并使用GenericeForeignKey的尴尬;但它们仍然是一个选项,可能适用于您的用例。

另一个强大的选择(我最喜欢的)可能是创建一个多态模型,它也是互斥的。

每个Comment都可以使用模型继承来引用Content的通用部分。 (我没有测试以下内容,因此它可能无法复制/粘贴)

class Content(models.Model):
  objects = InheritanceManager()
  # shared content fields could be stored in this model

class Article(Content):
  # article specific fields

class Comic(Content):
  # comic specific fields

class Comment(models.Model):
  content = models.OneToOneField(Content)

这是建模Comment与任何Content之间关系的有效方式。这会增加额外的查询开销,并且应该保证对您的用例进行审核。

InheritanceManager是由django-model-utils包提供的实用程序,非常轻量级。我已经在生产环境中使用过,只要您了解使用它对数据进行建模所涉及的额外查询,它就是高效的。 https://django-model-utils.readthedocs.org/en/latest/managers.html#inheritancemanager

文档中解释了查询开销。

如果您认为将来会添加其他Content子类,这可能是一种可扩展的方式来建模您的关系,并提供更灵活的过滤GenericForeignKey

答案 1 :(得分:0)

好吧,你可以为你添加另一个字段评论模型。像

import random
from itertools import islice

def m1(a,b):
    return list(set(a) - set(b))[:100]
def m2(a,b):
    return list(set(a).difference(b))[:100] 
def m3(a,b):
    return list(islice(set(a).difference(b), 100))
def m4(a,b):
    bset = set(b)
    return list(islice((x for x in a if x not in bset), 100))

创建评论对象后,将文章或漫画指向另一个对象并使assign = True。