Если мы возьмём dict наподобие user_dict и передадим его в функцию (или класс), используя **user_dict, Python распакует его. Он передаст ключи и значения user_dict напрямую как аргументы типа ключ-значение.
Поэтому, продолжая описанный выше пример с user_dict, написание такого кода:
Цель использованных в примере вспомогательных функций - не более чем демонстрация возможных операций с данными, но, конечно, они не обеспечивают настоящую безопасность.
Сокращение дублирования кода - это одна из главных идей FastAPI.
Поскольку дублирование кода повышает риск появления багов, проблем с безопасностью, проблем десинхронизации кода (когда вы обновляете код в одном месте, но не обновляете в другом), и т.д.
А все описанные выше модели используют много общих данных и дублируют названия атрибутов и типов.
Мы можем это улучшить.
Мы можем определить модель UserBase, которая будет базовой для остальных моделей. И затем мы можем создать подклассы этой модели, которые будут наследовать её атрибуты (объявления типов, валидацию, и т.п.).
Все операции конвертации, валидации, документации, и т.п. будут по-прежнему работать нормально.
В этом случае мы можем определить только различия между моделями (с password в чистом виде, с hashed_password и без пароля):
Вы можете определить ответ как Union из двух типов. Это означает, что ответ должен соответствовать одному из них.
Он будет определён в OpenAPI как anyOf.
Для этого используйте стандартные аннотации типов в Python typing.Union:
Примечание
При объявлении Union, сначала указывайте наиболее детальные типы, затем менее детальные. В примере ниже более детальный PlaneItem стоит перед CarItem в Union[PlaneItem, CarItem].
fromtypingimportUnionfromfastapiimportFastAPIfrompydanticimportBaseModelapp=FastAPI()classBaseItem(BaseModel):description:strtype:strclassCarItem(BaseItem):type:str="car"classPlaneItem(BaseItem):type:str="plane"size:intitems={"item1":{"description":"All my friends drive a low rider","type":"car"},"item2":{"description":"Music is my aeroplane, it's my aeroplane","type":"plane","size":5,},}@app.get("/items/{item_id}",response_model=Union[PlaneItem,CarItem])asyncdefread_item(item_id:str):returnitems[item_id]
fromtypingimportUnionfromfastapiimportFastAPIfrompydanticimportBaseModelapp=FastAPI()classBaseItem(BaseModel):description:strtype:strclassCarItem(BaseItem):type:str="car"classPlaneItem(BaseItem):type:str="plane"size:intitems={"item1":{"description":"All my friends drive a low rider","type":"car"},"item2":{"description":"Music is my aeroplane, it's my aeroplane","type":"plane","size":5,},}@app.get("/items/{item_id}",response_model=Union[PlaneItem,CarItem])asyncdefread_item(item_id:str):returnitems[item_id]
В этом примере мы передаём Union[PlaneItem, CarItem] в качестве значения аргумента response_model.
Поскольку мы передаём его как значение аргумента вместо того, чтобы поместить его в аннотацию типа, нам придётся использовать Union даже в Python 3.10.
Если оно было бы указано в аннотации типа, то мы могли бы использовать вертикальную черту как в примере:
some_variable:PlaneItem|CarItem
Но если мы помещаем его в response_model=PlaneItem | CarItem мы получим ошибку, потому что Python попытается произвести некорректную операцию между PlaneItem и CarItem вместо того, чтобы интерпретировать это как аннотацию типа.
Таким же образом вы можете определять ответы как списки объектов.
Для этого используйте typing.List из стандартной библиотеки Python (или просто list в Python 3.9 и выше):
fromfastapiimportFastAPIfrompydanticimportBaseModelapp=FastAPI()classItem(BaseModel):name:strdescription:stritems=[{"name":"Foo","description":"There comes my hero"},{"name":"Red","description":"It's my aeroplane"},]@app.get("/items/",response_model=list[Item])asyncdefread_items():returnitems
fromtypingimportListfromfastapiimportFastAPIfrompydanticimportBaseModelapp=FastAPI()classItem(BaseModel):name:strdescription:stritems=[{"name":"Foo","description":"There comes my hero"},{"name":"Red","description":"It's my aeroplane"},]@app.get("/items/",response_model=List[Item])asyncdefread_items():returnitems
Вы также можете определить ответ, используя произвольный одноуровневый dict и определяя только типы ключей и значений без использования Pydantic-моделей.
Это полезно, если вы заранее не знаете корректных названий полей/атрибутов (которые будут нужны при использовании Pydantic-модели).
В этом случае вы можете использовать typing.Dict (или просто dict в Python 3.9 и выше):
Используйте несколько Pydantic-моделей и свободно применяйте наследование для каждой из них.
Вам не обязательно иметь единственную модель данных для каждой сущности, если эта сущность должна иметь возможность быть в разных "состояниях". Как в случае с "сущностью" пользователя, у которого есть состояния с полями password, password_hash и без пароля.