Source code for fastapi_contrib.serializers.utils

from enum import Enum
from typing import Type, List, Set, Mapping, Tuple, Sequence

from pydantic import Required, create_model
from pydantic.fields import (
    SHAPE_LIST,
    SHAPE_SET,
    SHAPE_MAPPING,
    SHAPE_TUPLE,
    SHAPE_TUPLE_ELLIPSIS,
    SHAPE_SEQUENCE,
)

from fastapi_contrib.serializers.common import AbstractMeta, Serializer


[docs]class FieldGenerationMode(int, Enum): """ Defines modes in which fields of decorated serializer should be generated. """ REQUEST = 1 RESPONSE = 2
[docs]def gen_model(cls: Type, mode: FieldGenerationMode): """ Generate `pydantic.BaseModel` based on fields in Serializer class, its Meta class and possible Model class. :param cls: serializer class (could be modelserializer or regular one) :param mode: field generation mode :return: newly generated `BaseModel` from fields in Model & Serializer """ _fields = {} _Meta = getattr(cls, "Meta", type("Meta")) Meta = type("Meta", (_Meta, AbstractMeta), {}) Config = getattr(cls, "Config", getattr(Serializer, "Config")) if mode == FieldGenerationMode.RESPONSE: excluded = Meta.exclude | Meta.write_only_fields else: excluded = Meta.exclude | Meta.read_only_fields if hasattr(Meta, "model") and Meta.model is not None: for f, t in Meta.model.__fields__.items(): if f not in excluded: f_def = t.default if t.required: f_def = Required if t.shape == SHAPE_LIST: _type = List[t.type_] elif t.shape == SHAPE_SET: _type = Set[t.type_] elif t.shape == SHAPE_MAPPING: _type = Mapping[t.key_field.type_, t.type_] elif t.shape == SHAPE_TUPLE: _type = t.type_ elif t.shape == SHAPE_TUPLE_ELLIPSIS: _type = Tuple[t.type_, ...] elif t.shape == SHAPE_SEQUENCE: _type = Sequence[t.type_] else: _type = t.type_ _fields.update({f: (_type, f_def)}) for f, t in cls.__fields__.items(): if f not in excluded: f_def = t.default if t.required: f_def = Required _fields.update({f: (t.type_, f_def)}) if mode == FieldGenerationMode.REQUEST: response_model = gen_model(cls, mode=FieldGenerationMode.RESPONSE) Serializer.Config = Config model = create_model(cls.__name__, __base__=Serializer, **_fields) setattr(model, "response_model", response_model) setattr(model, "Meta", Meta) setattr(model, "Config", Config) reserved_attrs = ["Meta", "response_model", "Config"] for attr, value in cls.__dict__.items(): if not attr.startswith("_") and attr not in reserved_attrs: setattr(model, attr, value) return model return create_model( f"{cls.__name__}Response", __config__=Config, **_fields )