We are trying to speed up our app with prefetch_related
. It can follow the GenericForeignKey
relations, and it can go deeper with __
but unfortunately it will fail if related model do not have such field.
Here is some example of model structure
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()
So ModelC
instance can point either to ModelA
or ModelB
. And I can use such queryset to prefetch both A and B models: ModelC.objects.all().prefetch_related('content_object')
Unfortunately I also need to select the event object (SomeModelA
or SomeModelB
)
If I try to run
ModelC.objects.all().prefetch_related('content_object', 'content_object__event_object')
It will work if I have only ModelC
instances that points to ModelA
, but in ohter case it will fail because ModelB
do not have the event_object
field and have event
instead.
This models are used in many places across the code so it's not a good idea to rename the field. So I wonder if there is a way to create an alias for a field/column.
I was trying to do like this:
class ModelB(models.Model):
event = models.ForeignKey(SomeModelB)
event_object = models.ForeignKey(SomeModelB, db_column='event_id', related_name='+')
to make two fields that point to the same column in DB table. However this is not working as it breaks the save
method. Django creates an UPDATE
SQL query where one column is placed twice and gets a DatabaseError
Is there any way to create such alias? Or maybe there is another solution to make prefetch_related
not to throw an exception?
Update: In save
method there is an update_fields
parameter that can be used to exclude this field. However it was introduced in 1.5 and we are using 1.4. So I continue to search for an answer.
Update #2: @shx2 asked me to provide a traceback. There are 2 possible traceback. 1st - when attribute is missing on the first object:
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()
And if prefetch_related parameters are valid for the first object then I get the 2nd traceback:
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'