@@ -84,9 +84,20 @@ def get_queryset(self):
84
84
queryset = queryset .all ()
85
85
return queryset
86
86
87
- def get_iterable (self , instance , source_attrs ):
88
- relationship = get_attribute (instance , source_attrs )
89
- return relationship .all () if (hasattr (relationship , 'all' )) else relationship
87
+ def use_pk_only_optimization (self ):
88
+ return False
89
+
90
+ def get_attribute (self , instance ):
91
+ if self .use_pk_only_optimization () and self .source_attrs :
92
+ # Optimized case, return a mock object only containing the pk attribute.
93
+ try :
94
+ instance = get_attribute (instance , self .source_attrs [:- 1 ])
95
+ return PKOnlyObject (pk = instance .serializable_value (self .source_attrs [- 1 ]))
96
+ except AttributeError :
97
+ pass
98
+
99
+ # Standard case, return the object instance.
100
+ return get_attribute (instance , self .source_attrs )
90
101
91
102
@property
92
103
def choices (self ):
@@ -120,6 +131,9 @@ class PrimaryKeyRelatedField(RelatedField):
120
131
'incorrect_type' : _ ('Incorrect type. Expected pk value, received {data_type}.' ),
121
132
}
122
133
134
+ def use_pk_only_optimization (self ):
135
+ return True
136
+
123
137
def to_internal_value (self , data ):
124
138
try :
125
139
return self .get_queryset ().get (pk = data )
@@ -128,32 +142,6 @@ def to_internal_value(self, data):
128
142
except (TypeError , ValueError ):
129
143
self .fail ('incorrect_type' , data_type = type (data ).__name__ )
130
144
131
- def get_attribute (self , instance ):
132
- # We customize `get_attribute` here for performance reasons.
133
- # For relationships the instance will already have the pk of
134
- # the related object. We return this directly instead of returning the
135
- # object itself, which would require a database lookup.
136
- try :
137
- instance = get_attribute (instance , self .source_attrs [:- 1 ])
138
- return PKOnlyObject (pk = instance .serializable_value (self .source_attrs [- 1 ]))
139
- except AttributeError :
140
- return get_attribute (instance , self .source_attrs )
141
-
142
- def get_iterable (self , instance , source_attrs ):
143
- # For consistency with `get_attribute` we're using `serializable_value()`
144
- # here. Typically there won't be any difference, but some custom field
145
- # types might return a non-primitive value for the pk otherwise.
146
- #
147
- # We could try to get smart with `values_list('pk', flat=True)`, which
148
- # would be better in some case, but would actually end up with *more*
149
- # queries if the developer is using `prefetch_related` across the
150
- # relationship.
151
- relationship = super (PrimaryKeyRelatedField , self ).get_iterable (instance , source_attrs )
152
- return [
153
- PKOnlyObject (pk = item .serializable_value ('pk' ))
154
- for item in relationship
155
- ]
156
-
157
145
def to_representation (self , value ):
158
146
return value .pk
159
147
@@ -184,6 +172,9 @@ def __init__(self, view_name=None, **kwargs):
184
172
185
173
super (HyperlinkedRelatedField , self ).__init__ (** kwargs )
186
174
175
+ def use_pk_only_optimization (self ):
176
+ return self .lookup_field == 'pk'
177
+
187
178
def get_object (self , view_name , view_args , view_kwargs ):
188
179
"""
189
180
Return the object corresponding to a matched URL.
@@ -285,6 +276,11 @@ def __init__(self, view_name=None, **kwargs):
285
276
kwargs ['source' ] = '*'
286
277
super (HyperlinkedIdentityField , self ).__init__ (view_name , ** kwargs )
287
278
279
+ def use_pk_only_optimization (self ):
280
+ # We have the complete object instance already. We don't need
281
+ # to run the 'only get the pk for this relationship' code.
282
+ return False
283
+
288
284
289
285
class SlugRelatedField (RelatedField ):
290
286
"""
@@ -349,7 +345,8 @@ def to_internal_value(self, data):
349
345
]
350
346
351
347
def get_attribute (self , instance ):
352
- return self .child_relation .get_iterable (instance , self .source_attrs )
348
+ relationship = get_attribute (instance , self .source_attrs )
349
+ return relationship .all () if (hasattr (relationship , 'all' )) else relationship
353
350
354
351
def to_representation (self , iterable ):
355
352
return [
0 commit comments