如何在 Django 中遍历一个 GenericForeignKey?
How to traverse a GenericForeignKey in Django?
我使用的是 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, after the fix for #25354 已合并。
如果您使用的是 Django 1.10,则可以继续将 GenericRelation
放在抽象基础 class 上并像 related_query_name='%(app_label)s_%(class)s'
一样进行模板化,以确保整个子classes。
一般情况下,不可能按照您尝试的方式朝这个方向遍历GenericForeignKey
。 GenericForeignKey
可以指向应用程序中的任何模型,而不仅仅是 Bar
及其子类。因此,Foo.objects.filter(bar__somefield='some value')
暂时无法知道您的目标模型是什么,因此无法判断目标模型有哪些字段。事实上,在执行这样的查询时,无法选择要连接的数据库 table——它可以是任何 table,具体取决于 Foo.content_type
.[=26= 的值]
如果您确实想在联接中使用通用关系,则必须在该关系的另一端定义一个 GenericRelation
。这样你就可以让 Django 知道它应该在另一边寻找哪个模型。
例如,您可以像这样创建 BarX
和 BarY
模型:
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')
但是,您必须选择一个目标模型。这是一个你无法以任何方式真正克服的限制;每个数据库连接都需要提前知道它在哪个 table 上操作。
如果您绝对需要允许 BarX
和 BarY
作为目标,您应该能够使用 Q
表达式在查询过滤器中明确列出它们:
Foo.objects.filter(Q(bar_x__name='bar x') | Q(bar_y__name='bar y'))
我使用的是 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, after the fix for #25354 已合并。
如果您使用的是 Django 1.10,则可以继续将 GenericRelation
放在抽象基础 class 上并像 related_query_name='%(app_label)s_%(class)s'
一样进行模板化,以确保整个子classes。
一般情况下,不可能按照您尝试的方式朝这个方向遍历GenericForeignKey
。 GenericForeignKey
可以指向应用程序中的任何模型,而不仅仅是 Bar
及其子类。因此,Foo.objects.filter(bar__somefield='some value')
暂时无法知道您的目标模型是什么,因此无法判断目标模型有哪些字段。事实上,在执行这样的查询时,无法选择要连接的数据库 table——它可以是任何 table,具体取决于 Foo.content_type
.[=26= 的值]
如果您确实想在联接中使用通用关系,则必须在该关系的另一端定义一个 GenericRelation
。这样你就可以让 Django 知道它应该在另一边寻找哪个模型。
例如,您可以像这样创建 BarX
和 BarY
模型:
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')
但是,您必须选择一个目标模型。这是一个你无法以任何方式真正克服的限制;每个数据库连接都需要提前知道它在哪个 table 上操作。
如果您绝对需要允许 BarX
和 BarY
作为目标,您应该能够使用 Q
表达式在查询过滤器中明确列出它们:
Foo.objects.filter(Q(bar_x__name='bar x') | Q(bar_y__name='bar y'))