GenericForeignKey搭配UUIDField时,如何从通用对象查询集获取真实模型查询集?
解决GenericForeignKey与UUIDField配合时的查询问题
遇到这个问题的核心原因是:你的Generic模型里object_id存储的是UUID的字符串形式,而Foo的id是UUIDField(数据库层面多为二进制/原生UUID类型存储,Django ORM层面返回UUID对象),直接用id__in=Subquery(...)时,两者类型不匹配,导致数据库无法匹配到结果。
下面是兼顾兼容性(支持其他主键类型模型)的推荐解决方法:
方法一:将object_id转换为UUID类型后对比(推荐)
利用Django的Cast函数,在子查询中将object_id字符串转换为UUID类型,让两边类型一致,同时务必过滤content_type——避免混入其他模型的object_id(否则转换UUID会抛出数据库错误)。
from django.db.models import Subquery, Cast from django.db.models.fields import UUIDField from django.contrib.contenttypes.models import ContentType # 获取Foo模型对应的ContentType foo_ct = ContentType.objects.get_for_model(Foo) # 正确的查询语句 foo_queryset = Foo.objects.filter( id__in=Subquery( Generic.objects.filter(content_type=foo_ct) .values(Cast('object_id', output_field=UUIDField())) ) )
测试这个查询就能得到预期结果:
>>> foo_queryset <QuerySet [<Foo: Foo object (997eaf64-a115-4f48-b3ac-8cbcc21274a8)>]>
这种方式的优势是:数据库可以利用Foo.id的索引,查询性能更优,且仅针对Foo模型做类型转换,不影响其他模型的使用。
方法二:将Foo的id转换为字符串对比(不推荐,性能较差)
如果因特殊场景需要,也可以反过来把Foo.id转换成字符串匹配object_id,但这种方式可能无法利用索引,性能不如第一种:
from django.db.models import Subquery, Cast from django.db.models.fields import CharField from django.contrib.contenttypes.models import ContentType foo_ct = ContentType.objects.get_for_model(Foo) foo_queryset = Foo.objects.annotate( id_str=Cast('id', output_field=CharField(max_length=36)) ).filter( id_str__in=Subquery( Generic.objects.filter(content_type=foo_ct).values('object_id') ) )
关键注意事项
- 必须过滤
content_type:若不限制Generic查询的content_type,当存在其他模型(比如用整数主键的模型)的object_id时,转换UUID会触发数据库错误。 - 版本兼容性:两种方法在Django 1.11和2.0中均可正常工作,因为
Cast函数在这些版本已得到支持。
内容的提问来源于stack exchange,提问作者Oskar Persson
相关产品推荐
相关产品推荐

