77from django .core .exceptions import ImproperlyConfigured
88from django .urls import NoReverseMatch
99from 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
1212from rest_framework .reverse import reverse
1313from rest_framework .serializers import Serializer
1414
2929]
3030
3131
32+ class ManyRelatedField (DRFManyRelatedField ):
33+ '''
34+ This workaround skips "data" rendering for relationships in order to save some sql queries and improve performance
35+ '''
36+
37+ def __init__ (self , child_relation = None , * args , ** kwargs ):
38+ self .render_data = kwargs .pop ('render_data' , True )
39+ super (ManyRelatedField , self ).__init__ (child_relation , * args , ** kwargs )
40+
41+ def get_attribute (self , instance ):
42+ if self .render_data :
43+ return super (ManyRelatedField , self ).get_attribute (instance )
44+ raise SkipField
45+
46+
3247class ResourceRelatedField (PrimaryKeyRelatedField ):
3348 _skip_polymorphic_optimization = True
3449 self_link_view_name = None
@@ -49,7 +64,7 @@ class ResourceRelatedField(PrimaryKeyRelatedField):
4964 'no_match' : _ ('Invalid hyperlink - No URL match.' ),
5065 }
5166
52- def __init__ (self , self_link_view_name = None , related_link_view_name = None , ** kwargs ):
67+ def __init__ (self , self_link_view_name = None , related_link_view_name = None , render_data = True , ** kwargs ):
5368 if self_link_view_name is not None :
5469 self .self_link_view_name = self_link_view_name
5570 if related_link_view_name is not None :
@@ -74,6 +89,29 @@ def __init__(self, self_link_view_name=None, related_link_view_name=None, **kwar
7489
7590 super (ResourceRelatedField , self ).__init__ (** kwargs )
7691
92+ @classmethod
93+ def many_init (cls , * args , ** kwargs ):
94+ """
95+ This method handles creating a parent `ManyRelatedField` instance
96+ when the `many=True` keyword argument is passed.
97+
98+ Typically you won't need to override this method.
99+
100+ Note that we're over-cautious in passing most arguments to both parent
101+ and child classes in order to try to cover the general case. If you're
102+ overriding this method you'll probably want something much simpler, eg:
103+
104+ @classmethod
105+ def many_init(cls, *args, **kwargs):
106+ kwargs['child'] = cls()
107+ return CustomManyRelatedField(*args, **kwargs)
108+ """
109+ list_kwargs = {'child_relation' : cls (* args , ** kwargs )}
110+ for key in kwargs .keys ():
111+ if key in MANY_RELATION_KWARGS + ('render_data' ,):
112+ list_kwargs [key ] = kwargs [key ]
113+ return ManyRelatedField (** list_kwargs )
114+
77115 def use_pk_only_optimization (self ):
78116 # We need the real object to determine its type...
79117 return self .get_resource_type_from_included_serializer () is not None
@@ -293,7 +331,8 @@ def __new__(cls, *args, **kwargs):
293331 return cls .many_init (* args , ** kwargs )
294332 return super (ResourceRelatedField , cls ).__new__ (cls , * args , ** kwargs )
295333
296- def __init__ (self , child_relation = None , * args , ** kwargs ):
334+ def __init__ (self , child_relation = None , render_data = True , * args , ** kwargs ):
335+ self .render_data = render_data
297336 model = kwargs .pop ('model' , None )
298337 if child_relation is not None :
299338 self .child_relation = child_relation
@@ -306,11 +345,13 @@ def many_init(cls, *args, **kwargs):
306345 list_kwargs = {k : kwargs .pop (k ) for k in LINKS_PARAMS if k in kwargs }
307346 list_kwargs ['child_relation' ] = cls (* args , ** kwargs )
308347 for key in kwargs .keys ():
309- if key in ('model' ,) + MANY_RELATION_KWARGS :
348+ if key in ('model' , 'render_data' ) + MANY_RELATION_KWARGS :
310349 list_kwargs [key ] = kwargs [key ]
311350 return cls (** list_kwargs )
312351
313352 def get_attribute (self , instance ):
353+ if not self .render_data :
354+ raise SkipField
314355 # check for a source fn defined on the serializer instead of the model
315356 if self .source and hasattr (self .parent , self .source ):
316357 serializer_method = getattr (self .parent , self .source )
0 commit comments