Igo*_*gor 11 python django django-models django-database
我们正试图加快我们的应用程序prefetch_related
.它可以跟随GenericForeignKey
关系,它可以更深入,__
但不幸的是,如果相关模型没有这样的领域,它将失败.
这是模型结构的一些例子
class ModelA(models.Model):
event_object = models.ForeignKey(SomeModelA)
class ModelB(models.Model):
event = models.ForeignKey(SomeModelB)
class ModelC(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey()
Run Code Online (Sandbox Code Playgroud)
所以ModelC
实例可以指向ModelA
或ModelB
.我可以使用这样的查询集来预取A和B模型:ModelC.objects.all().prefetch_related('content_object')
不幸的是我还需要选择事件对象(SomeModelA
或SomeModelB
)
如果我试着跑
ModelC.objects.all().prefetch_related('content_object', 'content_object__event_object')
Run Code Online (Sandbox Code Playgroud)
如果我只有ModelC
指向的实例,它将工作ModelA
,但在更坏的情况下,它将失败,因为ModelB
没有该event_object
字段event
而是.
这个模型在代码中的许多地方使用,因此重命名字段不是一个好主意.所以我想知道是否有办法为字段/列创建别名.
我试图这样做:
class ModelB(models.Model):
event = models.ForeignKey(SomeModelB)
event_object = models.ForeignKey(SomeModelB, db_column='event_id', related_name='+')
Run Code Online (Sandbox Code Playgroud)
使两个字段指向DB表中的同一列.然而,这不起作用,因为它打破了save
方法.Django创建一个UPDATE
SQL查询,其中一列放置两次并获取DatabaseError
有没有办法创建这样的别名?或者也许还有另一种解决方案可以prefetch_related
不抛出异常?
更新:在save
方法中,有一个update_fields
参数可用于排除此字段.然而,它是在1.5中引入的,我们使用的是1.4.所以我继续寻找答案.
更新#2:@ shx2让我提供追溯.有2个可能的追溯.1st - 当第一个对象缺少属性时:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 72, in __repr__
data = list(self[:REPR_OUTPUT_SIZE + 1])
File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 97, in __iter__
len(self)
File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 89, in __len__
self._prefetch_related_objects()
File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 570, in _prefetch_related_objects
prefetch_related_objects(self._result_cache, self._prefetch_related_lookups)
File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 1664, in prefetch_related_objects
(attr, first_obj.__class__.__name__, lookup))
AttributeError: Cannot find 'event_object' on ModelB object, 'content_object__event_object' is an invalid parameter to prefetch_related()
Run Code Online (Sandbox Code Playgroud)
如果prefetch_related参数对第一个对象有效,那么我得到第二个回溯:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 72, in __repr__
data = list(self[:REPR_OUTPUT_SIZE + 1])
File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 97, in __iter__
len(self)
File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 89, in __len__
self._prefetch_related_objects()
File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 570, in _prefetch_related_objects
prefetch_related_objects(self._result_cache, self._prefetch_related_lookups)
File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 1680, in prefetch_related_objects
obj_list, additional_prl = prefetch_one_level(obj_list, prefetcher, attr)
File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 1803, in prefetch_one_level
qs = getattr(obj, attname).all()
AttributeError: 'ModelB' object has no attribute 'event_object'
Run Code Online (Sandbox Code Playgroud)
它看起来像是 django 中的错误或疏忽。作为解决方法,您可以尝试定义一个执行两阶段预取的自定义管理器。
from django.db import models
from django.db.models import Q
from django.contrib.contenttypes.models import ContentType
class PrefetchWorkaroundManager(models.Manager):
def get_queryset(self):
q = super(PrefetchWorkaroundManager, self).get_queryset()
content_typeA = ContentType.objects.get_for_model(ModelA)
content_typeB = ContentType.objects.get_for_model(ModelB)
return q.filter(content_type__pk = content_typeA.id).prefetch_related('content_object', 'content_object__event_object') | \
q.filter(content_type__pk = content_typeB.id).prefetch_related('content_object', 'content_object__event')
class ModelC(models.Model):
...
objects_prefetched = PrefetchWorkaroundManager()
Run Code Online (Sandbox Code Playgroud)
每个想要进行预取的调用者应该访问ModelC.objects_prefetched
而不是ModelC.objects
:
ModelC.objects_prefetched.filter(...)
Run Code Online (Sandbox Code Playgroud)
我承认,我没有测试它,所以它可能无法按原样工作。但我相信这种做法是合理的。
归档时间: |
|
查看次数: |
1294 次 |
最近记录: |