django模型中的通用关系是什么

时间:2012-10-15 03:32:45

标签: python django

有人在models.py

中写过此内容
documents = generic.GenericRelation(Document)

我想知道它与ForeignKeyOneToMany字段等其他类型的区别。

我没有看到关于此

的足够文档

如果找到了here

的内容

但即使在第一时间,我也不会不知道内容类型的作用以及使用它的原因。

1 个答案:

答案 0 :(得分:3)

想象一下,您的数据库(图像,视频,歌曲等)中有各种对象,并且您希望添加关键字。但是,您希望使用单个关键字表来管理包含关键字的所有对象的所有关键字。因此模型看起来像:

class Image(models.Model):
    # ...

class Video(models.Model):
    # ...

class Keyword(models.Model):
    keyword = models.CharField(max_length=64)

因此,有了这些模型,您需要将关键字连接到其他对象。为此,您需要使用多对多关系:

class KeywordItem(models.Model):
    keyword = models.ForeignKey('Keyword')
    image = models.ForeignKey('Image')

然而,这不起作用,因为这只允许您为图像添加关键字,但是您还需要为视频等设置关键字。换句话说,这种方法的问题在于它限制了将关键字链接到具体表,图像表。所以我的想法不是这样做,而是链接到任何表,然后只指定要链接到哪个表:

class KeywordItem(models.Model):
    keyword = models.ForeignKey('Keyword')
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    object = generic.GenericForeignKey('content_type', 'object_id')

content_type指定要链接到哪个表,object_id指定要链接到的表中行的主键。

现在的问题是如何指定您链接的是哪个表?一种解决方案是对选项进行硬编码(图像为1,视频为2等),但这很容易出错,因为必须对其进行维护。事实证明,Django使用content types framework解决了这个问题。当您执行syncdb时,Django会为您项目中的每个表/模型分配一个唯一的编号。对我来说,更容易将其视为表类型id(假设为表id,因为表本身没有id)。所以这个数字以后可以用来引用特定的表格。

然后,如果图片内容类型(表格类型ID)为1,并且您想要将关键字与pk 7关联到带有pk 11的图片,则可以通过:

KeywordItem.objects.create(keyword_id=7,
                           content_type_id=1,
                           object_id=11)

# or using Django magic (this automatically figures out the content type):
KeywordItem.objects.create(keyword_id=7,
                           object=Image.objects.get(pk=11))

希望这可以解释为什么你可能需要通用关系的概念。现在是GenericRelation。使用上面的示例,假设您想要访问特定图像的所有关键字。你必须做类似的事情:

img = Image.object.get(...)
img_type = ContentType.objects.get_for_model(img)
img_keywords = KeywordItem.objects.filter(content_type_id=img_type,
                                          object_id=img.pk)

然而,这看起来并不干净,这就是GenericRelation有用的原因。它使访问反向泛型关系变得非常容易。为此,您必须将以下字段添加到Image模型中:

class Image(models.Model):
    # ...
    keywords = generic.GenericRelation('KeywordItem')

现在,您只需执行以下操作即可访问关键字:

Image.object.get(...).keywords.all()

请注意

通用外键是一项棘手的业务,特别是在Django中,因为语法有些棘手。如果您是Django的初学者,我不建议您使用它们,直到您熟悉Django中的外键和Django提供的所有“神奇”(例如related_name参数)。一旦你理解了这一点,通用外键将更容易理解和使用。

相关问题