Баннер мобильный (3) Пройти тест

Pydantic и Faker в Python: валидация данных, создание моделей и генерация тестовых записей

Как генерировать данные с Faker и затем валидировать их с Pydantic

Uncategorized

10 июля 2026

Поделиться

Скопировано
Pydantic и Faker в Python: валидация данных, создание моделей и генерация тестовых записей

Содержание

    Протестировать свой сервис на данных, похожих на настоящие, помогают специальные библиотеки Faker и Pydantic. Рассказываем, как их установить и для каких задач использовать.

    Что такое библиотека Faker и для чего она нужна

    Во время разработки и тестирования приложения на Python, когда уже есть структура или API, часто нужен набор данных, похожих на реальные. Это могут быть списки пользователей, заказов или email-адресов. Использовать настоящие персональные данные небезопасно, а создавать десятки и сотни записей вручную долго. Поэтому разработчики используют библиотеку Faker.

    Как генерировать данные в Faker
    Официальная страница с документацией по Faker. Источник

    Faker генерирует реалистичные имена, адреса, даты, номера телефонов. Ими можно заполнять локальные базы данных, проверять работу API, а также использовать их в автоматических тестах и при демонстрации работы приложения. 

    Для отдельных значений Faker можно настроить ограничения: выбрать locale (локаль), задать диапазон возраста или чисел и период для дат, а также использовать собственные провайдеры (наборы методов). Так тестовые данные будут ближе к требованиям проекта.

    Но Faker не знает структуру и бизнес-правила вашего проекта. Например, сам он не поймет, что пользователи в списке должны быть старше 18 лет или что email-адрес должен относиться к конкретному домену. Поэтому после генерации данных их нужно проверить на соответствие правилам приложения.

    Как проверять входные данные

    Этот вопрос относится не только к сгенерированным, но и к любым другим данным, которые приложение получает извне. 

    Например, наше приложение получило JSON-запрос из API, значения из формы или переменные окружения. Да, мы можем валидировать данные вручную, но только если данных немного, а требования к ним простые. 

    Если же появляется много дополнительных условий, то приходится искать, как это сделать быстро.

    В помощь разработчикам Самуэль Колвин создал библиотеку Pydantic. Она позволяет задавать ожидаемую структуру данных, проверять значения и создавать модель с проверенными значениями и ожидаемыми типами. Например, Pydantic может преобразовать строку в число, а при недопустимом значении вернуть ошибку валидации.

    Pydantic документация библиотеки
    Документация по Pydantic Validation. Источник

    Генерируем данные с Faker

    Выше мы уже разобрались, что Faker — это библиотека для генерации данных, похожих на реальные. Она работает через провайдеров или наборы методов для разных категорий данных: имен, адресов, дат или банковских реквизитов.

    Метод name() создает имя, email() — адрес электронной почты, а date_between() возвращает случайную дату из заданного периода. 

    Faker также поддерживает локализованные провайдеры. Поэтому для разных регионов можно получить соответствующие имена, адреса и номера телефонов — нужно только указать локаль.

    Как установить Faker

    Сначала установите библиотеку в активное виртуальное окружение проекта. Так Faker будет доступен только в зависимостях конкретного приложения, а не во всех программах, запущенных через Python:

    python -m pip install Faker

    Установить пакет в глобальную среду Python тоже можно, но вы рискуете наткнуться на проблему с зависимостями, когда обновление библиотеки для одного проекта может нарушить работу другого.

    После установки импортируйте класс Faker: 

    from faker import Faker

    Затем создайте объект генератора:

    fake = Faker()

    Теперь вы сможете вызывать методы для получения тестовых значений, к которым относятся:

    • имена;
    print(fake.name())
    • адреса электронной почты;
    print(fake.email())
    • номера телефонов;
    print(fake.phone_number())
    • адреса с городом, улицей и почтовым индексом.
    print(fake.address())

    При каждом вызове метода Faker генерирует случайное значение. В разных вызовах значения могут отличаться, но повторы тоже возможны.

    Как указать локаль в Faker

    По умолчанию Faker использует локаль en_US, поэтому он генерирует данные в американском формате. Чтобы получить значения другого региона, при создании объекта Faker нужно передать код locale в формате язык_регион.

    Например, для русскоязычных имен, адресов и названий городов используют локаль ru_RU:

    from faker import Faker
    
    fake = Faker("ru_RU")
    
    print(fake.name())
    print(fake.city())
    print(fake.address())
    print(fake.phone_number())

    Запустим пример в Online Faker Compiler с предустановленным Faker и получим сгенерированные данные.

    Генерация данных Faker

    Как сгенерировать карточку пользователя и задать правила в Faker

    Faker можно использовать для генерации записи с несколькими полями — например, для создания карточки пользователя. Чтобы хранить полученные данные, мы используем словарь.

    Также прямо в генераторе мы можем задать ограничения по возрасту и дате регистрации. Для этого применим метод random_int(), который возвращает число из указанного диапазона, а также date_between(), который возвращает дату в заданном интервале.

    from faker import Faker
    
    fake = Faker("ru_RU")
    
    user_data = {
        "full_name": fake.name(),
        "email": fake.email(),
        "phone": fake.phone_number(),
        "city": fake.city(),
        "age": fake.random_int(min=18, max=70),
        "registered_at": fake.date_between(
            start_date="-2y",
            end_date="today",
        ).isoformat(),
    }
    
    print(user_data)

    В результате мы получим словарь с вымышленным набором данных:

    Сгенерированные данные для карточки клиента Faker

    Как генерировать значения из заданного списка в Faker

    Не все поля должны содержать произвольные данные. Например, статус заказа может принимать только несколько заранее определенных значений вроде new, paid, shipped или cancelled. Генератор не должен создавать другие статусы, иначе приложение их не распознает.

    Для таких случаев в Faker есть метод random_element(). В него передают список допустимых вариантов, а он случайно выбирает один из них:

    from faker import Faker
    
    fake = Faker()
    
    order_status = fake.random_element(
        elements=("new", "paid", "shipped", "cancelled")
    )
    
    print(order_status)

    Получим:

    Сгенерированные значения из заданного списка Faker

    Как сделать данные воспроизводимыми в Faker

    Набор значений можно сделать одинаковым при каждом запуске. Для этого генератору нужно задать начальное значение.

    Для отдельного объекта Faker используем метод seed_instance(). Он фиксирует последовательность данных только для этого генератора и не влияет на другие объекты Faker в проекте:

    from faker import Faker
    
    fake = Faker("ru_RU")
    fake.seed_instance(42)
    
    print(fake.name())
    print(fake.email())

    Получим:

    Воспроизводимые данные в Faker

    Если при нескольких запусках указаны одна и та же версия Faker, одна и та же локаль, одно и то же значение seed (начальное значение) и тот же порядок вызова методов, то результат будет одним и тем же. 

    Если нужно задать общее начальное значение для всех создаваемых объектов Faker, то можно использовать метод класса Faker.seed():

    from faker import Faker
    
    Faker.seed(42)
    
    first_fake = Faker("ru_RU")
    second_fake = Faker("en_US")

    Тут мы создали два генератора — first_fake и second_fake. Проверим их работу, вызвав name():

    print(first_fake.name())
    print(second_fake.name())

    Получим:

    Операторы в Faker

    Но важно понимать, что методы с относительными датами, например date_between(start_date=»-2y», end_date=»today»), могут дать иной результат при запуске в другой день, потому что границы интервала зависят от текущей даты. Если хотите получить стабильную генерацию, то нужно указывать конкретные даты.

    Валидируем и приводим данные к нужному типу с Pydantic

    Библиотека Pydantic позволяет валидировать входные данные. С ней можно описать ожидаемую структуру объекта с помощью аннотаций типов.

    Например, в наше приложение отправили JSON-запрос с данными пользователя. Pydantic проверит, есть ли в нем обязательные поля, попробует привести значения к нужным типам и сообщит об ошибке, если это сделать не получится.

    Как установить Pydantic

    Принцип такой же, как и для Faker. Устанавливаем библиотеку в активное виртуальное окружение проекта:

    python -m pip install pydantic

    После установки класс BaseModel и другие компоненты Pydantic можно импортировать в код проекта.

    Как создать модель с Pydantic BaseModel

    Создадим модель пользователя с использованием класса BaseModel. В ней укажем, что id и age должны быть целыми числами, full_name и email — строками, а registered_at — датой:

    from datetime import date
    
    from pydantic import BaseModel
    
    class User(BaseModel):
        id: int
        full_name: str
        email: str
        age: int
        registered_at: date

    После определения полей и ожидаемых типов в эту модель можно передавать словарь. Его мы сгенерируем с помощью Faker.

    Словарь user_data у нас будет содержать сгенерированную информацию о пользователе.

    user_data = {
        "id": "1",
        "full_name": "Александр Смирнов",
        "email": "alexander.smirnov@example.org",
        "age": "34",
        "registered_at": "2025-08-14",
    }
    user = User(**user_data)
    print(user)

    Оператор ** распакует словарь user_data в именованные аргументы. Pydantic создаст объект:

    id=1 full_name='Александр Смирнов' email='alexander.smirnov@example.org' age=34 registered_at=datetime.date(2025, 8, 14)

    Обратите внимание, что в исходном словаре id и age были строками, а в модели они стали числами. Значение registered_at тоже преобразовалось из строки в объект date.

    Вы всегда можете провести проверку типов:

    print(type(user.id))
    print(type(user.age))
    print(type(user.registered_at))

    И получить вывод в консоли:

    <class 'int'>
    <class 'int'>
    <class 'datetime.date'>

    У Pydantic также есть «строгий режим» (strict mode), в котором автоматическое преобразование типов ограничено. Например, при передаче Python-словаря строка «34» не преобразуется в число для поля age: int и вызовет ошибку валидации.

    Что будет, если данные окажутся некорректными

    Заменим возраст на текстовую строку, которую нельзя преобразовать в целое число:

    invalid_user_data = {
        "id": "1",
        "full_name": "Александр Смирнов",
        "email": "alexander.smirnov@example.org",
        "age": "тридцать четыре",
        "registered_at": "2025-08-14",
    }
    user = User(**invalid_user_data)

    Тогда при создании модели мы получим исключение ValidationError

    Ошибка валидации: некорректные данные

    В сообщении будет указано поле, в котором возникла проблема:

    pydantic_core._pydantic_core.ValidationError: 1 validation error for User
    age
      Input should be a valid integer, unable to parse string as an integer

    Какие ограничения есть в Pydantic Field 

    Кроме типа данных (строка, число и так далее), в Pydantic можно валидировать и другие параметры, например количество символов в строке или диапазон значений. 

    Для таких ограничений используют функцию Field(). Она позволяет:

    • задать ограничения;
    • указать значение по умолчанию;
    • добавить описание и примеры для документации.

    Создадим модель товара:

    from pydantic import BaseModel, Field
    
    
    class Product(BaseModel):
        name: str = Field(
            min_length=2,
            max_length=100,
            description="Название товара",
            examples=["Беспроводная мышь"],
        )
        price: float = Field(
            gt=0,
            description="Цена товара в рублях",
            examples=[1599.90],
        )
        quantity: int = Field(default=0, ge=0)
        model: str = Field(
            pattern=r"^[A-Z]{2}-\d{4}$",
            description="Артикул в формате AB-1234",
            examples=["EL-1024"],
        )

    Field() задает такие ограничения для полей:

    min_lengthминимальная длина строкиname: str = Field(min_length=2)
    max_lengthмаксимальная длина строкиname: str = Field(max_length=100)
    gtзначение должно быть строго больше указанногоprice: float = Field(gt=0)
    geзначение должно быть больше или равно указанномуquantity: int = Field(ge=0)
    patternСоответствие регулярному выражениюmodel: str = Field(pattern=…)

    Создадим модель:

    product = Product(
        name="Беспроводная мышь",
        price=1599.90,
        quantity=12,
        model="EL-1024"
    )
    print(product)

    Получим объект:

    name='Беспроводная мышь' price=1599.9 quantity=12 model='EL-1024'

    А вот такая модель не пройдет проверку:

    invalid_product = Product(
        name="М",
        price=0,
        quantity=-3,
        model="мышь-1",
    )

    Получим ошибку:

    Ошибка в значении Pydantic

    Pydantic сообщит, что:

    • значение name слишком короткое; 
    • price должно быть больше нуля; 
    • quantity не может быть отрицательным; 
    • model не соответствует формату AB-1234.

    Как работают обязательные поля и значения по умолчанию

    Поля можно сделать необязательными. Для этого указывают значение поля по умолчанию.

    Например, тут поле name обязательно:

    name: str = Field(min_length=2, max_length=100)

    Но если есть default-значение, то поле можно не указывать:

    quantity: int = Field(default=0, ge=0)

    То есть если при создании товара не передать quantity, Pydantic поставит 0.

    Бывает так, что значение по умолчанию нельзя записать заранее, когда, например, для каждого нового заказа нужен свой пустой список товаров, а для каждой новой записи — уникальный идентификатор. 

    Тогда используют default_factory, чтобы Pydantic вызывал указанную функцию при создании каждой модели.

    from uuid import uuid4
    
    from pydantic import BaseModel, Field
    
    class Order(BaseModel):
        id: str = Field(default_factory=lambda: uuid4().hex)
        items: list[str] = Field(default_factory=list)
    
    order = Order()
    
    print(order.id)
    print(order.items)

    Теперь при создании нового заказа сгенерируется уникальный id и появится отдельный пустой список items:

    Генерация items в Pydantic

    Описание и примеры поля

    Отдельно стоит упомянуть description и examples — это параметры, которые встречались в предыдущих примерах в Field(). Поле description описывает назначение поля, а examples показывает ожидаемое значение. Это нужно для отображения в документации.

    Например:

    price: float = Field(
        gt=0,
        description="Цена товара в рублях",
        examples=[1599.90],
    )

    Когда Field() не поможет

    Field() не предназначен для проверки правил, которые зависят сразу от нескольких полей.

    Например, нельзя описать через Field(), что дата окончания подписки должна быть позже даты начала:

    from datetime import date
    
    from pydantic import BaseModel
    
    
    class Subscription(BaseModel):
        start_date: date
        end_date: date

    Оба значения могут быть корректными датами по отдельности, но вместе образовать неверную запись:

    Subscription(
        start_date="2026-06-20",
        end_date="2026-06-10",
    )

    Field() не может сравнить их между собой. Для таких случаев нужно использовать валидатор Pydantic.

    Как создать сложные правила проверки с помощью валидаторов Pydantic

    В Pydantic (v2) для сложных условий проверки используют декораторы @field_validator и @model_validator.

    • Для проверки одного поля — @field_validator.

    Создадим правило: имя пользователя не должно содержать пробелы.

    from pydantic import BaseModel, field_validator
    
    
    class RegistrationData(BaseModel):
        username: str
        password: str
    
        @field_validator("username")
        @classmethod
        def username_must_not_contain_spaces(cls, value: str) -> str:
            if " " in value:
                raise ValueError(
                    "Имя пользователя не должно содержать пробелы"
                )
            return value

    Такая запись пройдет проверку:

    user = RegistrationData(
    username="anna_smith",
    password="qwerty123",
    )

    А такая не пройдет:

    user = RegistrationData(
        username="anna smith",
        password="qwerty123",
    )

    Все потому, что когда валидатор получает значение поля username, он ищет в нем пробел. Если пробел найден, функция вызывает ValueError. Pydantic преобразует ее в ошибку валидации и не создает объект RegistrationData.

    • Для проверки нескольких полей — @model_validator.

    С помощью @model_validator можно провалидировать всю модель:

    from datetime import date
    
    from pydantic import BaseModel, model_validator
    
    
    class Subscription(BaseModel):
        start_date: date
        end_date: date
    
        @model_validator(mode="after")
        def end_date_must_be_after_start_date(self):
            if self.end_date <= self.start_date:
                raise ValueError(
                    "Дата окончания должна быть позже даты начала"
                )
            return self

    Тогда наконец мы сможем проверять даты по критерию «начальная должна быть раньше конечной». Эта запись пройдет проверку:

    Subscription(
        start_date="2026-06-10",
        end_date="2026-06-20",
    )

    А эта не пройдет:

    Subscription(
        start_date="2026-06-20",
        end_date="2026-06-10",
    )

    Как преобразовать модель Pydantic в словарь или JSON

    Модель Pydantic зачастую нужно передать в другую функцию, сохранить в базу или вернуть из API. Для этого модель преобразуют в словарь или JSON-строку.

    Основной метод для получения словаря называется model_dump():

    from datetime import date
    
    from pydantic import BaseModel
    
    
    class UserProfile(BaseModel):
        id: int
        full_name: str
        email: str
        password_hash: str
        middle_name: str | None = None
        newsletter: bool = False
        registered_at: date
    
    
    user = UserProfile(
        id=1,
        full_name="Александр Смирнов",
        email="alexander.smirnov@example.org",
        password_hash="hashed-password",
        registered_at="2026-06-20",
    )
    
    user_data = user.model_dump()
    print(user_data)

    Так мы получим словарь:

    {
        "id": 1,
        "full_name": "Александр Смирнов",
        "email": "alexander.smirnov@example.org",
        "password_hash": "hashed-password",
        "middle_name": None,
        "newsletter": False,
        "registered_at": datetime.date(2026, 6, 20),
    }

    Для получения JSON используют model_dump_json():

    json_data = user.model_dump_json()
    print(json_data)

    В результате получим строку в формате JSON:

    {"id":1,"full_name":"Александр Смирнов","email":"alexander.smirnov@example.org","password_hash":"hashed-password","middle_name":null,"newsletter":false,"registered_at":"2026-06-20"}

    Как модели Pydantic работают в API

    FastAPI использует Pydantic-модели при работе с запросами и ответами API.

    Сначала клиент отправляет JSON-запрос, затем FastAPI проверяет его по модели Pydantic. После этого функция получает уже проверенные данные.

    Например, клиент создает пользователя. В запросе он передает имя, email и пароль. Для получения этих данных создадим модель UserCreate.

    Для проверки формата email нужен тип EmailStr. Он использует дополнительную зависимость email-validator, поэтому установим FastAPI и дополнительную зависимость Pydantic для проверки email:

    python -m pip install fastapi "pydantic[email]"

    Теперь создадим две модели:

    from fastapi import FastAPI
    from pydantic import BaseModel, EmailStr
    
    app = FastAPI()
    
    class UserCreate(BaseModel):
        full_name: str
        email: EmailStr
        password: str
    
    class UserResponse(BaseModel):
        id: int
        full_name: str
        email: EmailStr
    • UserCreate описывает данные, которые клиент отправляет при регистрации.
    • UserResponse описывает данные, которые клиент получит в ответ. Пароля в ней нет, потому что возвращать его клиенту не нужно.

    Добавим обработчик запроса — эндпоинт:

    @app.post("/users", response_model=UserResponse)
    def create_user(user: UserCreate):
        internal_user = {
            "id": 1,
            "full_name": user.full_name,
            "email": user.email,
            "password_hash": "hashed-password",
            "is_admin": False,
        }
    
        return internal_user

    Когда клиент отправит запрос POST /users, FastAPI сначала проверит его по модели UserCreate. Например, если в запросе нет full_name или указан некорректный email, API вернет ошибку валидации и не запустит функцию create_user().

    Если данные корректны, то FastAPI создаст объект user и передаст его в функцию. Внутри функции мы формируем словарь internal_user, который содержит и внутренние данные — password_hash и is_admin.

    Параметр response_model = UserResponse указывает, какие поля можно отправить клиенту. FastAPI возьмет из internal_user только поля, описанные в UserResponse:

    {
      "id": 1,
      "full_name": "Александр Смирнов",
      "email": "alexander.smirnov@example.org"
    }

    Поля password_hash и is_admin в ответ не попадут. Таким образом, Pydantic-модели в FastAPI помогают проверить входящий запрос и при этом не раскрывать внутренние данные в ответе API.

    Uncategorized

    Поделиться

    Скопировано
    0 комментариев
    Комментарии