66circular import difficulties.
77"""
88
9- import weakref
10-
119from django .db .backends import util
1210from django .utils import tree
1311
@@ -70,36 +68,39 @@ class DeferredAttribute(object):
7068 """
7169 def __init__ (self , field_name , model ):
7270 self .field_name = field_name
73- self .model_ref = weakref .ref (model )
74- self .loaded = False
7571
7672 def __get__ (self , instance , owner ):
7773 """
7874 Retrieves and caches the value from the datastore on the first lookup.
7975 Returns the cached value.
8076 """
8177 from django .db .models .fields import FieldDoesNotExist
78+ non_deferred_model = instance ._meta .proxy_for_model
79+ opts = non_deferred_model ._meta
8280
8381 assert instance is not None
84- cls = self .model_ref ()
8582 data = instance .__dict__
8683 if data .get (self .field_name , self ) is self :
8784 # self.field_name is the attname of the field, but only() takes the
8885 # actual name, so we need to translate it here.
8986 try :
90- cls ._meta .get_field_by_name (self .field_name )
91- name = self .field_name
87+ f = opts .get_field_by_name (self .field_name )[0 ]
9288 except FieldDoesNotExist :
93- name = [f .name for f in cls ._meta .fields
94- if f .attname == self .field_name ][0 ]
95- # We use only() instead of values() here because we want the
96- # various data coersion methods (to_python(), etc.) to be called
97- # here.
98- val = getattr (
99- cls ._base_manager .filter (pk = instance .pk ).only (name ).using (
100- instance ._state .db ).get (),
101- self .field_name
102- )
89+ f = [f for f in opts .fields
90+ if f .attname == self .field_name ][0 ]
91+ name = f .name
92+ # Lets see if the field is part of the parent chain. If so we
93+ # might be able to reuse the already loaded value. Refs #18343.
94+ val = self ._check_parent_chain (instance , name )
95+ if val is None :
96+ # We use only() instead of values() here because we want the
97+ # various data coersion methods (to_python(), etc.) to be
98+ # called here.
99+ val = getattr (
100+ non_deferred_model ._base_manager .only (name ).using (
101+ instance ._state .db ).get (pk = instance .pk ),
102+ self .field_name
103+ )
103104 data [self .field_name ] = val
104105 return data [self .field_name ]
105106
@@ -110,6 +111,20 @@ def __set__(self, instance, value):
110111 """
111112 instance .__dict__ [self .field_name ] = value
112113
114+ def _check_parent_chain (self , instance , name ):
115+ """
116+ Check if the field value can be fetched from a parent field already
117+ loaded in the instance. This can be done if the to-be fetched
118+ field is a primary key field.
119+ """
120+ opts = instance ._meta
121+ f = opts .get_field_by_name (name )[0 ]
122+ link_field = opts .get_ancestor_link (f .model )
123+ if f .primary_key and f != link_field :
124+ return getattr (instance , link_field .attname )
125+ return None
126+
127+
113128def select_related_descend (field , restricted , requested , reverse = False ):
114129 """
115130 Returns True if this field should be used to descend deeper for
0 commit comments