44from api_v2 import models
55
66class GameContentSerializer (serializers .HyperlinkedModelSerializer ):
7-
7+ """
8+ Much of the logic included in the GameContentSerializer is intended to
9+ support manipulating data returned by the serializer via query parameters.
10+ """
11+
812 def remove_unwanted_fields (self , dynamic_params ):
913 """
1014 Takes the value of the 'fields', a string of comma-separated values,
11- and removes all other fields from the serializer
15+ and removes all fields not in this list from the serializer
1216 """
1317 if fields_to_keep := dynamic_params .pop ("fields" , None ):
1418 fields_to_keep = set (fields_to_keep .split ("," ))
@@ -27,12 +31,20 @@ def get_or_create_dynamic_params(self, child):
2731
2832 @staticmethod
2933 def split_param (dynamic_param ):
34+ """
35+ Splits a dynamic parameter into its target child serializer and value.
36+ Returns the values as a tuple.
37+ eg.
38+ 'document__fields=name' -> ('document', 'fields=name')
39+ 'document__gamesystem__fields=name' -> ('document', 'gamesystem__fields=name')
40+ """
3041 crumbs = dynamic_param .split ("__" )
3142 return crumbs [0 ], "__" .join (crumbs [1 :]) if len (crumbs ) > 1 else None
3243
3344 def set_dynamic_params_for_children (self , dynamic_params ):
3445 """
3546 Passes nested dynamic params to child serializer.
47+ eg. the param 'document__fields=name'
3648 """
3749 for param , fields in dynamic_params .items ():
3850 child , child_dynamic_param = self .split_param (param )
@@ -48,13 +60,26 @@ def set_dynamic_params_for_children(self, dynamic_params):
4860
4961 @staticmethod
5062 def is_param_dynamic (p ):
63+ """
64+ Returns true if parameter 'p' is a dynamic parameter. Currently the
65+ only dynamic param supported is 'fields', so we check for that
66+ """
5167 return p .endswith ("fields" )
5268
5369 def get_dynamic_params_for_root (self , request ):
70+ """
71+ Returns a dict of dynamic query parameters extracted from the 'request'
72+ object. Only works on the root serializer (child serializers do no
73+ include a 'request' field)
74+ """
5475 query_params = request .query_params .items ()
5576 return {k : v for k , v in query_params if self .is_param_dynamic (k )}
5677
5778 def get_dynamic_params (self ):
79+ """
80+ Returns dynamic parameters stored on the serializer context
81+ """
82+ # The context for ListSerializers is stored on the parent
5883 if isinstance (self .parent , serializers .ListSerializer ):
5984 return self .parent ._context .get ("dynamic_params" , {})
6085 return self ._context .get ("dynamic_params" , {})
@@ -69,30 +94,34 @@ def handle_depth_serialization(self, instance, representation):
6994 max_depth = self ._context .get ("max_depth" , 0 )
7095 current_depth = self ._context .get ("current_depth" , 0 )
7196
72- # if we reach the maximum depth, nested serializers return their pk
97+ # if we reach the maximum depth, nested serializers return their pk (url)
7398 if current_depth >= max_depth :
7499 for field_name , field in self .fields .items ():
75100 if isinstance (field , serializers .HyperlinkedModelSerializer ):
76101 nested_representation = representation .get (field_name )
77102 if nested_representation and "url" in nested_representation :
78103 representation [field_name ] = nested_representation ["url" ]
104+ return representation
79105
80106 # otherwise, pass depth to children serializers
81- else :
82- for field_name , field in self .fields .items ():
83- if isinstance (field , GameContentSerializer ):
84- nested_instance = getattr (instance , field_name )
85- nested_serializer = field .__class__ (nested_instance , context = {
86- ** self ._context ,
87- "current_depth" : current_depth + 1 ,
88- "max_depth" : max_depth ,
89- })
90- # Ensure dynamic params are specific to the child serializer
91- child_dynamic_params = self .get_or_create_dynamic_params (field_name )
92- nested_serializer ._context ['dynamic_params' ] = child_dynamic_params
93- representation [field_name ] = nested_serializer .data
94-
107+ for field_name , field in self .fields .items ():
108+ # Guard clause: make sure the child is a GameContentSerializer
109+ if not isinstance (field , GameContentSerializer ):
110+ continue
111+
112+ nested_instance = getattr (instance , field_name )
113+ nested_serializer = field .__class__ (nested_instance , context = {
114+ ** self ._context ,
115+ "current_depth" : current_depth + 1 ,
116+ "max_depth" : max_depth ,
117+ })
118+
119+ # Ensure dynamic params are specific to the child serializer
120+ child_dynamic_params = self .get_or_create_dynamic_params (field_name )
121+ nested_serializer ._context ['dynamic_params' ] = child_dynamic_params
122+ representation [field_name ] = nested_serializer .data
95123 return representation
124+
96125
97126 def __init__ (self , * args , ** kwargs ):
98127 request = kwargs .get ("context" , {}).get ("request" )
@@ -104,9 +133,6 @@ def __init__(self, *args, **kwargs):
104133 self ._context .update ({"dynamic_params" : dynamic_params })
105134
106135 def to_representation (self , instance ):
107- max_depth = self ._context .get ("max_depth" , 0 )
108- current_depth = self ._context .get ("current_depth" , 0 )
109-
110136 if dynamic_params := self .get_dynamic_params ().copy ():
111137 self .remove_unwanted_fields (dynamic_params )
112138 self .set_dynamic_params_for_children (dynamic_params )
0 commit comments