7
7
from django .core .exceptions import ImproperlyConfigured
8
8
from django .urls import NoReverseMatch
9
9
from django .utils .translation import ugettext_lazy as _
10
- from rest_framework .fields import MISSING_ERROR_MESSAGE
11
- from rest_framework .relations import MANY_RELATION_KWARGS , PrimaryKeyRelatedField
10
+ from rest_framework .fields import MISSING_ERROR_MESSAGE , SkipField
11
+ from rest_framework .relations import MANY_RELATION_KWARGS , PrimaryKeyRelatedField , ManyRelatedField as DRFManyRelatedField
12
12
from rest_framework .reverse import reverse
13
13
from rest_framework .serializers import Serializer
14
14
29
29
]
30
30
31
31
32
+ class ManyRelatedField (DRFManyRelatedField ):
33
+ """
34
+ This workaround skips "data" rendering for relationships
35
+ in order to save some sql queries and improve performance
36
+ """
37
+
38
+ def __init__ (self , child_relation = None , * args , ** kwargs ):
39
+ self .render_data = kwargs .pop ('render_data' , True )
40
+ super (ManyRelatedField , self ).__init__ (child_relation , * args , ** kwargs )
41
+
42
+ def get_attribute (self , instance ):
43
+ if self .render_data :
44
+ return super (ManyRelatedField , self ).get_attribute (instance )
45
+ raise SkipField
46
+
47
+
32
48
class ResourceRelatedField (PrimaryKeyRelatedField ):
33
49
_skip_polymorphic_optimization = True
34
50
self_link_view_name = None
@@ -49,7 +65,7 @@ class ResourceRelatedField(PrimaryKeyRelatedField):
49
65
'no_match' : _ ('Invalid hyperlink - No URL match.' ),
50
66
}
51
67
52
- def __init__ (self , self_link_view_name = None , related_link_view_name = None , ** kwargs ):
68
+ def __init__ (self , self_link_view_name = None , related_link_view_name = None , render_data = True , ** kwargs ):
53
69
if self_link_view_name is not None :
54
70
self .self_link_view_name = self_link_view_name
55
71
if related_link_view_name is not None :
@@ -74,6 +90,29 @@ def __init__(self, self_link_view_name=None, related_link_view_name=None, **kwar
74
90
75
91
super (ResourceRelatedField , self ).__init__ (** kwargs )
76
92
93
+ @classmethod
94
+ def many_init (cls , * args , ** kwargs ):
95
+ """
96
+ This method handles creating a parent `ManyRelatedField` instance
97
+ when the `many=True` keyword argument is passed.
98
+
99
+ Typically you won't need to override this method.
100
+
101
+ Note that we're over-cautious in passing most arguments to both parent
102
+ and child classes in order to try to cover the general case. If you're
103
+ overriding this method you'll probably want something much simpler, eg:
104
+
105
+ @classmethod
106
+ def many_init(cls, *args, **kwargs):
107
+ kwargs['child'] = cls()
108
+ return CustomManyRelatedField(*args, **kwargs)
109
+ """
110
+ list_kwargs = {'child_relation' : cls (* args , ** kwargs )}
111
+ for key in kwargs .keys ():
112
+ if key in MANY_RELATION_KWARGS + ('render_data' ,):
113
+ list_kwargs [key ] = kwargs [key ]
114
+ return ManyRelatedField (** list_kwargs )
115
+
77
116
def use_pk_only_optimization (self ):
78
117
# We need the real object to determine its type...
79
118
return self .get_resource_type_from_included_serializer () is not None
@@ -293,7 +332,8 @@ def __new__(cls, *args, **kwargs):
293
332
return cls .many_init (* args , ** kwargs )
294
333
return super (ResourceRelatedField , cls ).__new__ (cls , * args , ** kwargs )
295
334
296
- def __init__ (self , child_relation = None , * args , ** kwargs ):
335
+ def __init__ (self , child_relation = None , render_data = True , * args , ** kwargs ):
336
+ self .render_data = render_data
297
337
model = kwargs .pop ('model' , None )
298
338
if child_relation is not None :
299
339
self .child_relation = child_relation
@@ -306,11 +346,13 @@ def many_init(cls, *args, **kwargs):
306
346
list_kwargs = {k : kwargs .pop (k ) for k in LINKS_PARAMS if k in kwargs }
307
347
list_kwargs ['child_relation' ] = cls (* args , ** kwargs )
308
348
for key in kwargs .keys ():
309
- if key in ('model' ,) + MANY_RELATION_KWARGS :
349
+ if key in ('model' , 'render_data' ) + MANY_RELATION_KWARGS :
310
350
list_kwargs [key ] = kwargs [key ]
311
351
return cls (** list_kwargs )
312
352
313
353
def get_attribute (self , instance ):
354
+ if not self .render_data :
355
+ raise SkipField
314
356
# check for a source fn defined on the serializer instead of the model
315
357
if self .source and hasattr (self .parent , self .source ):
316
358
serializer_method = getattr (self .parent , self .source )
0 commit comments