Opened 6 years ago

Closed 6 years ago

Last modified 5 years ago

#15790 closed Bug (fixed)

only() broken for proxy models

Reported by: michal.modzelewski@… Owned by: Michał Modzelewski
Component: Database layer (models, ORM) Version: master
Severity: Normal Keywords: proxy only
Cc: michal.modzelewski@… Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX:

Description

Usage of deferred fields with proxy models is broken.

For example when using the following models

class Item(models.Model):
    name = models.CharField(max_length=15)
    text = models.TextField(default="xyzzy")
    value = models.IntegerField()
    other_value = models.IntegerField(default=0)

class Proxy(Item):
    class Meta:
        proxy = True

we create an item with

>>> proxy = Proxy.objects.create(name="proxy", value=42)

Now depending on whether we call only() with 'id' or not we get different behaviour.
Without 'id' we get an endless loop.

>>> deferredproxy = Proxy.objects.only('value').get(pk=proxy.pk)
>>> deferredproxy.name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    deferredproxy.name
  File "/home/michal/.local/lib/python2.7/site-packages/django/db/models/query_utils.py", line 100, in __get__
    cls._base_manager.filter(pk=instance.pk).only(name).using(
  File "/home/michal/.local/lib/python2.7/site-packages/django/db/models/base.py", line 426, in _get_pk_val
    return getattr(self, meta.pk.attname)
  File "/home/michal/.local/lib/python2.7/site-packages/django/db/models/query_utils.py", line 100, in __get__
    cls._base_manager.filter(pk=instance.pk).only(name).using(
...
  File "/home/michal/.local/lib/python2.7/site-packages/django/db/models/base.py", line 426, in _get_pk_val
    return getattr(self, meta.pk.attname)
RuntimeError: maximum recursion depth exceeded

With 'id' we get the wrong value when accessing a deferred field.

>>> deferredproxy = Proxy.objects.only('id', 'value').get(pk=proxy.pk)
>>> deferredproxy.name
3

This value happens to be the object id.

>>> proxy.id
3

Comparing the objects returned with examples using the base model.

>>> Proxy.objects.only('other_value').get(pk=proxy.pk).__dict__
{'_state': <django.db.models.base.ModelState object at 0x89bd58c>, 'other_value': 3}
>>> Item.objects.only('other_value').get(pk=proxy.pk).__dict__
{'_state': <django.db.models.base.ModelState object at 0x89bd80c>, 'id': 3, 'other_value': 0}
>>> Proxy.objects.only('name', 'text', 'value', 'other_value').get(pk=proxy.pk).__dict__
{'text': u'proxy', '_state': <django.db.models.base.ModelState object at 0x89bda8c>, 'name': 3, 'value': u'xyzzy', 'other_value': 42}
>>> Item.objects.only('name', 'text', 'value', 'other_value').get(pk=proxy.pk).__dict__
{'name': u'proxy', 'text': u'xyzzy', '_state': <django.db.models.base.ModelState object at 0x89bdcec>, 'value': 42, 'other_value': 0, 'id': 3}

'id' is missing from the __dict__ for the proxy models, and all the values are shifted.

Attachments (2)

15790_only_broken_for_proxy_models.diff (2.6 KB) - added by michal.modzelewski@… 6 years ago.
Patch for this bug, with regression tests
15790_only_broken_for_proxy_models.2.diff (2.7 KB) - added by Michał Modzelewski 6 years ago.
Updated patch for trunk

Download all attachments as: .zip

Change History (9)

Changed 6 years ago by michal.modzelewski@…

Patch for this bug, with regression tests

comment:1 Changed 6 years ago by michal.modzelewski@…

Has patch: set
milestone: 1.3
Triage Stage: UnreviewedAccepted

comment:2 Changed 6 years ago by Luke Plant

Severity: Release blockerNormal

I don't think this is a release blocker, as it is not a regression, or a serious bug with a new feature in 1.3 that would have blocked it.

comment:3 Changed 6 years ago by Michał Modzelewski <michal.modzelewski@…>

Yes, i originally thought that this could result in an unintentional loss of data if you saved such a broken model instance, but since the pk value is missing from the __dict__ saving will just create a new record in the database.

comment:4 Changed 6 years ago by Michał Modzelewski

Easy pickings: unset
milestone: 1.31.4
Owner: changed from nobody to Michał Modzelewski
Status: newassigned

Changed 6 years ago by Michał Modzelewski

Updated patch for trunk

comment:5 Changed 6 years ago by kgibula

Triage Stage: AcceptedReady for checkin

Patch applies cleanly and the described issue is fixed.

comment:6 Changed 6 years ago by Ramiro Morales

Resolution: fixed
Status: assignedclosed

In [16228]:

Fixed #15790 -- Fixed QuerySet only() and defer() methods behavior with proxy models. Thanks Michal Modzelewzki for the report and patch.

comment:7 Changed 5 years ago by Jacob

milestone: 1.4

Milestone 1.4 deleted

Note: See TracTickets for help on using tickets.
Back to Top