0 added
0 removed
Original
2026-01-01
Modified
2026-03-10
1
<p>В эпоху реактивных фреймворков, таких как<a>React</a>,<a>Vue.js</a>,<a>Angular</a>и<a>SVELTE</a>, бекенд разработка смещается в сторону API - нам достаточно сделать<a>CRUD</a>-ы, настроить валидацию данных и (опционально) сделать авторизацию пользователя. В таком случае фронтенд-разработка максимально отделена от бекенд-части, тот же рендеринг шаблонов ложится на браузер пользователя.</p>
1
<p>В эпоху реактивных фреймворков, таких как<a>React</a>,<a>Vue.js</a>,<a>Angular</a>и<a>SVELTE</a>, бекенд разработка смещается в сторону API - нам достаточно сделать<a>CRUD</a>-ы, настроить валидацию данных и (опционально) сделать авторизацию пользователя. В таком случае фронтенд-разработка максимально отделена от бекенд-части, тот же рендеринг шаблонов ложится на браузер пользователя.</p>
2
<p>Для создания<a>RESTful</a>ресурсов есть дополнения для популярных библиотек. Например, для<a>Flask</a>можно воспользоваться<a>Flask-RESTful</a>, или более близкой к сегодняшней теме библиотекой<a>Flask-REST-JSONAPI</a>(эта реализация устарела и не поддерживается, есть<a>доработанный форк</a>с расширяющим возможности<a>дополнением</a>).</p>
2
<p>Для создания<a>RESTful</a>ресурсов есть дополнения для популярных библиотек. Например, для<a>Flask</a>можно воспользоваться<a>Flask-RESTful</a>, или более близкой к сегодняшней теме библиотекой<a>Flask-REST-JSONAPI</a>(эта реализация устарела и не поддерживается, есть<a>доработанный форк</a>с расширяющим возможности<a>дополнением</a>).</p>
3
<p>Для<a>Django</a>есть<a>Django REST framework</a>. Все эти и другие подобные решения отлично подходят для изменения курса разработки существующего проекта. Но что, если прямо сейчас необходимо начать проект с нуля, вы точно знаете, что нужно будет разрабатывать только API, а ещё хочется чего-то нового? Вот тут на сцене появляется<a>FastAPI</a>! Чем же примечателен этот фреймворк?</p>
3
<p>Для<a>Django</a>есть<a>Django REST framework</a>. Все эти и другие подобные решения отлично подходят для изменения курса разработки существующего проекта. Но что, если прямо сейчас необходимо начать проект с нуля, вы точно знаете, что нужно будет разрабатывать только API, а ещё хочется чего-то нового? Вот тут на сцене появляется<a>FastAPI</a>! Чем же примечателен этот фреймворк?</p>
4
<p>Ключевые преимущества можно прочитать на главной странице сайта, но главное, что нас сегодня интересует, это то, как здесь реализована работа с аннотациями типов. Если вы знакомы с<a>датаклассами</a>или библиотекой<a>attrs</a>, то уже понимаете суть<a>pydantic</a>- библиотеки, на которой и основан FastAPI. Давайте же посмотрим, как это работает (пропустим официальный<a>Hello World</a>, он ничем не отличается от того же Flask).</p>
4
<p>Ключевые преимущества можно прочитать на главной странице сайта, но главное, что нас сегодня интересует, это то, как здесь реализована работа с аннотациями типов. Если вы знакомы с<a>датаклассами</a>или библиотекой<a>attrs</a>, то уже понимаете суть<a>pydantic</a>- библиотеки, на которой и основан FastAPI. Давайте же посмотрим, как это работает (пропустим официальный<a>Hello World</a>, он ничем не отличается от того же Flask).</p>
5
<h3>Параметры в url</h3>
5
<h3>Параметры в url</h3>
6
<p>Похожим на Flask/Django образом указываем в пути переменную. Но делаем это в фигурных скобках, а тип объявляем в функции, которая будет обрабатывать запрос.</p>
6
<p>Похожим на Flask/Django образом указываем в пути переменную. Но делаем это в фигурных скобках, а тип объявляем в функции, которая будет обрабатывать запрос.</p>
7
from fastapi import FastAPI app = FastAPI() @app.get("/user/{user_id}/") def get_user(user_id: int): return {"user_id": user_id}<p>Также необходимо установить uvicorn для запуска. Пишем в терминале uvicorn main:app --reload и можем перейти на http://127.0.0.1:8000/docs/, где нас поприветствует автоматически сгенерированная Swagger документация:</p>
7
from fastapi import FastAPI app = FastAPI() @app.get("/user/{user_id}/") def get_user(user_id: int): return {"user_id": user_id}<p>Также необходимо установить uvicorn для запуска. Пишем в терминале uvicorn main:app --reload и можем перейти на http://127.0.0.1:8000/docs/, где нас поприветствует автоматически сгенерированная Swagger документация:</p>
8
<p><em>Автоматически сгенерированная документация Swagger</em>:</p>
8
<p><em>Автоматически сгенерированная документация Swagger</em>:</p>
9
<blockquote><p>Если хотите запускать не из терминала, а напрямую в коде, то достаточно импортировать uvicorn и добавить в конце файла строчку uvicorn.run(app).</p>
9
<blockquote><p>Если хотите запускать не из терминала, а напрямую в коде, то достаточно импортировать uvicorn и добавить в конце файла строчку uvicorn.run(app).</p>
10
</blockquote><p>Здесь мы можем в удобном виде тестировать созданные ресурсы. Но что же насчёт валидации? При попытке перейти на http://127.0.0.1:8000/user/spam/ мы получим такую ошибку:</p>
10
</blockquote><p>Здесь мы можем в удобном виде тестировать созданные ресурсы. Но что же насчёт валидации? При попытке перейти на http://127.0.0.1:8000/user/spam/ мы получим такую ошибку:</p>
11
{ "detail": [ { "loc": [ "path", "user_id" ], "msg": "value is not a valid integer", "type": "type_error.integer" } ] }<p>Библиотека выполнила валидацию за нас, поэтому мы можем быть уверены, что в функцию нам придёт именно int!</p>
11
{ "detail": [ { "loc": [ "path", "user_id" ], "msg": "value is not a valid integer", "type": "type_error.integer" } ] }<p>Библиотека выполнила валидацию за нас, поэтому мы можем быть уверены, что в функцию нам придёт именно int!</p>
12
<h3>Валидация объектов</h3>
12
<h3>Валидация объектов</h3>
13
<p>Теперь взглянем на более сложные запросы. Например, POST-запрос с передачей данных. Объявим модели с нужными нам полями:</p>
13
<p>Теперь взглянем на более сложные запросы. Например, POST-запрос с передачей данных. Объявим модели с нужными нам полями:</p>
14
class UserBase(BaseModel): """ Базовая модель пользователя. Задавать полное имя не обязательно """ nickname: str full_name: str = None class UserIn(UserBase): """ Описываем, какие данные ожидаем получить на вход при создании пользователя. Тут будут унаследованы все поля """ class UserOut(UserBase): """ То, как будет выглядеть возвращаемый объект. Добавляем поле `id`, оно будет "присвоено" при "сохранении" """ id: int<p>Теперь объявляем роут. Там проставляем айди-объекта в результат (необходимое поле для возврата на фронт):</p>
14
class UserBase(BaseModel): """ Базовая модель пользователя. Задавать полное имя не обязательно """ nickname: str full_name: str = None class UserIn(UserBase): """ Описываем, какие данные ожидаем получить на вход при создании пользователя. Тут будут унаследованы все поля """ class UserOut(UserBase): """ То, как будет выглядеть возвращаемый объект. Добавляем поле `id`, оно будет "присвоено" при "сохранении" """ id: int<p>Теперь объявляем роут. Там проставляем айди-объекта в результат (необходимое поле для возврата на фронт):</p>
15
@app.post("/user/", response_model=UserOut) def create_user(user_in: UserIn): user_out = UserOut(**user_in.dict(), id=42) return user_out<p>Можем перейти на страницу документации и посмотреть спецификацию. Тут мы видим и какие данные нужно прислать на сервер, и какие будут возвращены клиенту:</p>
15
@app.post("/user/", response_model=UserOut) def create_user(user_in: UserIn): user_out = UserOut(**user_in.dict(), id=42) return user_out<p>Можем перейти на страницу документации и посмотреть спецификацию. Тут мы видим и какие данные нужно прислать на сервер, и какие будут возвращены клиенту:</p>
16
<p><em>Спецификация POST-запроса</em>:</p>
16
<p><em>Спецификация POST-запроса</em>:</p>
17
<p>При запросе данные будут провалидированы, поэтому в переменной user_in будет та самая модель, которую мы ожидаем, и с нужными нам полями.</p>
17
<p>При запросе данные будут провалидированы, поэтому в переменной user_in будет та самая модель, которую мы ожидаем, и с нужными нам полями.</p>
18
<p>Теперь выполняем POST-запрос. В редакторе (на странице документации) можем для проверки убрать необязательное поле, поэтому передаём следующее:</p>
18
<p>Теперь выполняем POST-запрос. В редакторе (на странице документации) можем для проверки убрать необязательное поле, поэтому передаём следующее:</p>
19
<p>В ответ нам приходит такой JSON:</p>
19
<p>В ответ нам приходит такой JSON:</p>
20
{ "nickname": "OTUS", "full_name": null, "id": 42 }<p>Здесь мы видим, что всё пришло, как мы и ожидали. FastAPI выполнит валидацию полей "под капотом", а нам нужно заниматься только логикой приложения.</p>
20
{ "nickname": "OTUS", "full_name": null, "id": 42 }<p>Здесь мы видим, что всё пришло, как мы и ожидали. FastAPI выполнит валидацию полей "под капотом", а нам нужно заниматься только логикой приложения.</p>
21
<p>И это только самая верхушка возможностей валидации. Если вы были заинтересованы, то загляните в<a>User Guide</a>и<a>Advanced User Guide</a>- будете приятно удивлены количеством всего, что поможет упростить разработку! А вот, например,<a>готовый шаблон full-stack проекта</a>.</p>
21
<p>И это только самая верхушка возможностей валидации. Если вы были заинтересованы, то загляните в<a>User Guide</a>и<a>Advanced User Guide</a>- будете приятно удивлены количеством всего, что поможет упростить разработку! А вот, например,<a>готовый шаблон full-stack проекта</a>.</p>
22
<p>Кстати, ещё один пункт, как библиотека ускоряет разработку помимо того, что занимается валидацией данных за вас: благодаря pydantic и аннотациям типов ваша среда разработки будет подсказывать все свойства и методы объектов, с которыми вы работаете, а также подсвечивать ошибки, которые так легко допустить, изменив возвращаемые из функции данные, или просто опечатавшись.</p>
22
<p>Кстати, ещё один пункт, как библиотека ускоряет разработку помимо того, что занимается валидацией данных за вас: благодаря pydantic и аннотациям типов ваша среда разработки будет подсказывать все свойства и методы объектов, с которыми вы работаете, а также подсвечивать ошибки, которые так легко допустить, изменив возвращаемые из функции данные, или просто опечатавшись.</p>
23
23