select_related with nested fields affects query result
I belive select_related should never affect query result, but only optimize referenced objects loading. However, I've found a case where query result is changed.
models.py
from django.db import models
class Thing(models.Model):
name = models.CharField(max_length=255)
def __unicode__(self):
return self.name
class User(models.Model):
login = models.CharField(max_length=255)
class Lock(models.Model):
description = models.CharField(max_length=255)
# Only one lock per thing is allowed.
thing = models.OneToOneField(Thing)
user = models.ForeignKey(User)
tests.py
from django.test import TestCase
from reproduce.models import *
class SimpleTest(TestCase):
def setUp(self):
user = User.objects.create(login='james')
# Unlocked thing.
self.unlocked_thing = Thing.objects.create(name='unlocked')
# Locked thing.
self.locked_thing = Thing.objects.create(name='locked')
Lock.objects.create(thing=self.locked_thing, description='lock', user=user)
def test_simple_query(self):
things = Thing.objects.all()
self.assertEqual(set(things), set([self.unlocked_thing, self.locked_thing]))
def test_select_related(self):
things = Thing.objects.select_related('lock')
self.assertEqual(set(things), set([self.locked_thing, self.unlocked_thing]))
def test_deep_select_related(self):
things = Thing.objects.select_related('lock', 'lock__user')
# This fails.
self.assertEqual(set(things), set([self.locked_thing, self.unlocked_thing]))
./manage.py test reproduce produces:
Creating test database for alias 'default'...
F..
======================================================================
FAIL: test_deep_select_related (reproduce.tests.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/.../select_related_bug/reproduce/tests.py", line 27, in test_deep_select_related
self.assertEqual(set(things), set([self.locked_thing, self.unlocked_thing]))
AssertionError: Items in the second set but not the first:
<Thing: unlocked>
----------------------------------------------------------------------
Ran 3 tests in 0.006s
FAILED (failures=1)
Destroying test database for alias 'default'...
Change History
(5)
Easy pickings: |
unset
|
Triage Stage: |
Unreviewed → Accepted
|
Cc: |
daniel.barreto.n@… added
|
Resolution: |
→ fixed
|
Status: |
new → closed
|
The initial observation is correct:
select_related()
is only an optimisation and should never change the results returned at the outermost object level (it will, of course, select extra lower-level objects).