77 Iterable ,
88 List ,
99 Optional ,
10- Set ,
1110 Tuple ,
1211 Type ,
1312 TypeVar ,
1413 Union ,
1514)
1615
1716import pydantic
18- from pydantic import BaseConfig , root_validator , validator
17+ from pydantic import BaseConfig
1918from pydantic import BaseModel as PydanticBaseModel
20- from pydantic .class_validators import ROOT_VALIDATOR_CONFIG_KEY , VALIDATOR_CONFIG_KEY
21- from pydantic .fields import FieldInfo , ModelField , Validator
19+ from pydantic .fields import FieldInfo , ModelField
2220
2321from fastapi_jsonapi .data_typing import TypeSchema
2422from fastapi_jsonapi .schema import (
3533)
3634from fastapi_jsonapi .schema_base import BaseModel , Field , RelationshipInfo , registry
3735from fastapi_jsonapi .splitter import SPLIT_REL
36+ from fastapi_jsonapi .validation_utils import (
37+ extract_field_validators ,
38+ extract_validators ,
39+ )
3840
3941JSON_API_RESPONSE_TYPE = Dict [Union [int , str ], Dict [str , Any ]]
4042
@@ -291,7 +293,10 @@ def _get_info_from_schema_for_building(
291293 # works both for to-one and to-many
292294 included_schemas .append ((name , field .type_ , relationship .resource_type ))
293295 elif name == "id" :
294- id_validators = self ._extract_field_validators (schema , target_field_name = "id" )
296+ id_validators = extract_field_validators (
297+ schema ,
298+ include_for_field_names = {"id" },
299+ )
295300 resource_id_field = (* (resource_id_field [:- 1 ]), id_validators )
296301
297302 if not field .field_info .extra .get ("client_can_set_id" ):
@@ -310,7 +315,7 @@ class ConfigOrmMode(BaseConfig):
310315 f"{ base_name } AttributesJSONAPI" ,
311316 ** attributes_schema_fields ,
312317 __config__ = ConfigOrmMode ,
313- __validators__ = self . _extract_validators (schema , exclude_for_field_names = {"id" }),
318+ __validators__ = extract_validators (schema , exclude_for_field_names = {"id" }),
314319 )
315320
316321 relationships_schema = pydantic .create_model (
@@ -378,111 +383,6 @@ def create_relationship_data_schema(
378383 self .relationship_schema_cache [cache_key ] = relationship_data_schema
379384 return relationship_data_schema
380385
381- def _is_target_validator (self , attr_name : str , value : Any , validator_config_key : str ) -> bool :
382- """
383- True if passed object is validator of type identified by "validator_config_key" arg
384-
385- :param attr_name:
386- :param value:
387- :param validator_config_key: Choice field, available options are pydantic consts
388- VALIDATOR_CONFIG_KEY, ROOT_VALIDATOR_CONFIG_KEY
389- """
390- return (
391- # also with private items
392- not attr_name .startswith ("__" )
393- and getattr (value , validator_config_key , None )
394- )
395-
396- def _unpack_validators (self , model : Type [BaseModel ], validator_config_key : str ) -> Dict [str , Validator ]:
397- """
398- Selects all validators from model attrs and unpack them from class methods
399-
400- :param model: Type[BaseModel]
401- :param validator_config_key: Choice field, available options are pydantic consts
402- VALIDATOR_CONFIG_KEY, ROOT_VALIDATOR_CONFIG_KEY
403- """
404- root_validator_class_methods = {
405- # validators only
406- attr_name : value
407- for attr_name , value in model .__dict__ .items ()
408- if self ._is_target_validator (attr_name , value , validator_config_key )
409- }
410-
411- return {
412- validator_name : getattr (validator_method , validator_config_key )
413- for validator_name , validator_method in root_validator_class_methods .items ()
414- }
415-
416- def _extract_root_validators (self , model : Type [BaseModel ]) -> Dict [str , Callable ]:
417- validators = {}
418-
419- unpacked_validators = self ._unpack_validators (model , ROOT_VALIDATOR_CONFIG_KEY )
420- for validator_name , validator_instance in unpacked_validators .items ():
421- validators [validator_name ] = root_validator (
422- pre = validator_instance .pre ,
423- skip_on_failure = validator_instance .skip_on_failure ,
424- allow_reuse = True ,
425- )(validator_instance .func )
426-
427- return validators
428-
429- def _extract_field_validators (
430- self ,
431- model : Type [BaseModel ],
432- target_field_name : str = None ,
433- exclude_for_field_names : Set [str ] = None ,
434- ) -> Dict [str , Callable ]:
435- """
436- :param model: Type[BaseModel]
437- :param target_field_name: Name of field for which validators will be returned.
438- If not set the function will return validators for all fields.
439- """
440- validators = {}
441- validator_origin_param_keys = ("pre" , "each_item" , "always" , "check_fields" )
442-
443- unpacked_validators = self ._unpack_validators (model , VALIDATOR_CONFIG_KEY )
444- for validator_name , (field_names , validator_instance ) in unpacked_validators .items ():
445- if target_field_name and target_field_name not in field_names :
446- continue
447- elif target_field_name :
448- field_names = [target_field_name ] # noqa: PLW2901
449-
450- if exclude_for_field_names :
451- field_names = [ # noqa: PLW2901
452- # filter names
453- field_name
454- for field_name in field_names
455- if field_name not in exclude_for_field_names
456- ]
457-
458- if not field_names :
459- continue
460-
461- validators [validator_name ] = validator (
462- * field_names ,
463- allow_reuse = True ,
464- ** {
465- # copy origin params
466- param_name : getattr (validator_instance , param_name )
467- for param_name in validator_origin_param_keys
468- },
469- )(validator_instance .func )
470-
471- return validators
472-
473- def _extract_validators (
474- self ,
475- model : Type [BaseModel ],
476- exclude_for_field_names : Set [str ] = None ,
477- ) -> Dict [str , Callable ]:
478- return {
479- ** self ._extract_field_validators (
480- model ,
481- exclude_for_field_names = exclude_for_field_names ,
482- ),
483- ** self ._extract_root_validators (model ),
484- }
485-
486386 def _build_jsonapi_object (
487387 self ,
488388 base_name : str ,
0 commit comments