@@ -378,6 +378,106 @@ Computed fields are:
378378 computed = lambda data : data[' subtotal' ] + data.get(' tax' , 0 )
379379 )
380380
381+ input_filter
382+ ~~~~~~~~~~~~
383+
384+ **Type **: ``type ``
385+ **Default **: ``None ``
386+
387+ Specify an InputFilter class to use for nested validation. When this parameter is
388+ provided, the field value must be a dictionary and will be validated against the
389+ nested InputFilter's rules.
390+
391+ This allows you to compose complex validation structures by nesting InputFilters
392+ within each other, enabling validation of nested objects and hierarchical data
393+ structures.
394+
395+ **Key Features: **
396+
397+ - Validates nested dictionary structures
398+ - Applies all filters and validators from the nested InputFilter
399+ - Supports multiple levels of nesting
400+ - Provides clear error messages with field context
401+
402+ .. code-block :: python
403+
404+ from flask_inputfilter import InputFilter
405+ from flask_inputfilter.declarative import field
406+ from flask_inputfilter.validators import IsIntegerValidator, IsStringValidator
407+
408+ class UserInputFilter (InputFilter ):
409+ id : int = field(required = True , validators = [IsIntegerValidator()])
410+ name: str = field(required = True , validators = [IsStringValidator()])
411+ email: str = field(required = True , validators = [IsStringValidator()])
412+
413+ class OrderInputFilter (InputFilter ):
414+ quantity: int = field(required = True , validators = [IsIntegerValidator()])
415+ user: dict = field(required = True , input_filter = UserInputFilter)
416+
417+ **Usage Example: **
418+
419+ .. code-block :: python
420+
421+ order_filter = OrderInputFilter()
422+ order_filter.data = {
423+ ' quantity' : 5 ,
424+ ' user' : {
425+ ' id' : 123 ,
426+ ' name' : ' John Doe' ,
427+ 428+ }
429+ }
430+
431+ validated_data = order_filter.validate_data()
432+ # validated_data['user'] is now a validated dict
433+
434+ **Multiple Levels of Nesting: **
435+
436+ .. code-block :: python
437+
438+ class AddressInputFilter (InputFilter ):
439+ city: str = field(required = True , validators = [IsStringValidator()])
440+ zipcode: str = field(required = True , validators = [IsStringValidator()])
441+
442+ class UserInputFilter (InputFilter ):
443+ id : int = field(required = True , validators = [IsIntegerValidator()])
444+ name: str = field(required = True , validators = [IsStringValidator()])
445+ address: dict = field(required = True , input_filter = AddressInputFilter)
446+
447+ class OrderInputFilter (InputFilter ):
448+ quantity: int = field(required = True , validators = [IsIntegerValidator()])
449+ user: dict = field(required = True , input_filter = UserInputFilter)
450+
451+ # Now you can validate deeply nested structures
452+ order_filter = OrderInputFilter()
453+ order_filter.data = {
454+ ' quantity' : 10 ,
455+ ' user' : {
456+ ' id' : 456 ,
457+ ' name' : ' Jane Smith' ,
458+ ' address' : {
459+ ' city' : ' New York' ,
460+ ' zipcode' : ' 10001'
461+ }
462+ }
463+ }
464+
465+ **Error Handling: **
466+
467+ If nested validation fails, the error will include context about which field failed:
468+
469+ .. code-block :: python
470+
471+ # If user.name is missing:
472+ # ValidationError: {'user': "Nested validation failed for field 'user': {'name': \"Field 'name' is required.\"}"}
473+
474+ **Important Notes: **
475+
476+ - The field value must be a dictionary, otherwise a validation error is raised
477+ - If the field is optional (``required=False ``) and the value is ``None ``, nested validation is skipped
478+ - All filters and validators from the nested InputFilter are applied
479+ - The nested InputFilter can also have its own nested fields, allowing unlimited nesting depth
480+
381481Advanced Field Patterns
382482-----------------------
383483
0 commit comments