与带有 UUIDField 的模型一起使用时,GenericForeignKey 得到错误的 ID
GenericForeignKey gets wrong id when used with model with UUIDField
将 GenericForeignKey
与 UUIDField
一起使用时,从通用对象的查询集中获取 "real model" 的查询集的推荐方法是什么?
以下是我正在测试的模型:
import uuid
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
class Foo(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
class Generic(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.CharField(max_length=255)
content_object = GenericForeignKey()
这是我迄今为止尝试过的方法:
>>> from django.db.models import Subquery
>>> from foo.models import Foo, Generic
>>> f = Foo.objects.create()
>>> g = Generic.objects.create(content_object=f)
>>> Foo.objects.filter(id__in=Subquery(Generic.objects.all().values('object_id')))
<QuerySet []>
>>> Generic.objects.get().object_id
'997eaf64-a115-4f48-b3ac-8cbcc21274a8'
>>> Foo.objects.get().pk
UUID('997eaf64-a115-4f48-b3ac-8cbcc21274a8')
我猜这与保存的 UUID 没有 UUIDField
的连字符有关。我也无法将 object_id
变成 UUIDField
,因为我需要其他以整数和字符串作为主键的模型。
我使用的是 Django 1.11,但我也测试过 Django 2.0,它也有同样的问题。
尝试删除 Subquery()
。您还需要按内容类型进行过滤。
foo_content_type = ContentType.objects.get_for_model(Foo)
Foo.objects.filter(
id__in=Generic.objects.filter(content_type=foo_content_type).values('object_id'),
)
主要问题出在explicit type casts
所以,@Alasdair的想法,你可以试试:
foo_content_type = ContentType.objects.get_for_model(Foo)
gids = Generic.objects.filter(content_type=foo_content_type)
# create list of uuid strings
gids = list(gids.values_list('object_id', flat=True))
Foo.objects.filter(pk__in=gids)
其他解决方案:您可以将 uuid
字段添加到 Generic
模型。例如:
class Generic(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.CharField(max_length=255)
content_object = GenericForeignKey()
uuid4 = models.UUIDField(blank=True, null=True)
def save(self, *args, **kwargs):
try:
self.uuid4 = uuid.UUID(self.object_id)
except Exception as e:
pass
super().save(*args, **kwargs)
查询集将看起来:
foo_content_type = ContentType.objects.get_for_model(Foo)
gids = Generic.objects.filter(content_type=foo_content_type).values('uuid4')
Foo.objects.filter(pk__in=gids)
将 GenericForeignKey
与 UUIDField
一起使用时,从通用对象的查询集中获取 "real model" 的查询集的推荐方法是什么?
以下是我正在测试的模型:
import uuid
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
class Foo(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
class Generic(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.CharField(max_length=255)
content_object = GenericForeignKey()
这是我迄今为止尝试过的方法:
>>> from django.db.models import Subquery
>>> from foo.models import Foo, Generic
>>> f = Foo.objects.create()
>>> g = Generic.objects.create(content_object=f)
>>> Foo.objects.filter(id__in=Subquery(Generic.objects.all().values('object_id')))
<QuerySet []>
>>> Generic.objects.get().object_id
'997eaf64-a115-4f48-b3ac-8cbcc21274a8'
>>> Foo.objects.get().pk
UUID('997eaf64-a115-4f48-b3ac-8cbcc21274a8')
我猜这与保存的 UUID 没有 UUIDField
的连字符有关。我也无法将 object_id
变成 UUIDField
,因为我需要其他以整数和字符串作为主键的模型。
我使用的是 Django 1.11,但我也测试过 Django 2.0,它也有同样的问题。
尝试删除 Subquery()
。您还需要按内容类型进行过滤。
foo_content_type = ContentType.objects.get_for_model(Foo)
Foo.objects.filter(
id__in=Generic.objects.filter(content_type=foo_content_type).values('object_id'),
)
主要问题出在explicit type casts
所以,@Alasdair的想法,你可以试试:
foo_content_type = ContentType.objects.get_for_model(Foo)
gids = Generic.objects.filter(content_type=foo_content_type)
# create list of uuid strings
gids = list(gids.values_list('object_id', flat=True))
Foo.objects.filter(pk__in=gids)
其他解决方案:您可以将 uuid
字段添加到 Generic
模型。例如:
class Generic(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.CharField(max_length=255)
content_object = GenericForeignKey()
uuid4 = models.UUIDField(blank=True, null=True)
def save(self, *args, **kwargs):
try:
self.uuid4 = uuid.UUID(self.object_id)
except Exception as e:
pass
super().save(*args, **kwargs)
查询集将看起来:
foo_content_type = ContentType.objects.get_for_model(Foo)
gids = Generic.objects.filter(content_type=foo_content_type).values('uuid4')
Foo.objects.filter(pk__in=gids)