Kurs
Pydantic ist die beliebteste Datenvalidierungsbibliothek in Python, die Typhinweise in Validierungsregeln zur Laufzeit umwandeln kann.
Anstatt Dutzende von if isinstance()
Prüfungen und benutzerdefinierten Validierungsfunktionen zu schreiben, definierst du deine Datenstruktur einmal mit der vertrauten Python-Syntax.
Pydantic kümmert sich um den Rest: Es validiert die eingehenden Daten, konvertiert die Typen, wenn nötig, und gibt klare Fehlermeldungen aus, wenn die Validierung fehlschlägt.
Betrachte diese einfache Python-Funktion mit Typ-Hinweisen und der richtigen Dokumentation:
def calculate_user_discount(age: int, is_premium_member: bool, purchase_amount: float) -> float:
"""Calculate discount percentage based on user profile and purchase amount."""
if age >= 65:
base_discount = 0.15
elif is_premium_member:
base_discount = 0.10
else:
base_discount = 0.05
return purchase_amount * base_discount
Wenn du dir seine Signatur ansiehst, weißt du genau, welche Art von Daten er annimmt und am anderen Ende produziert. Aber kannst du dasselbe über den Endverbraucher sagen? Schließlich dienen diese Typ-Hinweise nur der Dokumentation - wenn jemand einen falschen Datentyp übergibt, stört sich der Python-Interpreter nicht daran:
# This runs without error, even though types are completely wrong!
discount = calculate_user_discount(True, 1, 5)
print(discount) # Output: 0.5 (True becomes 1, 1 is truthy, so 5 * 0.10)
Dies ist ein klassisches Beispiel für die dynamische Typisierung in Python. Sie ermöglicht Programmierern eine schnelle Entwicklung und Freiheit, aber das hat den Preis, dass sie Probleme bei der Typvalidierung bekommen, die in der Produktion oft auftreten.
In diesem Tutorial erkläre ich dir, wie du Pydantic in der Praxis einsetzen kannst. Wir beginnen mit deinem ersten Datenmodell und gehen dann zu fortgeschrittenen Mustern über, die in Produktionssystemen verwendet werden. Du erfährst, wie du verschachtelte Datenstrukturen validierst, benutzerdefinierte Validatoren für komplexe Geschäftsregeln schreibst und Pydantic mit gängigen Frameworks wie FastAPI und SQLAlchemy integrierst.
Was ist Pydantic?
Wenn du Anwendungen entwickelst, die mit der Außenwelt interagieren, spielst du im Grunde Datenroulette. Es kann vorkommen, dass Nutzer eine Telefonnummer als String statt als Ganzzahl übermitteln, dass eine API Nullwerte zurückgibt, wo du Zahlen erwartest, oder dass eine Konfigurationsdatei Tippfehler enthält, die deine Anwendung um 3 Uhr morgens zum Absturz bringen.
Traditionelle Python-Ansätze für dieses Problem werden schnell unhandlich. Am Ende schreibst du einen Validierungscode wie diesen:
def create_user(data):
# Manual validation nightmare
if not isinstance(data.get('age'), int):
raise ValueError("Age must be an integer")
if data['age'] < 0 or data['age'] > 150:
raise ValueError("Age must be between 0 and 150")
if not isinstance(data.get('email'), str) or '@' not in data['email']:
raise ValueError("Invalid email format")
if not isinstance(data.get('is_active'), bool):
raise ValueError("is_active must be a boolean")
# Finally create the user...
return User(data['age'], data['email'], data['is_active'])
Multipliziere dies mit jeder Datenstruktur in deiner Anwendung, und du wirst mehr Zeit damit verbringen, Validierungscode zu schreiben als Geschäftslogik.
Pydantic löst dieses Problem durch die Kombination von drei leistungsstarken Konzepten: Typ-Hinweise, Laufzeitvalidierung und automatische Serialisierung. Statt manueller Überprüfungen definierst du deine Datenstruktur einmal mit der Python-Syntax für Typ-Annotationen, und Pydantic übernimmt die gesamte Validierung automatisch:
from pydantic import BaseModel, EmailStr
from typing import Optional
class User(BaseModel):
age: int
email: EmailStr
is_active: bool = True
nickname: Optional[str] = None
# Pydantic automatically validates and converts data
user_data = {
"age": "25", # String gets converted to int
"email": "john@example.com",
"is_active": "true" # String gets converted to bool
}
user = User(**user_data)
print(user.age) # 25 (as integer)
print(user.model_dump()) # Clean dictionary output
Pydantics Eigenschaften
Der Ansatz von Pydantic bietet dir mehrere Vorteile. Der erste Vorteil, den du bemerken wirst, ist die Leistung - die zentrale Validierungslogik von Pydantic ist in Rust geschrieben, was sie in den meisten Fällen schneller macht als eine handgeschriebene Python-Validierung. Deine Anwendung kann Tausende von Anfragen verarbeiten, ohne dass die Validierung zu einem Engpass wird.
Pydantic ermöglicht auch die Integration mit modernen Frameworks. FastAPI, eines der am schnellsten wachsenden Web-Frameworks in Python, nutzt Pydantic-Modelle, um automatisch API-Dokumentation zu erstellen, Anfragekörper zu validieren und Antworten zu serialisieren. Wenn du ein Pydantic-Modell definierst, bekommst du die Erstellung eines OpenAPI-Schemas kostenlos:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class UserCreate(BaseModel):
name: str
email: EmailStr
age: int
@app.post("/users/")
async def create_user(user: UserCreate):
# FastAPI automatically validates the request body
# and generates API docs from your Pydantic model
return {"message": f"Created user {user.name}"}
Die Erstellung des JSON-Schemas erfolgt automatisch mit jedem Pydantic-Modell. Das bedeutet, dass deine Datenstrukturen selbstdokumentierend werden und du Client-Bibliotheken, Validierungsregeln für Frontend-Anwendungen oder Datenbankschemata aus derselben Quelle der Wahrheit erstellen kannst.
Pydantic vs. Datenbrillen vs. Marshmallow
Aber wann solltest du Pydantic den Alternativen vorziehen? Wenn du Pydantic mit Datenklassen vergleichst, hängt die Entscheidung von den Validierungsanforderungen ab. Pythons @dataclass
eignet sich perfekt für einfache Datencontainer, bei denen du den Eingaben vertraust, aber Pydantic ist besonders gut, wenn du Validierung, Serialisierung und Integration mit Web-Frameworks brauchst:
from dataclasses import dataclass
from pydantic import BaseModel
# Dataclass: fast, simple, no validation
@dataclass
class UserDataclass:
name: str
age: int
# Pydantic: validation, serialization, framework integration
class UserPydantic(BaseModel):
name: str
age: int
Wenn du Pydantic mit Marshmallow vergleichst, fühlt sich der Typ-Hinweis-Ansatz von Pydantic für Python-Entwickler natürlicher an, während Marshmallow Schemaklassen verwendet. Pydantic bietet außerdem eine bessere Leistung und eine einfachere Integration mit modernen asynchronen Frameworks.
Pydantic eignet sich am besten, wenn du APIs erstellst, externe Daten verarbeitest, Konfigurationen verwaltest oder in anderen Szenarien, in denen Fehler bei der Datenvalidierung frühzeitig erkannt werden sollen, anstatt später mysteriöse Bugs zu verursachen. Es wandelt Laufzeitfehler in klare, umsetzbare Validierungsmeldungen um, die sowohl Entwicklern als auch Nutzern helfen zu verstehen, was falsch gelaufen ist.
Erste Schritte mit Pydantic
Im Folgenden erfährst du, wie du Pydantic in deinen Projekten einsetzen kannst, von der Installation bis zur Erstellung deiner ersten Datenmodelle.
Installation und Einrichtung
Die Einrichtung von Pydantic ist einfach, aber es gibt ein paar typische Fallstricke, über die Neulinge stolpern können. Ich führe dich durch den korrekten Installationsprozess und helfe dir, die Fehler zu vermeiden, die unnötige Fehlersuche verursachen.
Als erstes erstellst du eine virtuelle Umgebung, um deine Projektabhängigkeiten zu isolieren. Das verhindert Versionskonflikte, die später zu mysteriösen Importfehlern führen können:
# Create and activate virtual environment (works in zsh and bash)
python -m venv pydantic_env
source pydantic_env/bin/activate # On macOS/Linux
# pydantic_env\Scripts\activate # On Windows
Installiere nun Pydantic mit dem folgenden Befehl:
pip install pydantic
Für die E-Mail-Validierungsfunktion, die du wahrscheinlich in realen Anwendungen (und in diesem Lernprogramm) brauchst, installiere die E-Mail-Extras:
pip install "pydantic[email]"
Die Anführungszeichen um pydantic[email]
sind in fast allen Terminals wichtig. Ohne sie kann es sein, dass du die Fehlermeldung "keine Treffer gefunden" erhältst.
Hinweis: Wenn du auf ModuleNotFoundError: No module named 'pydantic'
stößt, überprüfe diese Punkte:
- Falsche Python-Umgebung: Stelle sicher, dass deine virtuelle Umgebung aktiviert ist
- Mehrere Python-Versionen: Überprüfe, ob du dieselbe Python-Version verwendest, mit der du Pydantic installiert hast
- IDE-Konfiguration: Dein Code-Editor verwendet möglicherweise einen anderen Python-Interpreter
Kritische Benennungswarnung: Benenne deine Python-Datei niemals pydantic.py
. Dadurch entsteht ein zirkulärer Import, der deinen Code mit verwirrenden Fehlermeldungen kaputt macht. Python wird versuchen, deine Datei anstelle der eigentlichen Pydantic-Bibliothek zu importieren.
Dein erstes Pydantic-Modell
Lass uns ein einfaches Benutzermodell erstellen, um Pydantic in Aktion zu sehen. Anstatt mit abstrakten Beispielen zu beginnen, werden wir ein echtes Problem lösen: die Validierung von Benutzerregistrierungsdaten aus einem Webformular.
from pydantic import BaseModel, EmailStr
from typing import Optional
from datetime import datetime
class User(BaseModel):
name: str
email: EmailStr
age: int
is_active: bool = True
created_at: datetime = None
# Test with clean data
clean_data = {
"name": "Alice Johnson",
"email": "alice@example.com",
"age": 28
}
user = User(**clean_data)
print(f"User created: {user.name}, Age: {user.age}")
print(f"Model output: {user.model_dump()}")
Ausgabe:
User created: Alice Johnson, Age: 28
Model output: {'name': 'Alice Johnson', 'email': 'alice@example.com', 'age': 28, 'is_active': True, 'created_at': None}
Schauen wir uns an, was in dieser Modelldefinition passiert:
- Ein pydantisches Modell erstellen: Deine Klasse
User
erbt vonBaseModel
, wodurch sie alle Validierungs- und Serialisierungsfunktionen von Pydantic erhält. Diese Vererbung verwandelt eine normale Python-Klasse in ein Datenvalidierungswerkzeug. - Felddefinitionen: Jede Zeile in der Klasse definiert ein Feld mit seinem erwarteten Typ. Die
name: str
Syntax sagt Pydantic, dassname
eine Zeichenkette sein sollte,age: int
bedeutet, dass das Alter eine ganze Zahl sein sollte, und so weiter. - EmailStr erklärt:
EmailStr
ist ein spezieller Pydantic-Typ, der E-Mail-Adressen automatisch validiert. Sie stammt aus dem Paketpydantic[email]
, das du zuvor installiert hast, und verwendet reguläre Ausdrücke, um sicherzustellen, dass das E-Mail-Format gültig ist. Wenn jemand "not-an-email" angibt, wird Pydantic einen Validierungsfehler auslösen. - Standardwerte: Felder wie
is_active: bool = True
haben Standardwerte. Wenn du diese Felder bei der Erstellung eines Benutzers nicht angibst, verwendet Pydantic die Standardwerte. Die= None
fürcreated_at
macht dieses Feld optional. - Modellinstanziierung: Wenn du
User(**clean_data)
aufrufst, packt**
dein Wörterbuch aus und übergibt jedes Schlüssel-Wert-Paar als Schlüsselwortargumente an den Modellkonstruktor.
Jetzt wollen wir die automatische Typumwandlung von Pydantic in Aktion sehen:
# Messy data that still works
messy_data = {
"name": "Bob Smith",
"email": "bob@company.com",
"age": "35", # String instead of int
"is_active": "true" # String instead of bool
}
user = User(**messy_data)
print(f"Age type: {type(user.age)}") # <class 'int'>
print(f"Is active type: {type(user.is_active)}") # <class 'bool'>
Jetzt wollen wir die automatische Typumwandlung von Pydantic in Aktion sehen:
# Messy data that still works
messy_data = {
"name": "Bob Smith",
"email": "bob@company.com",
"age": "35", # String instead of int
"is_active": "true" # String instead of bool
}
user = User(**messy_data)
print(f"Age type: {type(user.age)}") # <class 'int'>
print(f"Is active type: {type(user.is_active)}") # <class 'bool'>
Ausgabe:
Age type: <class 'int'>
Is active type: <class 'bool'>
Wie du siehst, werden die Felder age
und is_inactive
durch die Validierung automatisch in ihr richtiges Format umgewandelt.
Wenn die Validierung fehlschlägt, gibt Pydantic klare Fehlermeldungen aus:
from pydantic import ValidationError
try:
invalid_user = User(
name="", # Empty string
email="not-an-email", # Invalid email
age=-5 # Negative age
)
except ValidationError as e:
print(e)
Ausgabe:
# Shows exactly which fields failed and why
1 validation error for User
email
value is not a valid email address: An email address must have an @-sign. [type=value_error, input_value='not-an-email', input_type=str]
BaseModel vs. Datenklassen
Wenn du weißt, wann du die BaseModel
von Pydantic und wann du die @dataclass von Python verwenden solltest, kannst du das richtige Werkzeug für jede Situation wählen.
Python-Datenklassen sind ideal für einfache Datencontainer, bei denen du die Eingabe kontrollierst:
from dataclasses import dataclass
@dataclass
class ProductDataclass:
name: str
price: float
in_stock: bool
# Fast, simple, but no validation
product = ProductDataclass("Laptop", 999.99, True)
# This also works, even though types are wrong:
broken_product = ProductDataclass(123, "expensive", "maybe")
Pydantische Modelle ermöglichen Validierung, Serialisierung und Framework-Integration:
from pydantic import BaseModel, Field
class ProductPydantic(BaseModel):
name: str = Field(min_length=1)
price: float = Field(gt=0) # Must be greater than 0
in_stock: bool
# Automatic validation prevents bad data
try:
product = ProductPydantic(name="", price=-10, in_stock="maybe")
except ValidationError as e:
print("Validation caught the errors!")
# Valid data works perfectly
good_product = ProductPydantic(
name="Laptop",
price="999.99", # String converted to float
in_stock=True
)
Wann sollte man welchen Ansatz wählen?
- Verwenden Sie Datenklassen für interne Datenstrukturen, Konfigurationsobjekte, oder wenn die Leistung entscheidend ist und du deinen Datenquellen vertraust
- Verwenden Sie Pydantic für API-Endpunkte, Benutzereingaben, das Parsen externer Daten oder wenn du JSON-Serialisierung brauchst
Pydantic verursacht im Vergleich zu Datenklassen einen gewissen Mehraufwand, aber diese Kosten sind in der Regel vernachlässigbar im Vergleich zu den Fehlern, die es verhindert, und der Entwicklungszeit, die es spart. Für Webanwendungen macht die automatische Integration mit Frameworks wie FastAPI Pydantic zur klaren Wahl.
Die Validierungs- und Serialisierungsfunktionen werden immer wertvoller, je größer deine Anwendung wird. Wenn du mit den Modellen von Pydantic beginnst, hast du eine solide Grundlage, die sich an deine Bedürfnisse anpassen lässt.
Datenmodelle mit Pydantic erstellen
Jetzt, wo du die Grundlagen kennst, können wir die Herausforderungen angehen, denen du bei der Erstellung produktionsreifer Anwendungen begegnen wirst.
Feldvalidierung und Beschränkungen
Betrachte eine Produktkatalog-API, bei der die Preisdaten von mehreren Anbietern mit unterschiedlichen Formatierungsstandards stammen. Manche senden Preise als Strings, andere als Floats, und gelegentlich sendet jemand einen negativen Preis, der dein Abrechnungssystem zum Absturz bringt.
Die Funktion Field()
von Pydantic verwandelt einfache Typ-Hinweise in ausgefeilte Validierungsregeln, die deine Anwendung schützen:
from pydantic import BaseModel, Field
from decimal import Decimal
from typing import Optional
class Product(BaseModel):
name: str = Field(min_length=1, max_length=100)
price: Decimal = Field(gt=0, le=10000) # Greater than 0, less than or equal to 10,000
description: Optional[str] = Field(None, max_length=500)
category: str = Field(..., pattern=r'^[A-Za-z\s]+$') # Only letters and spaces
stock_quantity: int = Field(ge=0) # Greater than or equal to 0
is_available: bool = True
# This works - all constraints satisfied
valid_product = Product(
name="Wireless Headphones",
price="199.99", # String converted to Decimal
description="High-quality wireless headphones",
category="Electronics",
stock_quantity=50
)
# This fails with clear error messages
try:
invalid_product = Product(
name="", # Too short
price=-50, # Negative price
category="Electronics123", # Contains numbers
stock_quantity=-5 # Negative stock
)
except ValidationError as e:
print(f"Validation errors: {len(e.errors())} issues found")
Jeder Field()
Parameter dient einem bestimmten Zweck: min_length
und max_length
verhindern Verletzungen des Datenbankschemas, gt
und le
schaffen Grenzen für die Geschäftslogik und pattern
validiert formatierte Daten mithilfe regulärer Ausdrücke. Die Field(...)
Syntax mit Ellipsen markiert die Pflichtfelder, während Field(None, ...)
optionale Felder mit Validierungsregeln erstellt.
Typenzwang vs. strenge Validierung
Standardmäßig wandelt Pydantic kompatible Typen um, anstatt sie komplett abzulehnen. Diese Flexibilität eignet sich gut für Benutzereingaben, aber es gibt auch Szenarien, die eine exakte Typenzuordnung erfordern:
from pydantic import BaseModel, Field, ValidationError
# Default: lenient type coercion
class FlexibleOrder(BaseModel):
order_id: int
total_amount: float
is_paid: bool
# These all work due to automatic conversion
flexible_order = FlexibleOrder(
order_id="12345", # String to int
total_amount="99.99", # String to float
is_paid="true" # String to bool
)
# Strict validation when precision matters
class StrictOrder(BaseModel):
model_config = {"str_strip_whitespace": True, "validate_assignment": True}
order_id: int = Field(strict=True)
total_amount: float = Field(strict=True)
is_paid: bool = Field(strict=True)
Das model_config
Wörterbuch steuert das Validierungsverhalten in deinem gesamten Modell. Die Option str_strip_whitespace
bereinigt String-Eingaben automatisch, während validate_assignment
sicherstellt, dass Feldänderungen nach der Modellerstellung weiterhin eine Validierung auslösen. Einzelne Felder können diese Einstellungen mit Field(strict=True)
außer Kraft setzen, wenn eine exakte Typenzuordnung erforderlich ist, z. B. bei finanziellen Berechnungen oder wissenschaftlichen Daten.
Verschachtelte Modelle und komplexe Daten
Echte Anwendungen arbeiten mit komplexen, vernetzten Datenstrukturen. Eine E-Commerce-Bestellung enthält Kundeninformationen, Lieferadressen und mehrere Produktartikel, die jeweils eine eigene Validierung erfordern:
from typing import List
from datetime import datetime
class Address(BaseModel):
street: str = Field(min_length=5)
city: str = Field(min_length=2)
postal_code: str = Field(pattern=r'^\d{5}(-\d{4})?$')
country: str = "USA"
class Customer(BaseModel):
name: str = Field(min_length=1)
email: EmailStr
shipping_address: Address
billing_address: Optional[Address] = None
class OrderItem(BaseModel):
product_id: int = Field(gt=0)
quantity: int = Field(gt=0, le=100)
unit_price: Decimal = Field(gt=0)
class Order(BaseModel):
order_id: str = Field(pattern=r'^ORD-\d{6}$')
customer: Customer
items: List[OrderItem] = Field(min_items=1)
order_date: datetime = Field(default_factory=datetime.now)
# Complex nested data validation
order_data = {
"order_id": "ORD-123456",
"customer": {
"name": "John Doe",
"email": "john@example.com",
"shipping_address": {
"street": "123 Main Street",
"city": "Anytown",
"postal_code": "12345"
}
},
"items": [
{"product_id": 1, "quantity": 2, "unit_price": "29.99"},
{"product_id": 2, "quantity": 1, "unit_price": "149.99"}
]
}
order = Order(**order_data)
print(f"Order validated with {len(order.items)} items")
Pydantic validiert verschachtelte Strukturen rekursiv - das Feld Customer
wird zu einem vollständigen Customer
Objekt, das sein eigenes Feld Address
validiert. Die List[OrderItem]
Syntax validiert jedes Listenelement als OrderItem
, während Field(min_items=1)
verhindert, dass leere Bestellungen dein Bestandssystem erreichen. Mit default_factory=datetime.now
werden eindeutige Zeitstempel für jede Bestellinstanz erstellt.
Optionale Felder und Keine Behandlung
Verschiedene Vorgänge benötigen unterschiedliche Datenanforderungen. Bei der Erstellung eines Benutzers müssen die Informationen vollständig sein, während bei der Aktualisierung auch Teiländerungen akzeptiert werden sollten:
from typing import Optional
class UserCreate(BaseModel):
name: str = Field(min_length=1)
email: EmailStr
age: int = Field(ge=13, le=120)
phone: Optional[str] = Field(None, pattern=r'^\+?1?\d{9,15}$')
class UserUpdate(BaseModel):
name: Optional[str] = Field(None, min_length=1)
email: Optional[EmailStr] = None
age: Optional[int] = Field(None, ge=13, le=120)
phone: Optional[str] = Field(None, pattern=r'^\+?1?\d{9,15}$')
# PATCH request with partial data
update_data = {"name": "Jane Smith", "age": 30}
user_update = UserUpdate(**update_data)
# Serialize only provided fields
patch_data = user_update.model_dump(exclude_none=True)
print(f"Fields to update: {list(patch_data.keys())}")
Die Serialisierung wandelt Pydantic-Objekte für die Speicherung oder Übertragung wieder in Wörterbücher oder JSON-Strings um. Die Methode model_dump()
kümmert sich um diese Umwandlung, wobei exclude_none=True
nicht bereitgestellte Felder entfernt. Dieses Muster eignet sich perfekt für PATCH-Anfragen, bei denen die Kunden nur die Felder senden, die sie ändern wollen, und verhindert so ein versehentliches Überschreiben der Daten in deiner Datenbank.
Diese Grundlage bereitet dich auf die nächste Herausforderung vor: die Implementierung einer benutzerdefinierten Validierungslogik, die die einzigartigen Geschäftsregeln deiner Anwendung erfasst.
Benutzerdefinierte Validierung und Integration in die reale Welt
Bei der Entwicklung von Produktionsanwendungen müssen Daten verarbeitet werden, die nicht in die Standard-Typenprüfungsmuster passen.
Denken Sie an ein Benutzerregistrierungsformular, bei dem die Passwortanforderungen je nach Abonnementplan variieren, oder an eine API, die Adressdaten aus mehreren Ländern mit unterschiedlichen Postleitzahlenformaten empfängt. Diese Szenarien erfordern eine benutzerdefinierte Validierungslogik, die deine spezifischen Geschäftsregeln erfasst und sich reibungslos in Web-Frameworks und Konfigurationssysteme integrieren lässt.
Dieser Abschnitt zeigt dir, wie du praktische benutzerdefinierte Validierungsmuster implementierst, Pydantic-Modelle mit FastAPI für die automatische API-Dokumentation integrierst und Anwendungseinstellungen über Umgebungsvariablen verwaltest, indem du den .env
Datei-Ansatz verwendest, auf den sich die meisten Produktionsanwendungen verlassen.
Feldvalidierer und Modellvalidierung
Wenn die Geschäftslogik die Gültigkeit der Daten bestimmt, verwandelt der @field_validator
Dekorator von Pydantic deine Validierungsfunktionen in einen Teil des Modells selbst. Stell dir ein System zur Benutzerregistrierung vor, bei dem die verschiedenen Abonnementstufen unterschiedliche Passwortanforderungen haben:
from pydantic import BaseModel, field_validator, Field
import re
class UserRegistration(BaseModel):
username: str = Field(min_length=3)
email: EmailStr
password: str
subscription_tier: str = Field(pattern=r'^(free|pro|enterprise)$')
@field_validator('password')
@classmethod
def validate_password_complexity(cls, password, info):
tier = info.data.get('subscription_tier', 'free')
if len(password) < 8:
raise ValueError('Password must be at least 8 characters')
if tier == 'enterprise' and not re.search(r'[A-Z]', password):
raise ValueError('Enterprise accounts require uppercase letters')
return password
Mit dem @field_validator
Dekorator kannst du über den Parameter info.data
auf andere Feldwerte zugreifen und so Validierungsregeln erstellen, die von mehreren Feldern abhängen. Der Validator wird ausgeführt, nachdem die grundlegende Typüberprüfung bestanden wurde, sodass du davon ausgehen kannst, dass subscription_tier
einer der zulässigen Werte ist.
Bei Überprüfungen, die sich über mehrere Felder erstrecken, wird der @model_validator
Dekorator ausgeführt, nachdem alle einzelnen Felder überprüft wurden:
from datetime import datetime
from pydantic import model_validator
class EventRegistration(BaseModel):
start_date: datetime
end_date: datetime
max_attendees: int = Field(gt=0)
current_attendees: int = Field(ge=0)
@model_validator(mode='after')
def validate_event_constraints(self):
if self.end_date <= self.start_date:
raise ValueError('Event end date must be after start date')
if self.current_attendees > self.max_attendees:
raise ValueError('Current attendees cannot exceed maximum')
return self
Der Parameter mode='after'
stellt sicher, dass der Validator eine vollständig konstruierte Modellinstanz erhält, was ihn perfekt für Geschäftslogik macht, die Zugriff auf alle validierten Felder benötigt. Der Validator muss self
zurückgeben, um die erfolgreiche Validierung anzuzeigen.
FastAPI-Integration
Die Integration von FastAPI in Pydantic ermöglicht eine automatische Validierung von Anfragen und eine API-Dokumentation. Das wichtigste Muster besteht darin, dass du für verschiedene Vorgänge separate Modelle erstellst, damit du die Kontrolle darüber hast, welche Daten in welche Richtung fließen:
from fastapi import FastAPI
from typing import Optional
from datetime import datetime
app = FastAPI()
class UserCreate(BaseModel):
username: str = Field(min_length=3)
email: EmailStr
password: str = Field(min_length=8)
class UserResponse(BaseModel):
id: int
username: str
email: EmailStr
created_at: datetime
@app.post("/users/", response_model=UserResponse)
async def create_user(user: UserCreate):
# FastAPI automatically validates the request body
new_user = {
"id": 1,
"username": user.username,
"email": user.email,
"created_at": datetime.now()
}
return UserResponse(**new_user)
Die Trennung zwischen Input- und Output-Modellen bietet mehrere Vorteile. Eingabemodelle können Validierungsregeln und Pflichtfelder enthalten, während Ausgabemodelle genau steuern, welche Daten an die Kunden gesendet werden. FastAPI generiert automatisch OpenAPI-Dokumentation aus deinen Pydantic-Modellen und erstellt interaktive API-Dokumente, die Entwickler zum Testen von Endpunkten verwenden können.
Für Aktualisierungsvorgänge kannst du Modelle erstellen, bei denen alle Felder optional sind:
class UserUpdate(BaseModel):
username: Optional[str] = Field(None, min_length=3)
email: Optional[EmailStr] = None
@app.patch("/users/{user_id}")
async def update_user(user_id: int, user_update: UserUpdate):
# Only update provided fields
update_data = user_update.model_dump(exclude_unset=True)
# Your database update logic here
return {"message": f"Updated user {user_id}"}
Der Parameter exclude_unset=True
in PATCH-Vorgängen stellt sicher, dass du nur Felder aktualisierst, die explizit angegeben wurden, um versehentliches Überschreiben zu verhindern. Dieses Muster eignet sich perfekt für REST-APIs, bei denen Kunden Teilaktualisierungen senden.
Konfigurationsmanagement mit Umgebungsvariablen
Produktionsanwendungen brauchen ein sicheres, einsatzfreundliches Konfigurationsmanagement. Pydantic's BaseSettings
kombiniert mit .env
Dateien bietet eine typsichere Konfiguration, die in Entwicklungs-, Staging- und Produktionsumgebungen funktioniert.
Erstelle zunächst eine .env
Datei im Stammverzeichnis deines Projekts:
# .env file
DATABASE_URL=postgresql://user:password@localhost:5432/myapp
SECRET_KEY=your-secret-key-here
DEBUG=false
ALLOWED_HOSTS=localhost,127.0.0.1,yourdomain.com
Dann definiere dein Einstellungsmodell:
from pydantic import BaseSettings, Field
from typing import List
class AppSettings(BaseSettings):
database_url: str = Field(description="Database connection URL")
secret_key: str = Field(description="Secret key for JWT tokens")
debug: bool = Field(default=False)
allowed_hosts: List[str] = Field(default=["localhost"])
class Config:
env_file = ".env"
case_sensitive = False
# Load settings automatically from environment and .env file
settings = AppSettings()
Die Klasse BaseSettings
liest automatisch aus Umgebungsvariablen, .env
Dateien und Kommandozeilenargumenten. Umgebungsvariablen haben Vorrang vor den Werten der .env
Datei, so dass es einfach ist, die Einstellungen in verschiedenen Einsatzumgebungen außer Kraft zu setzen. Die Einstellung case_sensitive = False
ermöglicht eine flexible Benennung von Umgebungsvariablen.
Für komplexe Anwendungen kannst du die Einstellungen in logischen Gruppen organisieren:
class DatabaseSettings(BaseSettings):
url: str = Field(env="DATABASE_URL")
max_connections: int = Field(default=5, env="DB_MAX_CONNECTIONS")
class AppSettings(BaseSettings):
secret_key: str
debug: bool = False
database: DatabaseSettings = DatabaseSettings()
class Config:
env_file = ".env"
settings = AppSettings()
# Access nested configuration
db_url = settings.database.url
Dieser verschachtelte Ansatz hält zusammengehörige Einstellungen zusammen und sorgt gleichzeitig für eine klare Trennung zwischen den verschiedenen Komponenten deiner Anwendung. Jede Einstellungsgruppe kann ihr eigenes Umgebungsvariablen-Präfix und eigene Validierungsregeln haben.
Der .env
Datei-Ansatz funktioniert mit Bereitstellungsplattformen wie Heroku, AWS und Docker, wo Umgebungsvariablen die Standardmethode zur Konfiguration von Anwendungen sind. Deine Anwendung erhält Typensicherheit und -validierung und folgt dabei Cloud-nativen Konfigurationsmustern, die Betriebsteams erwarten.
Diese Muster bilden die Grundlage für den Aufbau wartbarer Anwendungen, die die Komplexität der realen Welt bewältigen. Das Validierungssystem von Pydantic passt sich an deine spezifischen Anforderungen an und liefert klare Fehlermeldungen und eine automatische Dokumentation, die deinem gesamten Team hilft, die Datenstrukturen zu verstehen, die deine Anwendung erwartet.
Fazit
Du hast jetzt gesehen, wie Pydantic dir die mühsame Arbeit ersparen kann, den Validierungscode von Hand zu schreiben. Anstatt deine Funktionen mit isinstance()
Überprüfungen und benutzerdefinierter Fehlerbehandlung zu überfrachten, kannst du deine Datenstruktur einmal definieren und Pydantic den Rest erledigen lassen.
Lies unbedingt den Abschnitt "Häufig gestellte Fragen" weiter unten, wenn du weitere spezifische Fragen zu Pydantic hast. Für eine umfassende Dokumentation und fortgeschrittene Muster findest du hier ein paar Ressourcen, die du nutzen kannst:
- Offizielle Dokumentation:
- Pydantic Dokumentation- Vollständige Referenz mit Beispielen und Best Practices
- Pydantic Migration Guide- Unverzichtbare Lektüre für Upgrades zwischen Versionen
- FastAPI-Dokumentation- Erfahre, wie Pydantic die automatische API-Dokumentation unterstützt
- Verwandte DataCamp Lernressourcen:
- Modellvalidierung in Python- Tiefe Einblicke in Validierungstechniken mit Scikit-Learn
- Einführung in FastAPI- Produktions-APIs mit Pydantic-Integration erstellen
- Einführung in APIs in Python- Arbeit mit externen APIs und JSON-Datenvalidierung
- Python für Fortgeschrittene- Beherrsche die Python-Grundlagen, die den Gebrauch von Pydantic ergänzen
- Objektorientiertes Programmieren in Python- Schaffe die Grundlage für das Verständnis pydantischer Modelle
FAQs
Verlangsamt die Pydantic-Validierung meine Anwendung?
Pydantic V2 ist mit Rust unter der Haube gebaut, was es sehr schnell macht. Bei den meisten Anwendungen ist der Aufwand für die Validierung minimal im Vergleich zu den Fehlern, die dadurch verhindert werden. Du kannst model_construct()
verwenden, um die Validierung für vertrauenswürdige Datenquellen zu überspringen, wenn die Leistung entscheidend ist.
Wann sollte ich Pydantic anstelle von Python-Datenklassen verwenden?
Nutze Pydantic, wenn du Validierungen brauchst, mit externen Datenquellen arbeitest oder APIs erstellst. Verwende Dataklassen für einfache interne Datenstrukturen, bei denen du der Eingabe vertraust. Pydantic eignet sich besonders gut für Daten, die von Benutzern, APIs oder Konfigurationsdateien stammen.
Wie gehe ich mit Pydantic-Validierungsfehlern in Produktionsanwendungen um?
Fange ValidationError
Ausnahmen ab und extrahiere die .errors()
Liste, um strukturierte Fehlerinformationen zu erhalten. Jeder Fehler enthält die Felder loc
, msg
und type
, mit denen du benutzerfreundliche Fehlermeldungen erstellen oder detaillierte Debugging-Informationen protokollieren kannst.
Kann ich Pydantic zu bestehendem Code hinzufügen, ohne alles kaputt zu machen?
Ja! Beginne damit, externe Dateneingabepunkte (API-Anfragen, Dateiparsing) mit Pydantic-Modellen zu verpacken. Du kannst interne Datenstrukturen schrittweise umwandeln. Pydantic funktioniert neben bestehendem Code und erfordert nicht, dass du deine gesamte Codebasis auf einmal ändern musst.
Was ist der häufigste Fehler beim Start mit Pydantic?
Kein Verständnis für den Typenzwang. Pydantic wandelt kompatible Typen (wie die Zeichenkette "123" in die ganze Zahl 123) standardmäßig um. Wenn du eine strenge Typüberprüfung benötigst, verwende Field(strict=True)
oder setze den strengen Modus in deiner Modellkonfiguration, um automatische Konvertierungen zu verhindern.

Ich bin ein Data Science Content Creator mit über 2 Jahren Erfahrung und einem der größten Follower auf Medium. Ich schreibe gerne ausführliche Artikel über KI und ML mit einem etwas sarkastischen Stil, denn man muss etwas tun, damit sie nicht so langweilig sind. Ich habe mehr als 130 Artikel verfasst und einen DataCamp-Kurs gemacht, ein weiterer ist in Vorbereitung. Meine Inhalte wurden von über 5 Millionen Augenpaaren gesehen, von denen 20.000 zu Followern auf Medium und LinkedIn wurden.