3.2 Модели Pydantic и их валидация
7 из 7 шагов пройдено
5 из 5 баллов  получено

Продолжим работать над нашим  CRUD проектом, а именно добавим модели. И посмотрим на их преимущества и особенности. Как выглядит наш код на этом этапе вы можете посмотреть по этой ссылке на GitHub.

Продолжим работать с нашим кодом, первым делом добавим модели:

from pydantic import BaseModel


class Message(BaseModel):
    id: int
    text: str

По аналогии, как и раньше, наши записи содержат поля ID и текста. Ранее в функции, отвечающей за POST запрос, мы принимали параметр message: str = Body(), и далее увеличивали значение ID и записывали в словарь данные. Но так как использовать вложенный словарь в словаре в качестве БД тут будет не совсем уместно, первым делом переделаем нашу БД в виде списка:

messages_db = []

В результате чего, мы будем иметь следующую структуру нашей БД (списка)

[
  {
    "id": 0,
    "text": "string"
  },
  {
    "id": 1,
    "text": "string"
  },
  {
    "id": 2,
    "text": "string"
  }
]

Перейдем непосредственно к функции create_message и внесем следующие изменения:

@app.post("/message", status_code=status.HTTP_201_CREATED)
async def create_message(message: Message) -> str:
    if len(messages_db) == 0:
        message.id = 0
    else:
        message.id = max([i.dict()['id'] for i in messages_db]) + 1
    messages_db.append(message)
    return f"Message created!"

Теперь мы принимаем в переменную message модель Message, которая содержит поля: message.id и message.text.  Мы также увеличиваем значение ID исходя из максимального значения ID записи, прибавляем 1 и записываем модель в нашу БД(список), выводя сообщение об успешной записи. Если БД(список) пуст, то message.id будет равно 0.

Запустим приложение через команду  uvicorn crud:app --port 8000 --reload и проверим работу. Зайдем в документацию, и перейдем в функцию, отвечающую за POST запросы.

И мы встречаем самое первое изменение, у нас теперь нет параметров запроса. То есть все параметры теперь всегда будут находиться внутри содержимого тела запроса. Второе изменение, это то, что теперь у нас есть пример содержимого тела запроса.

На данном этапе пока не будем ничего отправлять в запросе, перейдем дальше к изменениям функциям отвечающим за GET запросы.

В функции get_all_messages мы изменим вывод всех записей, а именно изменим вывод данных. А в функции get_message мы теперь получаем message_id как тип данных int, и далее с помощью индексов списка возвращаем запись.

from typing import List


@app.get("/")
async def get_all_messages() -> dict:
    return {'Messages': messages_db}


@app.get(path="/message/{message_id}")
async def get_message(message_id: int):
    return messages_db[message_id]

Перейдем к тестированию GET и POST запросов нашего приложения. Перейдя в POST запросы, мы не будем менять содержимое полей, и просто несколько раз нажмем кнопку Execute

 

Мы также видим что содержимое находится внутри тела запроса.

curl -X 'POST' \
  'http://127.0.0.1:8000/message' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "id": 0,
  "text": "string"
}'

Далее перейдем GET запрос вывода всех записей и получим все содержимое нашей БД (списка):

Мы видим что ID записи все также динамически изменяется, теперь попробуем получить запись с ID=2:

Мы видим что наш CRUD проект успешно работает через модели Pydantic. В следующем шаге мы продолжим дорабатывать эти функции, но и переделаем остальные функции нашего приложения.


Будьте вежливы и соблюдайте наши принципы сообщества. Пожалуйста, не оставляйте решения и подсказки в комментариях, для этого есть отдельный форум.
@app.post("/message", status_code=status.HTTP_201_CREATED)
async def create_message(message: Message) -> str:
    message.id = len(messages_db)
    messages_db.append(message)
    return f"Message created!"

При такой реализации ID в конце списка могут дублироваться, если был удалён элемент из середины списка

@Anonymous_38661511, согласен, совсем не подумал об этом. Поправил код в этом модуле, добавив проверку.