Создание производственных конвейеров для создания фиктивных данных с помощью Polyfactory с использованием Dataclasses, Pydantic, Attrs и вложенных моделей

В этом руководстве мы подробно рассмотрим продвинутое использование Polyfactory, сосредоточив внимание на том, как можно генерировать богатые и реалистичные фиктивные данные непосредственно из подсказок типов Python.

Установка среды и зависимостей

Мы начнём с настройки среды и постепенно создадим фабрики для классов данных, моделей Pydantic и классов на основе attrs, демонстрируя настройку, переопределения, вычисляемые поля и генерацию вложенных объектов.

«`python
import subprocess
import sys

def install_package(package):
subprocess.check_call([sys.executable, «-m», «pip», «install», «-q», package])

packages = [
«polyfactory»,
«pydantic»,
«email-validator»,
«faker»,
«msgspec»,
«attrs»
]

for package in packages:
try:
install_package(package)
print(f»✓ Installed {package}»)
except Exception as e:
print(f»✗ Failed to install {package}: {e}»)

print(«\n»)
print(«=» * 80)
print(«SECTION 2: Basic Dataclass Factories»)
print(«=» * 80)
«`

Основные фабрики классов данных

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

«`python
from dataclasses import dataclass
from typing import List, Optional
from datetime import datetime, date
from uuid import UUID
from polyfactory.factories import DataclassFactory

@dataclass
class Address:
street: str
city: str
country: str
zip_code: str

@dataclass
class Person:
id: UUID
name: str
email: str
age: int
birth_date: date
is_active: bool
address: Address
phone_numbers: List[str]
bio: Optional[str] = None

class PersonFactory(DataclassFactory[Person]):
pass

person = PersonFactory.build()
print(f»Generated Person:»)
print(f» ID: {person.id}»)
print(f» Name: {person.name}»)
print(f» Email: {person.email}»)
print(f» Age: {person.age}»)
print(f» Address: {person.address.city}, {person.address.country}»)
print(f» Phone Numbers: {person.phone_numbers[:2]}»)
print()
«`

Настройка поведения фабрики

Мы сосредоточились на генерации простых, но реалистичных фиктивных данных, используя классы данных и поведение Polyfactory по умолчанию.

«`python
from faker import Faker
from polyfactory.fields import Use, Ignore

@dataclass
class Employee:
employee_id: str
full_name: str
department: str
salary: float
hire_date: date
is_manager: bool
email: str
internal_notes: Optional[str] = None

class EmployeeFactory(DataclassFactory[Employee]):
faker = Faker(locale=»en_US»)
random_seed = 42

@classmethod
def employee_id(cls) -> str:
return f»EMP-{cls.random.randint(10000, 99999)}»

@classmethod
def full_name(cls) -> str:
return cls.faker.name()

@classmethod
def department(cls) -> str:
departments = [«Engineering», «Marketing», «Sales», «HR», «Finance»]
return cls.random.choice(departments)

@classmethod
def salary(cls) -> float:
return round(cls.random.uniform(50000, 150000), 2)

@classmethod
def email(cls) -> str:
return cls.faker.company_email()
«`

Ограничения полей и вычисляемые поля

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

«`python
@dataclass
class Product:
product_id: str
name: str
description: str
price: float
discount_percentage: float
stock_quantity: int
final_price: Optional[float] = None
sku: Optional[str] = None

class ProductFactory(DataclassFactory[Product]):
@classmethod
def product_id(cls) -> str:
return f»PROD-{cls.random.randint(1000, 9999)}»

@classmethod
def name(cls) -> str:
adjectives = [«Premium», «Deluxe», «Classic», «Modern», «Eco»]
nouns = [«Widget», «Gadget», «Device», «Tool», «Appliance»]
return f»{cls.random.choice(adjectives)} {cls.random.choice(nouns)}»

@classmethod
def price(cls) -> float:
return round(cls.random.uniform(10.0, 1000.0), 2)

@classmethod
def discount_percentage(cls) -> float:
return round(cls.random.uniform(0, 30), 2)

@classmethod
def stock_quantity(cls) -> int:
return cls.random.randint(0, 500)
«`

Комплексные вложенные структуры

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

«`python
from enum import Enum

class OrderStatus(str, Enum):
PENDING = «pending»
PROCESSING = «processing»
SHIPPED = «shipped»
DELIVERED = «delivered»
CANCELLED = «cancelled»

@dataclass
class OrderItem:
product_name: str
quantity: int
unit_price: float
total_price: Optional[float] = None

@dataclass
class ShippingInfo:
carrier: str
tracking_number: str
estimated_delivery: date

@dataclass
class Order:
order_id: str
customer_name: str
customer_email: str
status: OrderStatus
items: List[OrderItem]
order_date: datetime
shipping_info: Optional[ShippingInfo] = None
total_amount: Optional[float] = None
notes: Optional[str] = None
«`

Интеграция с Attrs

