mirror of https://github.com/django/django.git
[1.5.x] Fixed #14694 -- Made ``defer()`` work with reverse relations
Reverse o2o fields are now usable with defer.
Backpatch of [6ebf115206
]
This commit is contained in:
parent
54ce0ff7ba
commit
a21e8dee76
|
@ -585,17 +585,22 @@ class Query(object):
|
||||||
for name in parts[:-1]:
|
for name in parts[:-1]:
|
||||||
old_model = cur_model
|
old_model = cur_model
|
||||||
source = opts.get_field_by_name(name)[0]
|
source = opts.get_field_by_name(name)[0]
|
||||||
cur_model = source.rel.to
|
if is_reverse_o2o(source):
|
||||||
|
cur_model = source.model
|
||||||
|
else:
|
||||||
|
cur_model = source.rel.to
|
||||||
opts = cur_model._meta
|
opts = cur_model._meta
|
||||||
# Even if we're "just passing through" this model, we must add
|
# Even if we're "just passing through" this model, we must add
|
||||||
# both the current model's pk and the related reference field
|
# both the current model's pk and the related reference field
|
||||||
# to the things we select.
|
# (if it's not a reverse relation) to the things we select.
|
||||||
must_include[old_model].add(source)
|
if not is_reverse_o2o(source):
|
||||||
|
must_include[old_model].add(source)
|
||||||
add_to_dict(must_include, cur_model, opts.pk)
|
add_to_dict(must_include, cur_model, opts.pk)
|
||||||
field, model, _, _ = opts.get_field_by_name(parts[-1])
|
field, model, _, _ = opts.get_field_by_name(parts[-1])
|
||||||
if model is None:
|
if model is None:
|
||||||
model = cur_model
|
model = cur_model
|
||||||
add_to_dict(seen, model, field)
|
if not is_reverse_o2o(field):
|
||||||
|
add_to_dict(seen, model, field)
|
||||||
|
|
||||||
if defer:
|
if defer:
|
||||||
# We need to load all fields for each model, except those that
|
# We need to load all fields for each model, except those that
|
||||||
|
@ -2032,3 +2037,10 @@ def add_to_dict(data, key, value):
|
||||||
data[key].add(value)
|
data[key].add(value)
|
||||||
else:
|
else:
|
||||||
data[key] = set([value])
|
data[key] = set([value])
|
||||||
|
|
||||||
|
def is_reverse_o2o(field):
|
||||||
|
"""
|
||||||
|
A little helper to check if the given field is reverse-o2o. The field is
|
||||||
|
expected to be some sort of relation field or related object.
|
||||||
|
"""
|
||||||
|
return not hasattr(field, 'rel') and field.field.unique
|
||||||
|
|
|
@ -55,6 +55,10 @@ class Feature(models.Model):
|
||||||
class SpecialFeature(models.Model):
|
class SpecialFeature(models.Model):
|
||||||
feature = models.ForeignKey(Feature)
|
feature = models.ForeignKey(Feature)
|
||||||
|
|
||||||
|
class OneToOneItem(models.Model):
|
||||||
|
item = models.OneToOneField(Item, related_name="one_to_one_item")
|
||||||
|
name = models.CharField(max_length=15)
|
||||||
|
|
||||||
class ItemAndSimpleItem(models.Model):
|
class ItemAndSimpleItem(models.Model):
|
||||||
item = models.ForeignKey(Item)
|
item = models.ForeignKey(Item)
|
||||||
simple = models.ForeignKey(SimpleItem)
|
simple = models.ForeignKey(SimpleItem)
|
||||||
|
|
|
@ -9,7 +9,7 @@ from django.db.models.loading import cache
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from .models import (ResolveThis, Item, RelatedItem, Child, Leaf, Proxy,
|
from .models import (ResolveThis, Item, RelatedItem, Child, Leaf, Proxy,
|
||||||
SimpleItem, Feature, ItemAndSimpleItem, SpecialFeature)
|
SimpleItem, Feature, ItemAndSimpleItem, OneToOneItem, SpecialFeature)
|
||||||
|
|
||||||
|
|
||||||
class DeferRegressionTest(TestCase):
|
class DeferRegressionTest(TestCase):
|
||||||
|
@ -111,6 +111,7 @@ class DeferRegressionTest(TestCase):
|
||||||
Item,
|
Item,
|
||||||
ItemAndSimpleItem,
|
ItemAndSimpleItem,
|
||||||
Leaf,
|
Leaf,
|
||||||
|
OneToOneItem,
|
||||||
Proxy,
|
Proxy,
|
||||||
RelatedItem,
|
RelatedItem,
|
||||||
ResolveThis,
|
ResolveThis,
|
||||||
|
@ -147,6 +148,7 @@ class DeferRegressionTest(TestCase):
|
||||||
"Leaf_Deferred_name_value",
|
"Leaf_Deferred_name_value",
|
||||||
"Leaf_Deferred_second_child_id_value",
|
"Leaf_Deferred_second_child_id_value",
|
||||||
"Leaf_Deferred_value",
|
"Leaf_Deferred_value",
|
||||||
|
"OneToOneItem",
|
||||||
"Proxy",
|
"Proxy",
|
||||||
"RelatedItem",
|
"RelatedItem",
|
||||||
"RelatedItem_Deferred_",
|
"RelatedItem_Deferred_",
|
||||||
|
@ -182,6 +184,33 @@ class DeferRegressionTest(TestCase):
|
||||||
self.assertEqual(1, qs.count())
|
self.assertEqual(1, qs.count())
|
||||||
self.assertEqual('Foobar', qs[0].name)
|
self.assertEqual('Foobar', qs[0].name)
|
||||||
|
|
||||||
|
def test_reverse_one_to_one_relations(self):
|
||||||
|
# Refs #14694. Test reverse relations which are known unique (reverse
|
||||||
|
# side has o2ofield or unique FK) - the o2o case
|
||||||
|
item = Item.objects.create(name="first", value=42)
|
||||||
|
o2o = OneToOneItem.objects.create(item=item, name="second")
|
||||||
|
self.assertEqual(len(Item.objects.defer('one_to_one_item__name')), 1)
|
||||||
|
self.assertEqual(len(Item.objects.select_related('one_to_one_item')), 1)
|
||||||
|
self.assertEqual(len(Item.objects.select_related(
|
||||||
|
'one_to_one_item').defer('one_to_one_item__name')), 1)
|
||||||
|
self.assertEqual(len(Item.objects.select_related('one_to_one_item').defer('value')), 1)
|
||||||
|
# Make sure that `only()` doesn't break when we pass in a unique relation,
|
||||||
|
# rather than a field on the relation.
|
||||||
|
self.assertEqual(len(Item.objects.only('one_to_one_item')), 1)
|
||||||
|
with self.assertNumQueries(1):
|
||||||
|
i = Item.objects.select_related('one_to_one_item')[0]
|
||||||
|
self.assertEquals(i.one_to_one_item.pk, o2o.pk)
|
||||||
|
self.assertEquals(i.one_to_one_item.name, "second")
|
||||||
|
with self.assertNumQueries(1):
|
||||||
|
i = Item.objects.select_related('one_to_one_item').defer(
|
||||||
|
'value', 'one_to_one_item__name')[0]
|
||||||
|
self.assertEquals(i.one_to_one_item.pk, o2o.pk)
|
||||||
|
self.assertEquals(i.name, "first")
|
||||||
|
with self.assertNumQueries(1):
|
||||||
|
self.assertEquals(i.one_to_one_item.name, "second")
|
||||||
|
with self.assertNumQueries(1):
|
||||||
|
self.assertEquals(i.value, 42)
|
||||||
|
|
||||||
def test_defer_with_select_related(self):
|
def test_defer_with_select_related(self):
|
||||||
item1 = Item.objects.create(name="first", value=47)
|
item1 = Item.objects.create(name="first", value=47)
|
||||||
item2 = Item.objects.create(name="second", value=42)
|
item2 = Item.objects.create(name="second", value=42)
|
||||||
|
|
Loading…
Reference in New Issue