如何遍历Django中的GenericForeignKey?

2024-06-17 12:16:44 发布

您现在位置:Python中文网/ 问答频道 /正文

我使用的是Django v1.9.4,后面是PostgreSQL 9.2.14。使用以下型号:

from django.db import models
from django.contrib.contenttypes.fields import GenericRelation, GenericForeignKey
from django.contrib.contenttypes.models import ContentType

class Foo(models.Model):
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    bar = GenericForeignKey('content_type', 'object_id')

class Bar(models.Model):
    foos = GenericRelation(Foo, related_query_name='bars')
    class Meta:
        abstract = True

class BarX(Bar):
    name = models.CharField(max_length=10, default='bar x')

class BarY(Bar):
    name = models.CharField(max_length=10, default='bar y')

创建一些实例来演示我的问题:

>>> bar_x = BarX.objects.create()
>>> bar_y = BarY.objects.create()
>>> foo1 = Foo.objects.create(bar=bar_x)
>>> foo2 = Foo.objects.create(bar=bar_y)
>>> foo1.bar.name
u'bar x'
>>> foo2.bar.name
u'bar y'

我无法遍历django中的GFK,尝试筛选会引发异常,并显示一条消息,建议添加GenericRelation。但是,通过相关查询名bars使用泛型关系并不可靠。 例如:

>>> [foo.bar.name for foo in Foo.objects.all()]
[u'bar x', u'bar y']  # in a pure python loop, it's working
>>> Foo.objects.filter(bar__name='bar x')
FieldError: Field 'bar' does not generate an automatic reverse relation and therefore cannot be used for reverse querying. If it is a GenericForeignKey, consider adding a GenericRelation.
>>> Foo.objects.values_list('bars__name', flat=1)
[None, u'bar y']   # but why None is returned in here?
>>> Foo.objects.filter(bars__name='bar x')
[]  # why no result here?
>>> Foo.objects.filter(bars__name='bar y')
[<Foo: Foo object>]  # but this one works?

我做错什么了?


提醒未来的读者:GenericRelation上模板化related_query_name在Django 1.9上无法正常工作。

在Django 1.10中添加了related_query_name now supports app label and class interpolation using the '%(app_label)s' and '%(class)s' strings,在#25354fix被合并之后。

如果您使用的是Django 1.10,那么可以将GenericRelation放在抽象基类上,并像related_query_name='%(app_label)s_%(class)s'那样对其进行模板化,以确保子类之间的唯一性。


Tags: djangonamefromimportobjectsfoomodelscreate
1条回答
网友
1楼 · 发布于 2024-06-17 12:16:44

一般来说,不可能按照您尝试的方式沿此方向遍历GenericForeignKeyGenericForeignKey可以指向应用程序中的任何模型,而不仅仅是Bar及其子类。基于这个原因,Foo.objects.filter(bar__somefield='some value')无法知道您目前所考虑的目标模型,因此也无法知道目标模型具有哪些字段。事实上,在执行这样的查询时,无法选择要连接的数据库表-它可以是任何表,具体取决于Foo.content_type的值。

如果确实要在联接中使用泛型关系,则必须在该关系的另一端定义^{}。这样你就可以让Django知道它应该在另一边寻找哪个模型。

例如,您可以创建这样的BarXBarY模型:

class BarX(Bar):
    name = models.CharField(max_length=10, default='bar x')
    foos = GenericRelation(Foo, related_query_name='bar_x')

class BarY(Bar):
    name = models.CharField(max_length=10, default='bar y')
    foos = GenericRelation(Foo, related_query_name='bar_y')

如果这样做,则可以执行如下查询:

Foo.objects.filter(bar_x__name='bar x')
Foo.objects.filter(bar_y__name='bar y')

但是,必须选择单个目标模型。这是一个无论如何都无法真正克服的限制;每个数据库连接都需要事先知道它在哪个表上操作。

如果绝对需要同时允许BarXBarY作为目标,则应该能够使用^{}表达式在查询筛选器中显式列出这两个值:

Foo.objects.filter(Q(bar_x__name='bar x') | Q(bar_y__name='bar y'))

相关问题 更多 >