def convert_to_pydantic_field_info(cls, allow_null: bool = False) -> FieldInfo: base = cls.default_value() if base is None: base = ( FieldInfo(default=None) if (cls.nullable or allow_null) else FieldInfo(default=pydantic.fields.Undefined) ) for attr_name in FieldInfo.__dict__.keys(): if cls.is_valid_field_info_field(attr_name): setattr(base, attr_name, cls.__dict__.get(attr_name)) return base
def create_response_field( name: str, type_: Type[Any], class_validators: Optional[Dict[str, Validator]] = None, default: Optional[Any] = None, required: Union[bool, UndefinedType] = False, model_config: Type[BaseConfig] = BaseConfig, field_info: Optional[FieldInfo] = None, alias: Optional[str] = None, ) -> ModelField: """ Create a new response field. Raises if type_ is invalid. """ class_validators = class_validators or {} field_info = field_info or FieldInfo(None) response_field = functools.partial( ModelField, name=name, type_=type_, class_validators=class_validators, default=default, required=required, model_config=model_config, alias=alias, ) try: return response_field(field_info=field_info) except RuntimeError: raise fastapi.exceptions.FastAPIError( f"Invalid args for response field! Hint: check that {type_} is a valid pydantic field type" )
def DjangoField(field): default = ... default_factory = None description = None title = None max_length = None # min_length = None if field.is_relation: internal_type = field.related_model._meta.pk.get_internal_type() if not field.concrete and field.auto_created: # Reverse relation default = None elif field.null: default = None python_type = FIELD_TYPES.get(internal_type, int) if field.one_to_many or field.many_to_many: python_type = List[python_type] field = field.target_field else: internal_type = field.get_internal_type() if internal_type in STR_TYPES: python_type = str max_length = field.max_length elif internal_type in INT_TYPES: python_type = int else: python_type = FIELD_TYPES[internal_type] deconstructed = field.deconstruct() field_options = deconstructed[3] or {} blank = field_options.pop("blank", False) null = field_options.pop("null", False) if field.has_default(): if callable(field.default): default_factory = field.default else: default = field.default elif field.primary_key or blank or null: default = None description = field.help_text title = field.verbose_name.title() return ( python_type, FieldInfo( default, default_factory=default_factory, title=title, description=description, max_length=max_length, # **extra, ), )
def get_schema_field(field: Field, *, depth: int = 0) -> Tuple: alias = None default = ... default_factory = None description = None title = None max_length = None python_type = None if field.is_relation: if depth > 0: return get_related_field_schema(field, depth=depth) internal_type = field.related_model._meta.pk.get_internal_type() if not field.concrete and field.auto_created or field.null: default = None if hasattr(field, "get_attname"): alias = field.get_attname() pk_type = TYPES.get(internal_type, int) if field.one_to_many or field.many_to_many: m2m_type = create_m2m_link_type(pk_type) python_type = List[m2m_type] else: python_type = pk_type else: field_options = field.deconstruct()[3] # 3 are the keywords blank = field_options.get("blank", False) null = field_options.get("null", False) max_length = field_options.get("max_length") internal_type = field.get_internal_type() python_type = TYPES[internal_type] if field.has_default(): if callable(field.default): default_factory = field.default else: default = field.default elif field.primary_key or blank or null: default = None description = field.help_text title = field.verbose_name.title() return ( python_type, FieldInfo( default, alias=alias, default_factory=default_factory, title=title, description=description, max_length=max_length, ), )
def create_field_info(cls, annotation: dict[str, Any], value: Any = Undefined): if isinstance(value, FieldInfo): # Annotations override the existing FieldInfo. Use deepcopy in case the FieldInfo is # used multiple times with different annotations. field_info, value = deepcopy(value), Undefined else: field_info = FieldInfo() # Populate the FieldInfo instance with our FieldAnnotations field_annotations = cls.collect_from_annotation(annotation) for field_annotation in field_annotations: field_annotation.populate_field_info(field_info) # If the value is set, use that as the default over any Default annotation. if value is not Undefined: field_info.default = value if (field_info.default is not Undefined and field_info.default_factory is not None): raise ValueError("cannot specify both default and default_factory") return field_info
def get_base_pydantic_field_info(cls, allow_null: bool) -> FieldInfo: """ Generates base pydantic.FieldInfo with only default and optionally required to fix pydantic Json field being set to required=False. Used in an ormar Model Metaclass. :param allow_null: flag if the default value can be None or if it should be populated by pydantic Undefined :type allow_null: bool :return: instance of base pydantic.FieldInfo :rtype: pydantic.FieldInfo """ base = cls.default_value() if base is None: base = (FieldInfo(default=None) if (cls.nullable or allow_null) else FieldInfo( default=Undefined)) if cls.__type__ == Json and base.default is Undefined: base.default = Required return base
def convert_to_pydantic_field_info(cls, allow_null: bool = False) -> FieldInfo: """ Converts a BaseField into pydantic.FieldInfo that is later easily processed by pydantic. Used in an ormar Model Metaclass. :param allow_null: flag if the default value can be None or if it should be populated by pydantic Undefined :type allow_null: bool :return: actual instance of pydantic.FieldInfo with all needed fields populated :rtype: pydantic.FieldInfo """ base = cls.default_value() if base is None: base = (FieldInfo(default=None) if (cls.nullable or allow_null) else FieldInfo( default=pydantic.fields.Undefined)) for attr_name in FieldInfo.__dict__.keys(): if cls.is_valid_field_info_field(attr_name): setattr(base, attr_name, cls.__dict__.get(attr_name)) return base
def _extension_model_factory( stac_extensions: Tuple[str], base_class: Type[Item], skip_remote_refs: bool = False ) -> Tuple[Type[BaseModel], FieldInfo]: """ Create a stac item properties model for a set of stac extensions """ fields = decompose_model(base_class.__fields__["properties"].type_) for ext in stac_extensions: if skip_remote_refs and ext.startswith("http"): continue if ext == "checksum": continue fields.update(decompose_model(Extensions.get(ext))) return ( create_model("CustomItemProperties", __base__=ItemProperties, **fields), FieldInfo(...), )
def get_related_field_schema(field: Field, *, depth: int): from ninja.orm import create_schema model = field.related_model schema = create_schema(model, depth=depth - 1) default = ... if not field.concrete and field.auto_created or field.null: default = None if isinstance(field, ManyToManyField): schema = List[schema] return ( schema, FieldInfo( default=default, description=field.help_text, title=field.verbose_name.title(), ), )
def MSONable_to_pydantic(monty_cls: type, pydantic_model=None): monty_props = { "@class": ( str, Field( default=monty_cls.__name__, title="MSONable Class", description="The formal class name for serialization lookup", ), ), "@module": ( str, Field( default=monty_cls.__module__, title="Python Module", description="The module this class is defined in", ), ), } if pydantic_model: props = { name: (field.type_, field.field_info) for name, field in pydantic_model.__fields__.items() } else: _type_hints = get_type_hints( monty_cls.__init__).items() # type: ignore props = { field_name: (__make_pydantic(field_type), FieldInfo(...)) for field_name, field_type in _type_hints } return create_model(monty_cls.__name__, field_definitions={ **monty_props, **props })
def create_cloned_field(field: ModelField) -> ModelField: original_type = field.type_ if is_dataclass(original_type) and hasattr(original_type, "__pydantic_model__"): original_type = original_type.__pydantic_model__ # type: ignore use_type = original_type if lenient_issubclass(original_type, BaseModel): original_type = cast(Type[BaseModel], original_type) use_type = create_model( original_type.__name__, __config__=original_type.__config__ ) for f in original_type.__fields__.values(): use_type.__fields__[f.name] = f use_type.__validators__ = original_type.__validators__ if PYDANTIC_1: new_field = ModelField( name=field.name, type_=use_type, class_validators={}, default=None, required=False, model_config=BaseConfig, field_info=FieldInfo(None), ) else: # pragma: nocover new_field = ModelField( # type: ignore name=field.name, type_=use_type, class_validators={}, default=None, required=False, model_config=BaseConfig, schema=FieldInfo(None), ) new_field.has_alias = field.has_alias new_field.alias = field.alias new_field.class_validators = field.class_validators new_field.default = field.default new_field.required = field.required new_field.model_config = field.model_config if PYDANTIC_1: new_field.field_info = field.field_info else: # pragma: nocover new_field.schema = field.schema # type: ignore new_field.allow_none = field.allow_none new_field.validate_always = field.validate_always if field.sub_fields: new_field.sub_fields = [ create_cloned_field(sub_field) for sub_field in field.sub_fields ] if field.key_field: new_field.key_field = create_cloned_field(field.key_field) new_field.validators = field.validators if PYDANTIC_1: new_field.pre_validators = field.pre_validators new_field.post_validators = field.post_validators else: # pragma: nocover new_field.whole_pre_validators = field.whole_pre_validators # type: ignore new_field.whole_post_validators = field.whole_post_validators # type: ignore new_field.parse_json = field.parse_json new_field.shape = field.shape try: new_field.populate_validators() except AttributeError: # pragma: nocover # TODO: remove when removing support for Pydantic < 1.0.0 new_field._populate_validators() # type: ignore return new_field
def __init__( self, path: str, endpoint: Callable, *, response_model: Type[Any] = None, status_code: int = 200, tags: List[str] = None, dependencies: Sequence[params.Depends] = None, summary: str = None, description: str = None, response_description: str = "Successful Response", responses: Dict[Union[int, str], Dict[str, Any]] = None, deprecated: bool = None, name: str = None, methods: Optional[Union[Set[str], List[str]]] = None, operation_id: str = None, response_model_include: Union[SetIntStr, DictIntStrAny] = None, response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(), response_model_by_alias: bool = True, response_model_exclude_unset: bool = False, include_in_schema: bool = True, response_class: Optional[Type[Response]] = None, dependency_overrides_provider: Any = None, ) -> None: self.path = path self.endpoint = endpoint self.name = get_name(endpoint) if name is None else name self.path_regex, self.path_format, self.param_convertors = compile_path( path) if methods is None: methods = ["GET"] self.methods = set([method.upper() for method in methods]) self.unique_id = generate_operation_id_for_path( name=self.name, path=self.path_format, method=list(methods)[0]) self.response_model = response_model if self.response_model: assert ( status_code not in STATUS_CODES_WITH_NO_BODY ), f"Status code {status_code} must not have a response body" response_name = "Response_" + self.unique_id if PYDANTIC_1: self.response_field: Optional[ModelField] = ModelField( name=response_name, type_=self.response_model, class_validators={}, default=None, required=False, model_config=BaseConfig, field_info=FieldInfo(None), ) else: self.response_field: Optional[ ModelField] = ModelField( # type: ignore # pragma: nocover name=response_name, type_=self.response_model, class_validators={}, default=None, required=False, model_config=BaseConfig, schema=FieldInfo(None), ) # Create a clone of the field, so that a Pydantic submodel is not returned # as is just because it's an instance of a subclass of a more limited class # e.g. UserInDB (containing hashed_password) could be a subclass of User # that doesn't have the hashed_password. But because it's a subclass, it # would pass the validation and be returned as is. # By being a new field, no inheritance will be passed as is. A new model # will be always created. self.secure_cloned_response_field: Optional[ ModelField] = create_cloned_field(self.response_field) else: self.response_field = None self.secure_cloned_response_field = None self.status_code = status_code self.tags = tags or [] if dependencies: self.dependencies = list(dependencies) else: self.dependencies = [] self.summary = summary self.description = description or inspect.cleandoc( self.endpoint.__doc__ or "") # if a "form feed" character (page break) is found in the description text, # truncate description text to the content preceding the first "form feed" self.description = self.description.split("\f")[0] self.response_description = response_description self.responses = responses or {} response_fields = {} for additional_status_code, response in self.responses.items(): assert isinstance(response, dict), "An additional response must be a dict" model = response.get("model") if model: assert ( additional_status_code not in STATUS_CODES_WITH_NO_BODY ), f"Status code {additional_status_code} must not have a response body" assert lenient_issubclass( model, BaseModel), "A response model must be a Pydantic model" response_name = f"Response_{additional_status_code}_{self.unique_id}" if PYDANTIC_1: response_field = ModelField( name=response_name, type_=model, class_validators=None, default=None, required=False, model_config=BaseConfig, field_info=FieldInfo(None), ) else: response_field = ModelField( # type: ignore # pragma: nocover name=response_name, type_=model, class_validators=None, default=None, required=False, model_config=BaseConfig, schema=FieldInfo(None), ) response_fields[additional_status_code] = response_field if response_fields: self.response_fields: Dict[Union[int, str], ModelField] = response_fields else: self.response_fields = {} self.deprecated = deprecated self.operation_id = operation_id self.response_model_include = response_model_include self.response_model_exclude = response_model_exclude self.response_model_by_alias = response_model_by_alias self.response_model_exclude_unset = response_model_exclude_unset self.include_in_schema = include_in_schema self.response_class = response_class assert inspect.isfunction(endpoint) or inspect.ismethod( endpoint), f"An endpoint must be a function or method" self.dependant = get_dependant(path=self.path_format, call=self.endpoint) for depends in self.dependencies[::-1]: self.dependant.dependencies.insert( 0, get_parameterless_sub_dependant(depends=depends, path=self.path_format), ) self.body_field = get_body_field(dependant=self.dependant, name=self.unique_id) self.dependency_overrides_provider = dependency_overrides_provider self.app = request_response(self.get_route_handler())
def BField(default: Any) -> Any: return FieldInfo( default, description="B Field", )
def ModelSchemaField(field: Any) -> tuple: default: Optional[Required] = Required default_factory = None description = None title = None max_length = None if field.is_relation: if not field.related_model: internal_type = field.model._meta.pk.get_internal_type() else: internal_type = field.related_model._meta.pk.get_internal_type() if not field.concrete and field.auto_created or field.null: default = None pk_type = FIELD_TYPES.get(internal_type, int) if field.one_to_many or field.many_to_many: python_type = List[Dict[str, pk_type]] else: python_type = pk_type if field.related_model: field = field.target_field else: if field.choices: enum_choices = {v: k for k, v in field.choices} python_type = Enum( # type: ignore f"{field.name.title().replace('_', '')}Enum", enum_choices, module=__name__, ) else: internal_type = field.get_internal_type() if internal_type in STR_TYPES: python_type = str if not field.choices: max_length = field.max_length elif internal_type in INT_TYPES: python_type = int elif internal_type in FIELD_TYPES: python_type = FIELD_TYPES[internal_type] else: # pragma: nocover # Only required for 2.2 to support custom field subclasses for field_class in type(field).__mro__: get_internal_type = getattr(field_class, "get_internal_type", None) if get_internal_type: _internal_type = get_internal_type(field_class()) if _internal_type in FIELD_TYPES: python_type = FIELD_TYPES[_internal_type] break deconstructed = field.deconstruct() field_options = deconstructed[3] or {} blank = field_options.pop("blank", False) null = field_options.pop("null", False) if field.has_default(): if callable(field.default): default_factory = field.default else: default = field.default elif field.primary_key or blank or null: default = None description = field.help_text title = field.verbose_name.title() if not description: description = field.name return ( python_type, FieldInfo( default, default_factory=default_factory, title=title, description=str(description), max_length=max_length, ), )
class Item(BaseModel): id: int name: str price: float desc: str = FieldInfo(default="", requierd=False) city: Optional[str] = None
def ModelSchemaField(field: Any, schema_name: str) -> tuple: default = Required default_factory = None description = None title = None max_length = None python_type = None if field.is_relation: if not field.related_model: internal_type = field.model._meta.pk.get_internal_type() else: internal_type = field.related_model._meta.pk.get_internal_type() if not field.concrete and field.auto_created or field.null: default = None pk_type = FIELD_TYPES.get(internal_type, int) if field.one_to_many or field.many_to_many: python_type = List[Dict[str, pk_type]] else: python_type = pk_type if field.related_model: field = field.target_field else: if field.choices: enum_choices = {} for k, v in field.choices: if Promise in type(v).__mro__: v = str(v) enum_choices[v] = k if field.blank: enum_choices['_blank'] = '' enum_prefix = ( f"{schema_name.replace('_', '')}{field.name.title().replace('_', '')}" ) python_type = Enum( # type: ignore f"{enum_prefix}Enum", enum_choices, module=__name__, ) if field.has_default() and isinstance(field.default, Enum): default = field.default.value else: internal_type = field.get_internal_type() if internal_type in STR_TYPES: python_type = str if not field.choices: max_length = field.max_length elif internal_type in INT_TYPES: python_type = int elif internal_type in FIELD_TYPES: python_type = FIELD_TYPES[internal_type] else: # pragma: nocover for field_class in type(field).__mro__: get_internal_type = getattr(field_class, "get_internal_type", None) if get_internal_type: _internal_type = get_internal_type(field_class()) if _internal_type in FIELD_TYPES: python_type = FIELD_TYPES[_internal_type] break if python_type is None: logger.warning("%s is currently unhandled, defaulting to str.", field.__class__) python_type = str deconstructed = field.deconstruct() field_options = deconstructed[3] or {} blank = field_options.pop("blank", False) null = field_options.pop("null", False) if default is Required and field.has_default(): if callable(field.default): default_factory = field.default default = Undefined else: default = field.default elif field.primary_key or blank or null: default = None if default is not None and field.null: python_type = Union[python_type, None] description = field.help_text title = field.verbose_name.title() if not description: description = field.name return ( python_type, FieldInfo( default, default_factory=default_factory, title=title, description=str(description), max_length=max_length, ), )