Мы расширили использование Polyfactory для проверенных моделей Pydantic и классов на основе attrs.

«`python
import attrs
from polyfactory.factories.attrs_factory import AttrsFactory

@attrs.define
class BlogPost:
title: str
author: str
content: str
views: int = 0
likes: int = 0
published: bool = False
published_at: Optional[datetime] = None
tags: List[str] = attrs.field(factory=list)

class BlogPostFactory(AttrsFactory[BlogPost]):
@classmethod
def title(cls) -> str:
templates = [
«10 Tips for {}»,
«Understanding {}»,
«The Complete Guide to {}»,
«Why {} Matters»,
«Getting Started with {}»
]
topics = [«Python», «Data Science», «Machine Learning», «Web Development», «DevOps»]
template = cls.random.choice(templates)
topic = cls.random.choice(topics)
return template.format(topic)
«`

Построение с конкретными переопределениями

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

«`python
custom_person = PersonFactory.build(
name=»Alice Johnson»,
age=30,
email=»alice@example.com»
)
print(f»Custom Person:»)
print(f» Name: {custom_person.name}»)
print(f» Age: {custom_person.age}»)
print(f» Email: {custom_person.email}»)
print(f» ID (auto-generated): {custom_person.id}»)
print()
«`

Контроль на уровне полей с использованием Use и Ignore

Мы продемонстрировали, как можно явно контролировать отдельные поля, используя Use и Ignore.

«`python
from polyfactory.fields import Use, Ignore

@dataclass
class Configuration:
app_name: str
version: str
debug: bool
created_at: datetime
api_key: str
secret_key: str

class ConfigFactory(DataclassFactory[Configuration]):
app_name = Use(lambda: «MyAwesomeApp»)
version = Use(lambda: «1.0.0»)
debug = Use(lambda: False)

@classmethod
def api_key(cls) -> str:
return f»apikey{».join(cls.random.choices(‘0123456789abcdef’, k=32))}»

@classmethod
def secret_key(cls) -> str:
return f»secret_{».join(cls.random.choices(‘0123456789abcdef’, k=64))}»
«`

Тестирование покрытия модели

Мы рассмотрели передовые шаблоны использования, такие как явные переопределения, постоянные значения полей и сценарии тестирования покрытия.

«`python
from pydantic import BaseModel, ConfigDict
from typing import Union

class PaymentMethod(BaseModel):
modelconfig = ConfigDict(useenum_values=True)
type: str
card_number: Optional[str] = None
bank_name: Optional[str] = None
verified: bool = False

class PaymentMethodFactory(ModelFactory[PaymentMethod]):
model = PaymentMethod

payment_methods = [
PaymentMethodFactory.build(type=»card», card_number=»4111111111111111″),
PaymentMethodFactory.build(type=»bank», bank_name=»Chase Bank»),
PaymentMethodFactory.build(verified=True),
]
«`

Резюме

В этом руководстве мы рассмотрели:

  • Базовые фабрики классов данных.

  • Настраиваемые генераторы полей.

  • Ограничения полей.

  • Интеграцию с Pydantic.

  • Комплексные вложенные структуры.

  • Поддержку Attrs.

  • Построение с переопределениями.

  • Контроль на уровне полей с использованием Use и Ignore.

  • Тестирование покрытия.

Ключевые выводы:

  • Polyfactory автоматически генерирует фиктивные данные из подсказок типов.

  • Настройте генерацию с помощью методов класса и декораторов.

  • Поддерживает несколько библиотек: dataclasses, Pydantic, attrs, msgspec.

  • Используйте PostGenerated для вычисляемых/зависимых полей.

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

  • Идеально подходит для тестирования, разработки и прототипирования.

1. Какие библиотеки используются вместе с Polyfactory для генерации фиктивных данных?

В статье упоминается использование нескольких библиотек вместе с Polyfactory для генерации фиктивных данных. Среди них: dataclasses, Pydantic, attrs, msgspec.

2. Какие преимущества даёт использование Polyfactory для генерации фиктивных данных?

Polyfactory позволяет автоматически генерировать фиктивные данные из подсказок типов. Это упрощает процесс создания тестовых данных и делает его более эффективным. Кроме того, Polyfactory поддерживает настройку генерации с помощью методов класса и декораторов, что позволяет адаптировать генерацию под конкретные потребности.

3. Какие методы используются для настройки поведения фабрики в Polyfactory?

Для настройки поведения фабрики в Polyfactory используются методы класса и декораторы. В статье приводятся примеры использования методов класса для определения поведения фабрики при генерации данных, например, для генерации идентификаторов сотрудников или их имён.

4. Какие типы данных можно генерировать с помощью Polyfactory?

С помощью Polyfactory можно генерировать различные типы данных, включая строки, числа, даты и другие. В статье приводятся примеры генерации идентификаторов, имён, адресов, телефонных номеров и других данных.

5. Какие возможности Polyfactory можно использовать для тестирования покрытия модели?

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

Источник