diff --git a/django/db/models/query.py b/django/db/models/query.py
index 41c24c7..25f0883 100644
a
|
b
|
def get_klass_info(klass, max_depth=0, cur_depth=0, requested=None,
|
1284 | 1284 | # Build the list of fields that *haven't* been requested |
1285 | 1285 | for field, model in klass._meta.get_fields_with_model(): |
1286 | 1286 | if field.name not in load_fields: |
1287 | | skip.add(field.name) |
| 1287 | skip.add(field.attname) |
1288 | 1288 | elif local_only and model is not None: |
1289 | 1289 | continue |
1290 | 1290 | else: |
… |
… |
def get_klass_info(klass, max_depth=0, cur_depth=0, requested=None,
|
1315 | 1315 | |
1316 | 1316 | related_fields = [] |
1317 | 1317 | for f in klass._meta.fields: |
1318 | | if select_related_descend(f, restricted, requested): |
| 1318 | if select_related_descend(f, restricted, requested, load_fields): |
1319 | 1319 | if restricted: |
1320 | 1320 | next = requested[f.name] |
1321 | 1321 | else: |
… |
… |
def get_klass_info(klass, max_depth=0, cur_depth=0, requested=None,
|
1327 | 1327 | reverse_related_fields = [] |
1328 | 1328 | if restricted: |
1329 | 1329 | for o in klass._meta.get_all_related_objects(): |
1330 | | if o.field.unique and select_related_descend(o.field, restricted, requested, reverse=True): |
| 1330 | if o.field.unique and select_related_descend(o.field, restricted, requested, |
| 1331 | only_load.get(o.model), reverse=True): |
1331 | 1332 | next = requested[o.field.related_query_name()] |
1332 | 1333 | klass_info = get_klass_info(o.model, max_depth=max_depth, cur_depth=cur_depth+1, |
1333 | 1334 | requested=next, only_load=only_load, local_only=True) |
diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py
index a56ab5c..68c79e9 100644
a
|
b
|
class DeferredAttribute(object):
|
110 | 110 | """ |
111 | 111 | instance.__dict__[self.field_name] = value |
112 | 112 | |
113 | | def select_related_descend(field, restricted, requested, reverse=False): |
| 113 | def select_related_descend(field, restricted, requested, load_fields, reverse=False): |
114 | 114 | """ |
115 | 115 | Returns True if this field should be used to descend deeper for |
116 | 116 | select_related() purposes. Used by both the query construction code |
117 | 117 | (sql.query.fill_related_selections()) and the model instance creation code |
118 | | (query.get_cached_row()). |
| 118 | (query.get_klass_info()). |
119 | 119 | |
120 | 120 | Arguments: |
121 | 121 | * field - the field to be checked |
122 | 122 | * restricted - a boolean field, indicating if the field list has been |
123 | 123 | manually restricted using a requested clause) |
124 | 124 | * requested - The select_related() dictionary. |
| 125 | * load_fields - the set of fields to be loaded on this model |
125 | 126 | * reverse - boolean, True if we are checking a reverse select related |
126 | 127 | """ |
127 | 128 | if not field.rel: |
… |
… |
def select_related_descend(field, restricted, requested, reverse=False):
|
135 | 136 | return False |
136 | 137 | if not restricted and field.null: |
137 | 138 | return False |
| 139 | if load_fields: |
| 140 | if field.name not in load_fields: |
| 141 | if restricted and field.name in requested: |
| 142 | raise InvalidQuery("Field %s.%s cannot be both deferred" |
| 143 | " and traversed using select_related" |
| 144 | " at the same time." % |
| 145 | (field.model._meta.object_name, field.name)) |
| 146 | return False |
138 | 147 | return True |
139 | 148 | |
140 | 149 | # This function is needed because data descriptors must be defined on a class |
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
index 72948f9..9474d89 100644
a
|
b
|
class SQLCompiler(object):
|
595 | 595 | if avoid_set is None: |
596 | 596 | avoid_set = set() |
597 | 597 | orig_dupe_set = dupe_set |
| 598 | only_load = self.query.get_loaded_field_names() |
598 | 599 | |
599 | 600 | # Setup for the case when only particular related fields should be |
600 | 601 | # included in the related selection. |
… |
… |
class SQLCompiler(object):
|
606 | 607 | restricted = False |
607 | 608 | |
608 | 609 | for f, model in opts.get_fields_with_model(): |
609 | | if not select_related_descend(f, restricted, requested): |
| 610 | if not select_related_descend(f, restricted, requested, |
| 611 | only_load.get(model or self.query.model)): |
610 | 612 | continue |
611 | 613 | # The "avoid" set is aliases we want to avoid just for this |
612 | 614 | # particular branch of the recursion. They aren't permanently |
… |
… |
class SQLCompiler(object):
|
679 | 681 | if o.field.unique |
680 | 682 | ] |
681 | 683 | for f, model in related_fields: |
682 | | if not select_related_descend(f, restricted, requested, reverse=True): |
| 684 | if not select_related_descend(f, restricted, requested, |
| 685 | only_load.get(model), reverse=True): |
683 | 686 | continue |
684 | 687 | # The "avoid" set is aliases we want to avoid just for this |
685 | 688 | # particular branch of the recursion. They aren't permanently |
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index ed2bc06..38ee4fe 100644
a
|
b
|
class Query(object):
|
1836 | 1836 | |
1837 | 1837 | If no fields are marked for deferral, returns an empty dictionary. |
1838 | 1838 | """ |
1839 | | collection = {} |
1840 | | self.deferred_to_data(collection, self.get_loaded_field_names_cb) |
1841 | | return collection |
| 1839 | # We cache this because we call this function multiple times |
| 1840 | # (compiler.fill_related_selections, query.iterator) |
| 1841 | try: |
| 1842 | return self._loaded_field_names_cache |
| 1843 | except AttributeError: |
| 1844 | collection = {} |
| 1845 | self.deferred_to_data(collection, self.get_loaded_field_names_cb) |
| 1846 | self._loaded_field_names_cache = collection |
| 1847 | return collection |
1842 | 1848 | |
1843 | 1849 | def get_loaded_field_names_cb(self, target, model, fields): |
1844 | 1850 | """ |
diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt
index 7633555..302bd0a 100644
a
|
b
|
to ``defer()``::
|
1079 | 1079 | # Load all fields immediately. |
1080 | 1080 | my_queryset.defer(None) |
1081 | 1081 | |
| 1082 | .. versionchanged:: 1.4 |
| 1083 | |
1082 | 1084 | Some fields in a model won't be deferred, even if you ask for them. You can |
1083 | 1085 | never defer the loading of the primary key. If you are using |
1084 | 1086 | :meth:`select_related()` to retrieve related models, you shouldn't defer the |
1085 | | loading of the field that connects from the primary model to the related one |
1086 | | (at the moment, that doesn't raise an error, but it will eventually). |
| 1087 | loading of the field that connects from the primary model to the related |
| 1088 | one, doing so will result in an error. |
1087 | 1089 | |
1088 | 1090 | .. note:: |
1089 | 1091 | |
… |
… |
logically::
|
1143 | 1145 | # existing set of fields). |
1144 | 1146 | Entry.objects.defer("body").only("headline", "body") |
1145 | 1147 | |
| 1148 | .. versionchanged:: 1.4 |
| 1149 | |
1146 | 1150 | All of the cautions in the note for the :meth:`defer` documentation apply to |
1147 | 1151 | ``only()`` as well. Use it cautiously and only after exhausting your other |
1148 | | options. |
| 1152 | options. Also note that using :meth:`only` and omitting a field requested |
| 1153 | using :meth:`select_related` is an error as well. |
1149 | 1154 | |
1150 | 1155 | using |
1151 | 1156 | ~~~~~ |
diff --git a/tests/modeltests/defer/tests.py b/tests/modeltests/defer/tests.py
index 542162c..7af36b3 100644
a
|
b
|
|
1 | 1 | from __future__ import absolute_import |
2 | 2 | |
3 | | from django.db.models.query_utils import DeferredAttribute |
| 3 | from django.db.models.query_utils import DeferredAttribute, InvalidQuery |
4 | 4 | from django.test import TestCase |
5 | 5 | |
6 | 6 | from .models import Secondary, Primary, Child, BigChild |
… |
… |
class DeferTests(TestCase):
|
73 | 73 | self.assert_delayed(qs.defer("name").get(pk=p1.pk), 1) |
74 | 74 | self.assert_delayed(qs.only("name").get(pk=p1.pk), 2) |
75 | 75 | |
76 | | # DOES THIS WORK? |
77 | | self.assert_delayed(qs.only("name").select_related("related")[0], 1) |
78 | | self.assert_delayed(qs.defer("related").select_related("related")[0], 0) |
| 76 | # When we defer a field and also select_related it, the query is |
| 77 | # invalid and raises an exception. |
| 78 | with self.assertRaises(InvalidQuery): |
| 79 | qs.only("name").select_related("related")[0] |
| 80 | with self.assertRaises(InvalidQuery): |
| 81 | qs.defer("related").select_related("related")[0] |
| 82 | |
| 83 | # With a depth-based select_related, all deferred ForeignKeys are |
| 84 | # deferred instead of traversed. |
| 85 | with self.assertNumQueries(3): |
| 86 | obj = qs.defer("related").select_related()[0] |
| 87 | self.assert_delayed(obj, 1) |
| 88 | self.assertEqual(obj.related.id, s1.pk) |
79 | 89 | |
80 | 90 | # Saving models with deferred fields is possible (but inefficient, |
81 | 91 | # since every field has to be retrieved first). |
diff --git a/tests/regressiontests/defer_regress/models.py b/tests/regressiontests/defer_regress/models.py
index 812d2da..bd4f845 100644
a
|
b
|
class SimpleItem(models.Model):
|
47 | 47 | |
48 | 48 | class Feature(models.Model): |
49 | 49 | item = models.ForeignKey(SimpleItem) |
| 50 | |
| 51 | class ItemAndSimpleItem(models.Model): |
| 52 | item = models.ForeignKey(Item) |
| 53 | simple = models.ForeignKey(SimpleItem) |
diff --git a/tests/regressiontests/defer_regress/tests.py b/tests/regressiontests/defer_regress/tests.py
index 4afe39b..f21f691 100644
a
|
b
|
from django.db.models.loading import cache
|
9 | 9 | from django.test import TestCase |
10 | 10 | |
11 | 11 | from .models import (ResolveThis, Item, RelatedItem, Child, Leaf, Proxy, |
12 | | SimpleItem, Feature) |
| 12 | SimpleItem, Feature, ItemAndSimpleItem) |
13 | 13 | |
14 | 14 | |
15 | 15 | class DeferRegressionTest(TestCase): |
… |
… |
class DeferRegressionTest(TestCase):
|
109 | 109 | Child, |
110 | 110 | Feature, |
111 | 111 | Item, |
| 112 | ItemAndSimpleItem, |
112 | 113 | Leaf, |
113 | 114 | Proxy, |
114 | 115 | RelatedItem, |
… |
… |
class DeferRegressionTest(TestCase):
|
125 | 126 | ), |
126 | 127 | ) |
127 | 128 | ) |
| 129 | # FIXME: This is dependent on the order in which tests are run -- |
| 130 | # this test case has to be the first, otherwise a LOT more classes |
| 131 | # appear. |
128 | 132 | self.assertEqual( |
129 | 133 | klasses, [ |
130 | 134 | "Child", |
131 | 135 | "Child_Deferred_value", |
132 | 136 | "Feature", |
133 | 137 | "Item", |
| 138 | "ItemAndSimpleItem", |
134 | 139 | "Item_Deferred_name", |
135 | 140 | "Item_Deferred_name_other_value_text", |
136 | 141 | "Item_Deferred_name_other_value_value", |
… |
… |
class DeferRegressionTest(TestCase):
|
139 | 144 | "Leaf", |
140 | 145 | "Leaf_Deferred_child_id_second_child_id_value", |
141 | 146 | "Leaf_Deferred_name_value", |
142 | | "Leaf_Deferred_second_child_value", |
| 147 | "Leaf_Deferred_second_child_id_value", |
143 | 148 | "Leaf_Deferred_value", |
144 | 149 | "Proxy", |
145 | 150 | "RelatedItem", |
… |
… |
class DeferRegressionTest(TestCase):
|
174 | 179 | qs = ResolveThis.objects.defer('num') |
175 | 180 | self.assertEqual(1, qs.count()) |
176 | 181 | self.assertEqual('Foobar', qs[0].name) |
| 182 | |
| 183 | def test_defer_with_select_related(self): |
| 184 | item1 = Item.objects.create(name="first", value=47) |
| 185 | item2 = Item.objects.create(name="second", value=42) |
| 186 | simple = SimpleItem.objects.create(name="simple", value="23") |
| 187 | related = ItemAndSimpleItem.objects.create(item=item1, simple=simple) |
| 188 | |
| 189 | obj = ItemAndSimpleItem.objects.defer('item').select_related('simple').get() |
| 190 | self.assertEqual(obj.item, item1) |
| 191 | self.assertEqual(obj.item_id, item1.id) |
| 192 | |
| 193 | obj.item = item2 |
| 194 | obj.save() |
| 195 | |
| 196 | obj = ItemAndSimpleItem.objects.defer('item').select_related('simple').get() |
| 197 | self.assertEqual(obj.item, item2) |
| 198 | self.assertEqual(obj.item_id, item2.id) |
diff --git a/tests/regressiontests/select_related_regress/tests.py b/tests/regressiontests/select_related_regress/tests.py
index 4cd4f78..3016206 100644
a
|
b
|
class SelectRelatedRegressTests(TestCase):
|
133 | 133 | self.assertEqual(troy.state.name, u'Western Australia') |
134 | 134 | |
135 | 135 | # Also works if you use only, rather than defer |
136 | | troy = SpecialClient.objects.select_related('state').only('name').get(name='Troy Buswell') |
| 136 | troy = SpecialClient.objects.select_related('state').only('name', 'state').get(name='Troy Buswell') |
137 | 137 | |
138 | 138 | self.assertEqual(troy.name, u'Troy Buswell') |
139 | 139 | self.assertEqual(troy.value, 42) |