7676 'read_only' , 'write_only' , 'required' , 'default' , 'initial' , 'source' ,
7777 'label' , 'help_text' , 'style' , 'error_messages' , 'allow_empty' ,
7878 'instance' , 'data' , 'partial' , 'context' , 'allow_null' ,
79- 'max_length' , 'min_length'
79+ 'max_length' , 'min_length' , 'max_depth'
8080)
81- LIST_SERIALIZER_KWARGS_REMOVE = ('allow_empty' , 'min_length' , 'max_length' )
81+ LIST_SERIALIZER_KWARGS_REMOVE = ('allow_empty' , 'min_length' , 'max_length' , 'max_depth' )
8282
8383ALL_FIELDS = '__all__'
8484
@@ -111,20 +111,25 @@ class BaseSerializer(Field):
111111 .data - Available.
112112 """
113113
114+ default_error_messages = {
115+ 'max_depth' : _ ('Nesting depth exceeds maximum allowed depth of {max_depth}.' )
116+ }
117+
114118 def __init__ (self , instance = None , data = empty , ** kwargs ):
115119 self .instance = instance
116120 if data is not empty :
117121 self .initial_data = data
118122 self .partial = kwargs .pop ('partial' , False )
119123 self ._context = kwargs .pop ('context' , {})
120124 kwargs .pop ('many' , None )
125+ self .max_depth = kwargs .pop ('max_depth' , None )
121126 super ().__init__ (** kwargs )
122127 self ._current_depth = 0
123- self ._root_max_depth = None
128+ self ._root_max_depth = self . max_depth
124129
125130 def bind (self , field_name , parent ):
126131 super ().bind (field_name , parent )
127- if hasattr (parent , '_root_max_depth' ) and parent ._root_max_depth is not None :
132+ if self . max_depth is None and hasattr (parent , '_root_max_depth' ) and parent ._root_max_depth is not None :
128133 self ._root_max_depth = parent ._root_max_depth
129134 self ._current_depth = parent ._current_depth + 1
130135
@@ -137,6 +142,32 @@ def _propagate_depth_to_child(self):
137142 if hasattr (field , '_propagate_depth_to_child' ):
138143 field ._propagate_depth_to_child ()
139144
145+ def _check_data_depth (self , data , current_level ):
146+ if isinstance (data , dict ):
147+ for value in data .values ():
148+ if isinstance (value , (list , tuple , dict )):
149+ next_level = current_level + 1
150+ if next_level > self ._root_max_depth :
151+ message = self .error_messages ['max_depth' ].format (
152+ max_depth = self ._root_max_depth
153+ )
154+ raise ValidationError ({
155+ api_settings .NON_FIELD_ERRORS_KEY : [message ]
156+ }, code = 'max_depth' )
157+ self ._check_data_depth (value , next_level )
158+ elif isinstance (data , (list , tuple )):
159+ for item in data :
160+ if isinstance (item , (list , tuple , dict )):
161+ next_level = current_level + 1
162+ if next_level > self ._root_max_depth :
163+ message = self .error_messages ['max_depth' ].format (
164+ max_depth = self ._root_max_depth
165+ )
166+ raise ValidationError ({
167+ api_settings .NON_FIELD_ERRORS_KEY : [message ]
168+ }, code = 'max_depth' )
169+ self ._check_data_depth (item , next_level )
170+
140171 def __new__ (cls , * args , ** kwargs ):
141172 # We override this method in order to automatically create
142173 # `ListSerializer` classes instead when `many=True` is set.
@@ -390,7 +421,8 @@ def fields(self):
390421 fields = BindingDict (self )
391422 for key , value in self .get_fields ().items ():
392423 fields [key ] = value
393- self ._propagate_depth_to_child ()
424+ if self ._root_max_depth is not None :
425+ self ._propagate_depth_to_child ()
394426 return fields
395427
396428 @property
@@ -507,6 +539,9 @@ def to_internal_value(self, data):
507539 raise ValidationError ({
508540 api_settings .NON_FIELD_ERRORS_KEY : [message ]
509541 }, code = 'invalid' )
542+ if self ._root_max_depth is not None and self .max_depth is not None :
543+ start_level = self ._current_depth
544+ self ._check_data_depth (data , start_level )
510545
511546 ret = {}
512547 errors = {}
@@ -672,6 +707,32 @@ def run_child_validation(self, data):
672707 """
673708 return self .child .run_validation (data )
674709
710+ def _check_data_depth (self , data , current_level ):
711+ if isinstance (data , (list , tuple )):
712+ for item in data :
713+ if isinstance (item , (list , tuple , dict )):
714+ next_level = current_level + 1
715+ if next_level > self ._root_max_depth :
716+ message = self .error_messages ['max_depth' ].format (
717+ max_depth = self ._root_max_depth
718+ )
719+ raise ValidationError ({
720+ api_settings .NON_FIELD_ERRORS_KEY : [message ]
721+ }, code = 'max_depth' )
722+ self ._check_data_depth (item , next_level )
723+ elif isinstance (data , dict ):
724+ for value in data .values ():
725+ if isinstance (value , (list , tuple , dict )):
726+ next_level = current_level + 1
727+ if next_level > self ._root_max_depth :
728+ message = self .error_messages ['max_depth' ].format (
729+ max_depth = self ._root_max_depth
730+ )
731+ raise ValidationError ({
732+ api_settings .NON_FIELD_ERRORS_KEY : [message ]
733+ }, code = 'max_depth' )
734+ self ._check_data_depth (value , next_level )
735+
675736 def to_internal_value (self , data ):
676737 """
677738 List of dicts of native values <- List of dicts of primitive datatypes.
@@ -687,6 +748,10 @@ def to_internal_value(self, data):
687748 api_settings .NON_FIELD_ERRORS_KEY : [message ]
688749 }, code = 'not_a_list' )
689750
751+ if self ._root_max_depth is not None and self .max_depth is not None :
752+ start_level = self ._current_depth
753+ self ._check_data_depth (data , start_level )
754+
690755 if not self .allow_empty and len (data ) == 0 :
691756 message = self .error_messages ['empty' ]
692757 raise ValidationError ({
0 commit comments