New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
🌐 Add Russian translation for docs/tutorial/extra-models.md
#9619
Merged
tiangolo
merged 3 commits into
tiangolo:master
from
ivan-abc:ru/docs/tutorial/extra-models
Jun 22, 2023
Merged
Changes from 1 commit
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,252 @@ | ||
# Дополнительные модели | ||
|
||
В продолжение прошлого примера будет уже обычным делом иметь несколько связанных между собой моделей. | ||
|
||
Это особенно применимо в случае моделей пользователя, потому что: | ||
|
||
* **Модель для ввода** должна иметь возможность содержать пароль. | ||
* **Модель для вывода** не должна содержать пароль. | ||
* **Модель для базы данных**, возможно, должна содержать хэшированный пароль. | ||
|
||
!!! danger "Внимание" | ||
Никогда не храните пароли пользователей в чистом виде. Всегда храните "безопасный хэш", который вы затем сможете проверить. | ||
|
||
Если вам это не знакомо, вы можете узнать про "хэш пароля" в [главах о безопасности](security/simple-oauth2.md#password-hashing){.internal-link target=_blank}. | ||
|
||
## Множественные модели | ||
|
||
Ниже изложена основная идея того, как могут выглядеть эти модели с полями для паролей, а также описаны места, где они используются: | ||
|
||
=== "Python 3.10+" | ||
|
||
```Python hl_lines="7 9 14 20 22 27-28 31-33 38-39" | ||
{!> ../../../docs_src/extra_models/tutorial001_py310.py!} | ||
``` | ||
|
||
=== "Python 3.6+" | ||
|
||
```Python hl_lines="9 11 16 22 24 29-30 33-35 40-41" | ||
{!> ../../../docs_src/extra_models/tutorial001.py!} | ||
``` | ||
|
||
### Про `**user_in.dict()` | ||
|
||
#### `.dict()` из Pydantic | ||
|
||
`user_in` - это Pydantic-модель класса `UserIn`. | ||
|
||
У Pydantic-моделей есть метод `.dict()`, который возвращает `dict` с данными модели. | ||
|
||
Поэтому, если мы создадим Pydantic-объект `user_in` таким способом: | ||
|
||
```Python | ||
user_in = UserIn(username="john", password="secret", email="john.doe@example.com") | ||
``` | ||
|
||
и затем вызовем: | ||
|
||
```Python | ||
user_dict = user_in.dict() | ||
``` | ||
|
||
то теперь у нас есть `dict` с данными модели в переменной `user_dict` (это `dict` вместо объекта Pydantic-модели). | ||
|
||
И если мы вызовем: | ||
|
||
```Python | ||
print(user_dict) | ||
``` | ||
|
||
мы можем получить `dict` с такими данными: | ||
|
||
```Python | ||
{ | ||
'username': 'john', | ||
'password': 'secret', | ||
'email': 'john.doe@example.com', | ||
'full_name': None, | ||
} | ||
``` | ||
|
||
#### Распаковка `dict` | ||
|
||
Если мы возьмём `dict` наподобие `user_dict` и передадим его в функцию (или класс), используя `**user_dict`, Python распакует его. Он передаст ключи и значения `user_dict` напрямую как аргументы типа ключ-значение. | ||
|
||
Поэтому, продолжая описанный выше пример с `user_dict`, написание такого кода: | ||
|
||
```Python | ||
UserInDB(**user_dict) | ||
``` | ||
|
||
Будет работать так же, как примерно такой код: | ||
|
||
```Python | ||
UserInDB( | ||
username="john", | ||
password="secret", | ||
email="john.doe@example.com", | ||
full_name=None, | ||
) | ||
``` | ||
|
||
Или, если для большей точности мы напрямую используем `user_dict` с любым потенциальным содержимым, то этот пример будет выглядеть так: | ||
|
||
```Python | ||
UserInDB( | ||
username = user_dict["username"], | ||
password = user_dict["password"], | ||
email = user_dict["email"], | ||
full_name = user_dict["full_name"], | ||
) | ||
``` | ||
|
||
#### Pydantic-модель из содержимого другой модели | ||
|
||
Как в примере выше мы получили `user_dict` из `user_in.dict()`, этот код: | ||
|
||
```Python | ||
user_dict = user_in.dict() | ||
UserInDB(**user_dict) | ||
``` | ||
|
||
будет равнозначен такому: | ||
|
||
```Python | ||
UserInDB(**user_in.dict()) | ||
``` | ||
|
||
...потому что `user_in.dict()` - это `dict`, и затем мы указываем, чтобы Python его "распаковал", когда передаём его в `UserInDB` и ставим перед ним `**`. | ||
|
||
Таким образом мы получаем Pydantic-модель на основе данных из другой Pydantic-модели. | ||
|
||
#### Распаковка `dict` и дополнительные именованные аргументы | ||
|
||
И затем, если мы добавим дополнительный именованный аргумент `hashed_password=hashed_password` как здесь: | ||
|
||
```Python | ||
UserInDB(**user_in.dict(), hashed_password=hashed_password) | ||
``` | ||
|
||
... то мы получим что-то подобное: | ||
|
||
```Python | ||
UserInDB( | ||
username = user_dict["username"], | ||
password = user_dict["password"], | ||
email = user_dict["email"], | ||
full_name = user_dict["full_name"], | ||
hashed_password = hashed_password, | ||
) | ||
``` | ||
|
||
!!! warning "Предупреждение" | ||
ivan-abc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Цель использованных в примере вспомогательных функций - не более чем демонстрация возможных операций с данными, но, конечно, они не обеспечивают настоящую безопасность. | ||
|
||
## Сократите дублирование | ||
|
||
Сокращение дублирования кода - это одна из главных идей **FastAPI**. | ||
|
||
Поскольку дублирование кода повышает риск появления багов, проблем с безопасностью, проблем десинхронизации кода (когда вы обновляете код в одном месте, но не обновляете в другом), и т.д. | ||
|
||
А все описанные выше модели используют много общих данных и дублируют названия атрибутов и типов. | ||
|
||
Мы можем это улучшить. | ||
|
||
Мы можем определить модель `UserBase`, которая будет базовой для остальных моделей. И затем мы можем создать подклассы этой модели, которые будут наследовать её атрибуты (объявления типов, валидацию, и т.п.). | ||
|
||
Все операции конвертации, валидации, документации, и т.п. будут по-прежнему работать нормально. | ||
|
||
В этом случае мы можем определить только различия между моделями (с `password` в чистом виде, с `hashed_password` и без пароля): | ||
|
||
=== "Python 3.10+" | ||
|
||
```Python hl_lines="7 13-14 17-18 21-22" | ||
{!> ../../../docs_src/extra_models/tutorial002_py310.py!} | ||
``` | ||
|
||
=== "Python 3.6+" | ||
|
||
```Python hl_lines="9 15-16 19-20 23-24" | ||
{!> ../../../docs_src/extra_models/tutorial002.py!} | ||
``` | ||
|
||
## `Union` или `anyOf` | ||
|
||
Вы можете определить ответ как `Union` из двух типов. Это означает, что ответ должен соответствовать одному из них. | ||
|
||
Он будет определён в OpenAPI как `anyOf`. | ||
|
||
Для этого используйте стандартные аннотации типов в Python <a href="https://docs.python.org/3/library/typing.html#typing.Union" class="external-link" target="_blank">`typing.Union`</a>: | ||
|
||
!!! note "Примечание" | ||
ivan-abc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
При объявлении <a href="https://pydantic-docs.helpmanual.io/usage/types/#unions" class="external-link" target="_blank">`Union`</a>, сначала указывайте наиболее детальные типы, затем менее детальные. В примере ниже более детальный `PlaneItem` стоит перед `CarItem` в `Union[PlaneItem, CarItem]`. | ||
|
||
=== "Python 3.10+" | ||
|
||
```Python hl_lines="1 14-15 18-20 33" | ||
{!> ../../../docs_src/extra_models/tutorial003_py310.py!} | ||
``` | ||
|
||
=== "Python 3.6+" | ||
|
||
```Python hl_lines="1 14-15 18-20 33" | ||
{!> ../../../docs_src/extra_models/tutorial003.py!} | ||
``` | ||
|
||
### `Union` в Python 3.10 | ||
|
||
В этом примере мы передаём `Union[PlaneItem, CarItem]` в качестве значения аргумента `response_model`. | ||
|
||
Поскольку мы передаём его как **значение аргумента** вместо того, чтобы поместить его в **аннотацию типа**, нам придётся использовать `Union` даже в Python 3.10. | ||
|
||
Если оно было бы указано в аннотации типа, то мы могли бы использовать вертикальную черту как в примере: | ||
|
||
```Python | ||
some_variable: PlaneItem | CarItem | ||
``` | ||
|
||
Но если мы помещаем его в `response_model=PlaneItem | CarItem` мы получим ошибку, потому что Python попытается произвести **некорректную операцию** между `PlaneItem` и `CarItem` вместо того, чтобы интерпретировать это как аннотацию типа. | ||
|
||
## Список моделей | ||
|
||
Таким же образом вы можете определять ответы как списки объектов. | ||
|
||
Для этого используйте `typing.List` из стандартной библиотеки Python (или просто `list` в Python 3.9 и выше): | ||
|
||
=== "Python 3.9+" | ||
|
||
```Python hl_lines="18" | ||
{!> ../../../docs_src/extra_models/tutorial004_py39.py!} | ||
``` | ||
|
||
=== "Python 3.6+" | ||
|
||
```Python hl_lines="1 20" | ||
{!> ../../../docs_src/extra_models/tutorial004.py!} | ||
``` | ||
|
||
## Ответ с произвольным `dict` | ||
|
||
Вы также можете определить ответ, используя произвольный одноуровневый `dict` и определяя только типы ключей и значений без использования Pydantic-моделей. | ||
|
||
Это полезно, если вы заранее не знаете корректных названий полей/атрибутов (которые будут нужны при использовании Pydantic-модели). | ||
|
||
В этом случае вы можете использовать `typing.Dict` (или просто `dict` в Python 3.9 и выше): | ||
|
||
=== "Python 3.9+" | ||
|
||
```Python hl_lines="6" | ||
{!> ../../../docs_src/extra_models/tutorial005_py39.py!} | ||
``` | ||
|
||
=== "Python 3.6+" | ||
|
||
```Python hl_lines="1 8" | ||
{!> ../../../docs_src/extra_models/tutorial005.py!} | ||
``` | ||
|
||
## Резюме | ||
|
||
Используйте несколько Pydantic-моделей и свободно применяйте наследование для каждой из них. | ||
|
||
Вам не обязательно иметь единственную модель данных для каждой сущности, если эта сущность должна иметь возможность быть в разных "состояниях". Как в случае с "сущностью" пользователя, у которого есть состояния с полями `password`, `password_hash` и без пароля. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Мы до этого всегда писали без скобок, думаю есть смысл поддерживать стиль :)