Pydantic to biblioteka Pythona, która waliduje i parsuje dane na podstawie zwykłych type hints — definiujesz klasę z adnotacjami typów, a Pydantic sam sprawdza, konwertuje i odrzuca niepoprawne dane w czasie działania programu. Jest fundamentem FastAPI, coraz częściej używany też do zarządzania konfiguracją aplikacji i jako alternatywa dla serializerów w Django REST Framework. W wersji 2 (2023) przeszedł przepisanie rdzenia na Rust, co wymusiło zmianę składni walidatorów i przeniesienie `BaseSettings` do osobnego pakietu.
Jeśli pisałeś kiedykolwiek funkcję, która na początku robi dziesięć
if-ów sprawdzających, czy dane od użytkownika mają sens — czy wiek to
w ogóle liczba, czy email zawiera znak @, czy cena nie jest ujemna —
to właśnie problem, który Pydantic rozwiązuje raz, deklaratywnie, zamiast
ręcznie za każdym razem od nowa.
Czym jest Pydantic i czym są jego modele?
Pydantic to biblioteka do walidacji danych oparta na natywnych type hints
Pythona. Zamiast pisać ręczną logikę sprawdzającą, definiujesz klasę
dziedziczącą po BaseModel, w której typy pól są jednocześnie regułami
walidacji:
from pydantic import BaseModel
class Uzytkownik(BaseModel):
imie: str
wiek: int
email: str
dane = {"imie": "Anna", "wiek": "28", "email": "anna@example.com"}
uzytkownik = Uzytkownik(**dane)
print(uzytkownik.wiek) # 28 (int, mimo że wejście było stringiem)
Taka klasa nazywana jest modelem — to po prostu struktura danych ze
zdefiniowanym kształtem i typami. Gdy podasz dane niezgodne z modelem
(np. wiek: "dwadzieścia osiem"), Pydantic rzuci czytelny wyjątek
ValidationError ze szczegółową informacją, które pole i dlaczego nie
przeszło walidacji — zamiast cichego błędu gdzieś głębiej w kodzie.
Pydantic robi przy tym dwie rzeczy naraz: waliduje (sprawdza, czy dane
pasują do typu) i parsuje (próbuje je rozsądnie skonwertować — string
"28" na int 28, zanim w ogóle zgłosi błąd). To rozróżnienie jest ważne:
model Pydantic to nie tylko "strażnik", który coś odrzuca, ale też
narzędzie, które normalizuje dane wejściowe do przewidywalnej postaci.
Do czego faktycznie wykorzystuje się Pydantic?
Pydantic rzadko jest celem samym w sobie — w praktyce pojawia się jako fundament innych narzędzi:
- FastAPI — najpopularniejszy framework do budowy API w Pythonie używa modeli Pydantic jako głównego mechanizmu walidacji requestów i responsów. Zadeklarowany model w sygnaturze endpointu automatycznie waliduje przychodzący JSON i generuje dokumentację API (OpenAPI/Swagger) bez dodatkowej konfiguracji.
- Zarządzanie konfiguracją aplikacji — przez
pydantic-settings(patrz niżej) można zdefiniować ustawienia aplikacji jako model, który sam czyta zmienne środowiskowe czy plik.envi waliduje ich typy przy starcie programu, zamiast odkrywać literówkę wPORTdopiero w produkcji. - Alternatywa dla serializerów w Django REST Framework — DRF ma własny system serializerów do walidacji danych wejściowych API, ale część zespołów backendowych w Pythonie decyduje się na modele Pydantic także przy pracy z Django, szczególnie w warstwie integracji z zewnętrznymi API lub kolejkami zadań, gdzie i tak nie ma pełnego kontekstu requestu HTTP, na którym opiera się DRF.
- Walidacja danych na granicy systemu w ogóle — pliki konfiguracyjne, odpowiedzi z zewnętrznych API, dane z kolejki wiadomości — wszędzie tam, gdzie dane "z zewnątrz" wchodzą do Twojego programu i warto upewnić się, że mają oczekiwany kształt, zanim cokolwiek z nimi zrobisz.
Własna walidacja pól i typowe pułapki
Oprócz prostych typów (str, int, list[str]) Pydantic pozwala pisać
własne reguły walidacji przez dekorator @field_validator:
from pydantic import BaseModel, field_validator
class Produkt(BaseModel):
cena: float
@field_validator("cena")
@classmethod
def cena_musi_byc_dodatnia(cls, wartosc):
if wartosc <= 0:
raise ValueError("Cena musi być większa od zera")
return wartosc
Jedna z najczęstszych pułapek początkujących (to realny i częsty problem
— potwierdza to ponad 92 tysiące wyświetleń jednego z pytań na Stack
Overflow) to pytanie, co
Pydantic robi z polami, których nie ma w modelu, ale są w przesłanych
danych. Domyślnie dodatkowe pola są po prostu ignorowane — jeśli zależy
Ci na tym, by model odrzucał nieznane pola zamiast je pomijać, trzeba
to jawnie skonfigurować przez model_config = ConfigDict(extra="forbid").
To rozróżnienie — "ignoruj" vs "odrzuć" — jest źródłem sporej części
nieporozumień przy pierwszym kontakcie z biblioteką.
Pydantic V2 — co się zmieniło i jak zmigrować z V1
Wersja 2, wydana w 2023 roku, przepisała rdzeń biblioteki z czystego
Pythona na Rust (pydantic-core), co dało kilkukrotny wzrost wydajności
walidacji — ale wymusiło też zmiany w API, które najbardziej doskwierają
projektom migrującym starszy kod. Poniżej najczęstsze realne problemy,
z jakimi mierzą się programiści (na podstawie faktycznie zadawanych
pytań na Stack Overflow, nie teoretycznej listy zmian z changeloga):
| Problem przy migracji | Co się zmieniło |
|---|---|
ImportError: BaseSettings has been moved | BaseSettings nie jest już częścią głównego pakietu pydantic — trzeba doinstalować osobny pakiet pydantic-settings i zmienić import na from pydantic_settings import BaseSettings |
@validator przestaje działać / ostrzeżenie o deprecjacji | Dekorator @validator z V1 zastąpiono @field_validator — nowa składnia wymaga też @classmethod nad metodą walidującą |
field_validator() got an unexpected keyword argument 'pre' | Argument pre=True (walidacja przed konwersją typu) zniknął — w V2 odpowiada mu mode="before" w @field_validator |
Brak dostępu do innych pól w walidatorze (dawne values) | W V1 walidator dostawał słownik values z już zwalidowanymi polami; w V2 trzeba użyć @model_validator (walidacja na poziomie całego modelu) zamiast pojedynczego pola |
.env nie jest wykrywany, gdy program uruchamiany jest z innego katalogu | pydantic-settings domyślnie szuka .env względem bieżącego katalogu roboczego — trzeba jawnie podać ścieżkę przez model_config = SettingsConfigDict(env_file=".env") z pełną/względną ścieżką |
__root__ przestało działać | Model z pojedynczym polem __root__ (typowy dla modeli opakowujących listę/wartość) zastąpiono osobną klasą RootModel |
Jeśli migrujesz istniejący projekt, najbezpieczniejsza kolejność to:
najpierw zmiana importów (BaseSettings), potem przegląd wszystkich
@validator pod kątem pre=/values, na końcu sprawdzenie modeli
korzystających z __root__. Oficjalne narzędzie bump-pydantic od
zespołu Pydantic automatyzuje większość tych zmian składniowych, choć
logikę biznesową warto i tak przejrzeć ręcznie.
Nasi mentorzy na ścieżce Python/Django widzą ten sam wzorzec niemal w każdym
projekcie przechodzącym na nowsze wersje zależności: zespoły odkładają
migrację "na później", aż pydantic V1 trafia na listę pakietów bez
wsparcia bezpieczeństwa — a wtedy migracja robi się pod presją czasu,
zamiast spokojnie, modelami.


