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

Для этой цели мы используем пакет Python с именем Pydantic.

pip install pydantic

После установки pydantic import BaseModel.

from pydantic import BaseModel

Теперь нам нужно создать нашу модель Pydantic для студентов, наследующих BaseModel.

class Student(BaseModel):
    name: str
    grade: str
    roll: int
    email: str
    phone: int
    subjects: List[str]
    friends: List[str]

Теперь мы создаем экземпляр Student [это только для демонстрационных целей].

student = Student(
    name=in_memory_student_db[0]["name"],
    grade=in_memory_student_db[0]["grade"],
    roll=in_memory_student_db[0]["roll"],
    email=in_memory_student_db[0]["email"],
    phone=in_memory_student_db[0]["phone"],
    subjects=in_memory_student_db[0]["subjects"],
    friends=in_memory_student_db[0]["friends"]
)
or
student = Student(**in_memory_student_db[0])

Оба способа мы можем создавать объекты Student.

Что делает класс pydantic?

  • Класс pydantic позволяет определять пользовательские типы данных, или мы можем расширить валидацию с помощью методов на модели, украшенной декоратором валидатора.
  • Здесь тип данных имени, оценки, списка, электронной почты, телефона, субъектов, друзей должен быть таким, как указано в классе pydantic, иначе это вызовет ошибку (ошибка проверки pydantic)

Но если вы передадите roll = «16», то он не будет показывать никаких ошибок, так как он преобразуется в целое число.

Теперь, поскольку мы создали класс Student, мы можем создать модель Student, а затем работать с этой моделью, поскольку она проверяет данные в нашей системе так, как мы хотим.

Чтобы получить сообщение об ошибке в формате JSON:

app.config.FALLBACK_ERROR_FORMAT = "json"

Поскольку объект Student не является сериализуемым JSON, мы используем метод dict и делаем объект Student JSON сериализуемым.

  • Поскольку мы не хотим сохранять объекты Student в нашей реальной базе данных, скорее мы хотим отображать и вставлять данные в строки в случае SQL или как объекты JSON в NoSQL, поэтому мы конвертируем объект Student в словарь Python.

Зачем делать объект Student JSON сериализуемым?

  • В Python «сериализация» не делает ничего, кроме преобразования заданной структуры данных (например, dict) в ее действительный объект JSON. JSON - это формат, который кодирует объекты в строку. Сериализация означает преобразование объекта в эту строку, а десериализация - это обратная операция (преобразование строки - ›объект).
  • При передаче ученика в файле, ученик должен быть байтовыми строками, но объекты Pydntic (здесь Student) редко бывают в этом формате. Сериализация может преобразовывать эти объекты Pydantic в байтовые строки для такого использования. После того, как байтовые строки будут переданы, получатель должен будет восстановить исходный объект из байтовой строки. Ref
  • Нам нужны сериализуемые объекты JSON для предоставления HTTP-ответа.

Теперь мы модифицируем наш код в соответствии с объектом Student.

Для метода POST

@app.post("/")
async def post_student(request):
    student = Student(**request.json)
    in_memory_student_db.append(student.dict())
    return response.json("message":"inserted","data":student.dict())
  • Мы запросим данные в формате JSON и передадим их классу Student, который вернет объект Student и сохранит его в переменной student.
  • перед тем, как вставить объект Student в БД, мы преобразовали объект Student в словарь Python, а затем вставили его в базу данных.
  • для только что вставленного ответа мы снова преобразовали объект Student в словарь Python.

Для метода PUT:

@app.put("/<id_:int>")
async def update_student(request, id_):
    student = Student(**request.json)
    if id_ in range(len(in_memory_student_db)):
        in_memory_student_db[id_] = student.dict()
        return response.json({"message":"updated Successfully"})
    return response.json({"error": "No Student with given id"})
  • Мы передаем id и упоминаем, что id_ имеет целочисленный тип,
  • Мы запросим обновление данных в формате JSON и передадим данные классу Student, который вернет объект Student и сохранит его в переменной student.
  • перед тем, как вставить объекты учеников в БД, мы проверяем, существует ли указанный идентификатор.
  • если id_ существует, мы преобразовали объект Student в словарь Python, а затем вставили его в базу данных и предоставили сообщение Success.
  • в противном случае мы сообщаем об ошибке.

Для метода GET:

Поскольку получение данных из базы данных не требует проверки, но данные, которые должны быть вставлены в базу данных, должны быть проверены, поэтому мы не будем использовать модель ученика при извлечении данных.

@app.get("/")
async def get_student(request):
    id_ = int(request.args.get("id", -1))
    if id_ != -1:
        if id_ in range(len(in_memory_student_db)):
            return response.json(in_memory_student_db[id_])
        else:
            return response.json(
            {
            "status": "error",
             "message": f"data with id {id_} not found. Try with different id!!!",
            })
    return response.json(in_memory_student_db)
  • Мы передаем идентификатор параметра запроса, чтобы получить данные определенного идентификатора. если параметру запроса не передается значение по умолчанию, если id_ равно -1
  • если значение if id_ не равно -1, мы проверяем, существует ли id_ в базе данных, если существует, мы предоставляем ответ этих данных, иначе мы предоставляем данные с заданным идентификатором, который не найден.
  • иначе мы предоставляем все данные, которые есть в базе данных. т.е. если значение id равно -1 (по умолчанию)

Теперь вы получите такой ответ:

Для метода DELETE

@app.delete("/<id_:int>")
async def delete_student(request, id_):
    if id_ in range(len(in_memory_student_db)):
        del in_memory_student_db[id_]
        return response.json({"message": "Deleted student successfully"})
    return response.json({"error": "No Student with given id"})
  • Мы передаем id и упоминаем, что id_ имеет целочисленный тип,
  • мы проверяем, существует ли id_ в базе данных, если существует, удаляем данные и даем ответ, что данные удалены
  • в противном случае мы сообщаем об ошибке.

Таким образом, мы можем проверить наши данные, мы также можем использовать настраиваемые валидаторы в модели pydantic, например, если нам требуется принять имя, которое должно содержать пробел, тогда вы можете указать валидатор для этого,

from pydantic import validator
@validator("name")
def name_must_contain_space(cls, name):
    if " " not in name:
        raise ValueError("must contain a space")
    return name.title()

На этом операции CRUD завершены, и мы научились проверять данные с помощью пидантических моделей. В следующей серии вместо использования базы данных в памяти мы будем использовать СУБД для хранения наших данных.

Больше контента на plainenglish.io