#12851 closed (fixed)
Queryset "defer" won't work properly with select_related
Reported by: | ruosteinen | Owned by: | ruosteinen |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 1.2-beta |
Severity: | Keywords: | select_related, defer, only, nesting, query | |
Cc: | Carl Meyer, ruosteinen, Gregor Müllegger | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Select_related and defer don't seem to work properly together. Consider following models in APP buggy.
from django.db import models class ProblemModel(models.Model): name = models.CharField(max_length=100) target = models.ForeignKey('Target', null=True) class Target(models.Model): location = models.CharField(max_length=100)
Even though Queryset API document the use of underscore in deferring, the following ends up in assertion error.
from buggy.models import Target, ProblemModel Target.objects.all().delete(); ProblemModel.objects.all().delete() tgt = Target.objects.create(location='Earth') pm = ProblemModel.objects.create(name='bug', target=tgt) pm = ProblemModel.objects.select_related('target').defer('target__location').get(name='bug') assert pm.target.location == 'bug'
At least according to Queryset API docs, defer() is supposed to allow access to deferred fields even if they're not loaded initially.
In addition pm.target is not of type Target_Deferred_location_deferred as it should be(?). There seem to be some other weirdness as well with select_related and defer but they'll probably be fixed once nesting works properly.
This in Django 1.2 beta 1 SVN-12416
I'll start working on a patch right away.
-Tommi Penttinen-
Attachments (2)
Change History (16)
comment:1 by , 15 years ago
comment:2 by , 15 years ago
Cc: | added |
---|
comment:3 by , 15 years ago
I haven't played with this yet, but your repro code doesn't look right. I would expect an assertion error with that code: pm.target.location should be 'Earth' not 'bug'.
comment:4 by , 15 years ago
milestone: | → 1.2 |
---|---|
Triage Stage: | Unreviewed → Accepted |
@carljm - Agreed: "Earth" should be the expected result. Either way, it's not working - pm.target.location is returning an empty string.
@ruosteinen - Although #10733 also deals with select_related() + defer()/only() problems, I'm not immediately convinced that it is the same problem (at least, not enough to close this as a duplicate).#10733 deals with having multiple joins to the same table; this ticket is about traversing a join correctly. I suspect the problem here is something to do with populating the related object cache correctly - the query is doing the join as it should, but because the query defers the only data field on the related object, and it is being deferred, the deferred class isn't being instantiated correctly.
comment:5 by , 15 years ago
@carljm, sorry. Changed the code a bit on the fly. But as russellm said, it still doesn't work.
@russellm True. Although in the proposed patch to #10733 only_load=only_load get passed properly to django.db.models.query.get_cached_row while recursing and after that relationships with deferred fields work properly i.e. type(pm.target) == <class 'buggy.models.Target_Deferred_location'>.
comment:6 by , 15 years ago
Cc: | added |
---|---|
Has patch: | set |
Owner: | changed from | to
Status: | new → assigned |
I'll mark this as "has patch" since one is available at #10733.
comment:7 by , 15 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
comment:8 by , 15 years ago
by , 15 years ago
Attachment: | 12851_inheritance_regression_test.patch added |
---|
The current fix for this bug does not work if you use model inheritance. Here is a failing testcase.
comment:9 by , 15 years ago
Cc: | added |
---|---|
Keywords: | only added |
Resolution: | fixed |
Status: | closed → reopened |
Unfortunatelly this bug is still valid in trunk if you use model inheritance.
My history with this bug was the following:
- Discovered it with 1.2-beta-1
- Saw that it was fixed in trunk and updated (and worked)
- Changed my models to use inheritance which broke things again.
I added a testcase which fails for me with the following backtrace:
Failed example: markus.value Exception raised: Traceback (most recent call last): File "C:\Python26\lib\site-packages\django\test\_doctest.py", line 1267, in __run compileflags, 1) in test.globs File "<doctest issue12851.models.__test__.API_TESTS[45]>", line 1, in <module> markus.value File "C:\Python26\lib\site-packages\django\db\models\query_utils.py", line 208, in __get__ cls._base_manager.filter(pk=instance.pk).only(name).using( File "C:\Python26\lib\site-packages\django\db\models\manager.py", line 141, in filter return self.get_query_set().filter(*args, **kwargs) File "C:\Python26\lib\site-packages\django\db\models\query.py", line 550, in filter return self._filter_or_exclude(False, *args, **kwargs) File "C:\Python26\lib\site-packages\django\db\models\query.py", line 568, in _filter_or_exclude clone.query.add_q(Q(*args, **kwargs)) File "C:\Python26\lib\site-packages\django\db\models\sql\query.py", line 1131, in add_q can_reuse=used_aliases) File "C:\Python26\lib\site-packages\django\db\models\sql\query.py", line 1071, in add_filter connector) File "C:\Python26\lib\site-packages\django\db\models\sql\where.py", line 66, in add value = obj.prepare(lookup_type, value) File "C:\Python26\lib\site-packages\django\db\models\sql\where.py", line 299, in prepare return self.field.get_prep_lookup(lookup_type, value) File "C:\Python26\lib\site-packages\django\db\models\fields\related.py", line 134, in get_prep_lookup return self._pk_trace(value, 'get_prep_lookup', lookup_type) File "C:\Python26\lib\site-packages\django\db\models\fields\related.py", line 196, in _pk_trace v = getattr(field, prep_func)(lookup_type, v, **kwargs) File "C:\Python26\lib\site-packages\django\db\models\fields\__init__.py", line 292, in get_prep_lookup return self.get_prep_value(value) File "C:\Python26\lib\site-packages\django\db\models\fields\__init__.py", line 476, in get_prep_value return int(value) ValueError: invalid literal for int() with base 10: 'Markus McDonalds'
IMO the SQL query is correct and retrieves the correct fields from the database, but the fields are populated in the wrong way.
comment:10 by , 15 years ago
Resolution: | → fixed |
---|---|
Status: | reopened → closed |
by , 15 years ago
Attachment: | 12851_inheritance_regression_test_2.patch added |
---|
Here is another testcase using "only" instead of "defer" which fails with the current trunk.
comment:11 by , 15 years ago
Resolution: | fixed |
---|---|
Status: | closed → reopened |
I'm very sorry for reopening the ticket *again*.
But the issue is still there if you use the "only" method instead of "defer". I've attached a new testcase which fails with the current trunk.
Here is the traceback:
Failed example: troy.state.name Exception raised: Traceback (most recent call last): File "C:\Python26\lib\site-packages\django\test\_doctest.py", line 1267, in __run compileflags, 1) in test.globs File "<doctest issue12851.models.__test__.API_TESTS[56]>", line 1, in <module> troy.state.name File "C:\Python26\lib\site-packages\django\db\models\query_utils.py", line 208, in __get__ cls._base_manager.filter(pk=instance.pk).only(name).using( File "C:\Python26\lib\site-packages\django\db\models\manager.py", line 141, in filter return self.get_query_set().filter(*args, **kwargs) File "C:\Python26\lib\site-packages\django\db\models\query.py", line 550, in filter return self._filter_or_exclude(False, *args, **kwargs) File "C:\Python26\lib\site-packages\django\db\models\query.py", line 568, in _filter_or_exclude clone.query.add_q(Q(*args, **kwargs)) File "C:\Python26\lib\site-packages\django\db\models\sql\query.py", line 1131, in add_q can_reuse=used_aliases) File "C:\Python26\lib\site-packages\django\db\models\sql\query.py", line 1071, in add_filter connector) File "C:\Python26\lib\site-packages\django\db\models\sql\where.py", line 66, in add value = obj.prepare(lookup_type, value) File "C:\Python26\lib\site-packages\django\db\models\sql\where.py", line 299, in prepare return self.field.get_prep_lookup(lookup_type, value) File "C:\Python26\lib\site-packages\django\db\models\fields\related.py", line 134, in get_prep_lookup return self._pk_trace(value, 'get_prep_lookup', lookup_type) File "C:\Python26\lib\site-packages\django\db\models\fields\related.py", line 196, in _pk_trace v = getattr(field, prep_func)(lookup_type, v, **kwargs) File "C:\Python26\lib\site-packages\django\db\models\fields\__init__.py", line 292, in get_prep_lookup return self.get_prep_value(value) File "C:\Python26\lib\site-packages\django\db\models\fields\__init__.py", line 476, in get_prep_value return int(value) ValueError: invalid literal for int() with base 10: 'Troy Buswell'
comment:12 by , 15 years ago
Resolution: | → fixed |
---|---|
Status: | reopened → closed |
Seems to be related to #10733.