⏱️4 min read · 799 words
يعد Pydantic v2 أهم ترقية لمكتبة Python في عام 2026. تمت إعادة كتابته في Rust (عبر pydantic-core)، وهو أسرع بمقدار 5-50 مرة من الإصدار 1 مع واجهة برمجة تطبيقات أكثر نظافة ووضعًا صارمًا وأدوات التحقق من صحة النماذج. يغطي هذا الدليل كل ميزات Pydantic v2 مع أمثلة عملية.
📋 Table of Contents
التثبيت والهجرة
pip install pydantic>=2.0
# Auto-migrate from v1
pip install bump-pydantic
bump-pydantic .
# Key changes from v1:
# - class Config -> model_config = ConfigDict(...)
# - @validator -> @field_validator + @model_validator
# - .dict() -> .model_dump()
# - .json() -> .model_dump_json()
# - __fields__ -> model_fields
النماذج الأساسية
from pydantic import BaseModel, Field, ConfigDict, EmailStr
from datetime import datetime
from typing import Optional
class User(BaseModel):
model_config = ConfigDict(
str_strip_whitespace=True, # strip whitespace from strings
str_min_length=1, # no empty strings
validate_assignment=True, # validate on attribute assignment
populate_by_name=True, # allow field name OR alias
use_enum_values=True, # store enum.value not enum
)
id: int
name: str = Field(min_length=2, max_length=50)
email: EmailStr
age: Optional[int] = Field(default=None, ge=13, le=120)
role: str = Field(default="user", pattern=r"^(user|admin|moderator)$")
created_at: datetime = Field(default_factory=datetime.utcnow)
tags: list[str] = Field(default_factory=list, max_length=10)
# Create
user = User(id=1, name="Alice Chen", email="alice@example.com", age=30)
print(user.model_dump())
print(user.model_dump_json(indent=2))
# Partial update
updated = user.model_copy(update={"name": "Alice Smith"})
# From dict
user2 = User.model_validate({"id": 2, "name": "Bob", "email": "bob@example.com"})
# From JSON string
user3 = User.model_validate_json('{"id": 3, "name": "Carol", "email": "carol@example.com"}')
المدققون
from pydantic import BaseModel, field_validator, model_validator, Field
import re
class PasswordChange(BaseModel):
current_password: str
new_password: str = Field(min_length=8)
confirm_password: str
@field_validator("new_password")
@classmethod
def password_strength(cls, v: str) -> str:
if not re.search(r"[A-Z]", v):
raise ValueError("Must contain uppercase letter")
if not re.search(r"[0-9]", v):
raise ValueError("Must contain digit")
if not re.search(r"[!@#$%^&*]", v):
raise ValueError("Must contain special character")
return v
@model_validator(mode="after") # runs after all field validators
def passwords_match(self) -> "PasswordChange":
if self.new_password != self.confirm_password:
raise ValueError("Passwords do not match")
if self.new_password == self.current_password:
raise ValueError("New password must differ from current")
return self
# Validate and get all errors at once
from pydantic import ValidationError
try:
PasswordChange(
current_password="old123",
new_password="short",
confirm_password="different"
)
except ValidationError as e:
print(e.error_count(), "errors")
for error in e.errors():
print(f" {error['loc']}: {error['msg']}")
الوضع الصارم
from pydantic import BaseModel, ConfigDict
class StrictUser(BaseModel):
model_config = ConfigDict(strict=True) # no coercion!
id: int
name: str
active: bool
# Without strict: "1" coerced to int, "true" to bool
# With strict: exact types required
try:
StrictUser(id="1", name="Alice", active="true") # raises!
except Exception as e:
print(e)
# Strict per-field
from pydantic import Strict
from typing import Annotated
class PartiallyStrict(BaseModel):
id: Annotated[int, Strict()] # strict for this field only
name: str # coercion allowed
الأسماء المستعارة والتسلسل
from pydantic import BaseModel, Field, AliasPath
from pydantic.functional_serializers import model_serializer
class APIResponse(BaseModel):
# Accept camelCase from API, use snake_case internally
model_config = ConfigDict(populate_by_name=True)
user_id: int = Field(alias="userId")
first_name: str = Field(alias="firstName")
last_name: str = Field(alias="lastName")
created_at: str = Field(alias="createdAt")
# Nested alias path
city: str = Field(validation_alias=AliasPath("address", "city"))
# Parse camelCase JSON
data = {
"userId": 1,
"firstName": "Alice",
"lastName": "Chen",
"createdAt": "2026-01-15",
"address": {"city": "Sydney", "country": "AU"}
}
response = APIResponse.model_validate(data)
print(response.user_id, response.first_name, response.city)
# Serialize back to camelCase
print(response.model_dump(by_alias=True)) # {"userId": 1, ...}
Pydantic + FastAPI (نمط 2026)
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field, ConfigDict
from typing import Annotated
from datetime import datetime
app = FastAPI()
class UserCreate(BaseModel):
name: Annotated[str, Field(min_length=2, max_length=50)]
email: Annotated[str, Field(pattern=r"^[^@]+@[^@]+\.[^@]+$")]
password: Annotated[str, Field(min_length=8, exclude=True)] # exclude from response
class UserResponse(BaseModel):
model_config = ConfigDict(from_attributes=True) # allows ORM model input
id: int
name: str
email: str
created_at: datetime
@classmethod
def from_orm_with_extras(cls, obj, **extras) -> "UserResponse":
return cls.model_validate({**obj.__dict__, **extras})
@app.post("/users", response_model=UserResponse, status_code=201)
async def create_user(data: UserCreate) -> UserResponse:
# data.password available but won't appear in response
user = await db.users.create({
"name": data.name,
"email": data.email,
"password_hash": hash_password(data.password)
})
return UserResponse.model_validate(user)
# Automatic OpenAPI docs show the schema
# Pydantic v2 JSON Schema generation is much better than v1
الأداء: الإصدار 1 مقابل الإصدار 2
import timeit
from pydantic import BaseModel
class BenchModel(BaseModel):
id: int
name: str
email: str
score: float
data = {"id": 1, "name": "Alice", "email": "alice@example.com", "score": 9.5}
n = 100_000
t = timeit.timeit(lambda: BenchModel(**data), number=n)
print(f"Pydantic v2: {t:.2f}s for {n:,} validations ({n/t:,.0f}/sec)")
# Typical: ~2-5x faster than v1, up to 50x for complex nested models
Pydantic v2 في عام 2026 هو معيار التحقق من صحة Python. يوفر قلب Rust مكاسب كبيرة في الأداء. تكتشف أدوات التحقق من الصحة الميدانية مشكلات البيانات مبكرًا، وتفرض أدوات التحقق من صحة النماذج قيودًا عبر الحقول، ويزيل الوضع الصارم أخطاء الإكراه الصامتة. قم بالترحيل من الإصدار الأول باستخدام Bump-Pydantic — فهو يتعامل مع 90% من التغييرات تلقائيًا.
🔗 Share this article
✍️ Leave a Comment