404 Not Found

404 Not Found


nginx

هيكل الطلب والتحقق من البيانات — دليل عمقي متعمق لـ Pydantic V2

التحقق من البيانات يشبه أمن المطارات — كل راكب (طلب) يجب أن يمر عبر التحقق من الهوية (فحص الأنواع)، وفحص الأمتعة (التحقق من القيود)، ومراجعة إقراره (المتحققون المخصصون) قبل صعود الطائرة (دخول منطق الأعمال).

1. ما ستتعلمه


2. القصة الحقيقية لـ Alice

(1) نقطة الألم: إرسال بيانات الأسعار والتحقق منها مليئة بالأخطاء

يتلقى PriceTracker الخاص بـ Alice بيانات أسعار مقدمة من الموردين، مما يتطلب أن تكون الأسعار أكبر من 0، وأن تحدد العملات برموز ISO 4217 المكونة من ثلاثة أحرف، وألا تكون أسماء المنتجات فارغة. ومع ذلك، فإن واجهة Bob الأمامية ترسل أحيانًا أسعارًا سالبة أو عملات غير صالحة. كود التحقق المخصص المكتوب في Flask منتشر عبر 10 مواقع مختلفة، ويتجاهل الحالة الحدية حيث "السعر يساوي 0"، مما يؤدي إلى بيانات متسخة بسعر 0 في قاعدة البيانات.

(2) حل للتحقق التعريفي في Pydantic V2

يستبدل Pydantic V2 التحقق الإلزامي بنموذج تعريفي؛ يتم تعريف جميع القيود داخل النموذج، وبمجرد تعريفها، يتم تطبيقها عالميًا.

PYTHON
from pydantic import BaseModel, Field, field_validator

class PriceCreate(BaseModel):
    product_id: int = Field(gt=0)
    price: float = Field(gt=0, description="Price in USD")
    currency: str = Field(pattern=r"^[A-Z]{3}$", examples=["USD", "EUR"])

    @field_validator("currency")
    @classmethod
    def validate_currency(cls, v: str) -> str:
        if v not in {"USD", "EUR", "GBP", "JPY", "CNY"}:
            raise ValueError(f"Unsupported currency: {v}")
        return v

(3) العائد

تم دمج كود التحقق من الأسعار من 10 كتل منطقية منفصلة في تعريف نموذج واحد؛ يتم تصفية القيم 0 والأسعار السالبة تلقائيًا، ويضمن التحقق من العملات نهجًا مزدوج الطبقة يجمع بين التعبيرات النمطية والمتحققين المخصصين، مما يقلل البيانات المتسخة من قاعدة البيانات.


3. تدفق بيانات Pydantic V2

(1) دورة الحياة الكاملة

100%
flowchart TD
    A[JSON Request Body] --> B[model_validate]
    B --> C[Type Coercion]
    C --> D[field_validator]
    D --> E[model_validator]
    E --> F[Valid Model Instance]
    F --> G[model_dump]
    G --> H[JSON Response]
    F --> I[model_dump_json]
    I --> J[JSON String]
المرحلة الطريقة الوصف
تحليل الإدخال model_validate(data) تحليل والتحقق من dict/JSON
تحويل النوع تلقائي "42"42, "9.99"9.99
التحقق من الحقل @field_validator تحقق مخصص لحقل واحد
التحقق من النموذج @model_validator تحقق مشترك بين الحقول
تسلسل المخرجات model_dump() / model_dump_json() تحويل النموذج إلى dict/JSON

(1) ▶ مثال: النموذج الأساسي لـ Pydantic V2

PYTHON
from pydantic import BaseModel, Field

class ProductCreate(BaseModel):
    name: str = Field(min_length=1, max_length=200)
    category: str = Field(max_length=100)
    base_price: float = Field(gt=0, description="Base price in USD")

# تحليل والتحقق
data = {"name": "Widget", "category": "electronics", "base_price": 29.99}
product = ProductCreate.model_validate(data)
print(product.model_dump())

الناتج:

TEXT
{'name': 'Widget', 'category': 'electronics', 'base_price': 29.99}

4. field_validator و model_validator

(1) مقارنة الانتقال من V1 إلى V2

100%
flowchart LR
    V1[Pydantic V1] --> Migrate[V1 → V2 Migration]
    Migrate --> V2[Pydantic V2]
    V1 --- A["@validator('field')"]
    V2 --- B["@field_validator('field')"]
    V1 --- C["@root_validator"]
    V2 --- D["@model_validator(mode='after')"]
    V1 --- E["class Config:"]
    V2 --- F["model_config = ConfigDict(...)"]
    V1 --- G[".dict()"]
    V2 --- H[".model_dump()"]
بنية V1 بنية V2 الوصف
@validator("field") @field_validator("field") محقق الحقل
@root_validator @model_validator(mode="after") التحقق على مستوى النموذج
class Config: orm_mode = True model_config = ConfigDict(from_attributes=True) نموذج ORM
.dict() .model_dump() تسلسل إلى dict
.json() .model_dump_json() تسلسل إلى JSON

(1) ▶ مثال: field_validator — التحقق من حقل واحد

PYTHON
from pydantic import BaseModel, Field, field_validator

class PriceCreate(BaseModel):
    product_id: int = Field(gt=0)
    price: float = Field(gt=0)
    currency: str = Field(default="USD", max_length=3)

    @field_validator("price")
    @classmethod
    def price_precision(cls, v: float) -> float:
        # تقريب إلى منزلتين عشريتين
        return round(v, 2)

    @field_validator("currency")
    @classmethod
    def valid_currency(cls, v: str) -> str:
        allowed = {"USD", "EUR", "GBP", "JPY", "CNY"}
        if v not in allowed:
            raise ValueError(f"Currency must be one of {allowed}")
        return v.upper()

# اختبار التحقق
p = PriceCreate(product_id=1, price=9.999, currency="usd")
print(p.model_dump())

الناتج:

TEXT
{'product_id': 1, 'price': 10.0, 'currency': 'USD'}

(2) ▶ مثال: model_validator للتحقق المشترك بين الحقول

PYTHON
from pydantic import BaseModel, Field, model_validator

class PriceRangeQuery(BaseModel):
    min_price: float = Field(ge=0, description="Min price in USD")
    max_price: float = Field(ge=0, description="Max price in USD")

    @model_validator(mode="after")
    def check_range(self):
        if self.min_price > self.max_price:
            raise ValueError("min_price must be <= max_price")
        return self

# صالح
valid = PriceRangeQuery(min_price=10, max_price=100)
print(valid.model_dump())

# غير صالح - يثير خطأ التحقق
# PriceRangeQuery(min_price=100, max_price=10)

الناتج:

TEXT
{'min_price': 10.0, 'max_price': 100.0}

5. القيود المتقدمة لـ Field() و JSON Schema

(1) مرجع سريع لمعاملات Field()

المعامل النوع الوصف تعيين JSON Schema
gt قيمة أكبر من exclusiveMinimum
ge قيمة أكبر من أو يساوي minimum
lt قيمة أصغر من exclusiveMaximum
le قيمة أصغر من أو يساوي maximum
min_length نص الطول الأدنى minLength
max_length نص الطول الأقصى maxLength
pattern نص تعبير نمطي pattern
default أي القيمة الافتراضية default
examples قائمة قيم أمثلة examples
description نص الوصف description
alias نص اسم بديل للحقل تعيين الاسم البديل

(1) ▶ مثال: قيود Field و JSON Schema

PYTHON
from pydantic import BaseModel, Field

class ProductCreate(BaseModel):
    name: str = Field(
        min_length=1,
        max_length=200,
        description="Product display name",
        examples=["Wireless Mouse", "USB Cable"],
    )
    sku: str = Field(
        pattern=r"^[A-Z]{2}-\d{4,6}$",
        description="SKU code: 2 letters + 4-6 digits",
        examples=["EL-1234", "CB-567890"],
    )
    base_price: float = Field(
        gt=0,
        le=999999.99,
        description="Base price in USD",
        examples=[9.99, 49.99, 199.99],
    )

# عرض JSON Schema المُولّد
print(ProductCreate.model_json_schema())

الناتج:

TEXT
# التنفيذ ناجح

6. النماذج المتداخلة وتوليفات النماذج

(1) بنية النموذج المتداخل

(1) ▶ مثال: النموذج المتداخل لـ PriceTracker

PYTHON
from pydantic import BaseModel, Field
from typing import Optional

class PriceInfo(BaseModel):
    amount: float = Field(gt=0, description="Price amount in USD")
    currency: str = Field(default="USD", pattern=r"^[A-Z]{3}$")
    source: str = Field(max_length=100, description="Price source")

class ProductCreate(BaseModel):
    name: str = Field(min_length=1, max_length=200)
    category: str = Field(max_length=100)
    current_price: PriceInfo  # نموذج متداخل
    original_price: Optional[PriceInfo] = None  # متداخل اختياري

# تحقق متداخل
data = {
    "name": "Wireless Mouse",
    "category": "electronics",
    "current_price": {"amount": 29.99, "currency": "USD", "source": "Amazon"},
    "original_price": {"amount": 49.99, "currency": "USD", "source": "Amazon"},
}
product = ProductCreate.model_validate(data)
print(product.model_dump())

الناتج:

TEXT
# التنفيذ ناجح

(2) ▶ مثال: Union و Literal

PYTHON
from pydantic import BaseModel, Field
from typing import Union, Literal

class SinglePrice(BaseModel):
    type: Literal["single"] = "single"
    amount: float = Field(gt=0)

class RangePrice(BaseModel):
    type: Literal["range"] = "range"
    min_amount: float = Field(gt=0)
    max_amount: float = Field(gt=0)

class ProductPrice(BaseModel):
    product_id: int = Field(gt=0)
    pricing: Union[SinglePrice, RangePrice]  # اتحاد مميّز

# يستخدم FastAPI حقل "type" لتحديد النموذج المراد التحقق منه
data = {"product_id": 1, "pricing": {"type": "range", "min_amount": 10, "max_amount": 50}}
pp = ProductPrice.model_validate(data)
print(pp.model_dump())

الناتج:

TEXT
# التنفيذ ناجح

(2) إعدادات ConfigDict

(3) ▶ مثال: model_config ونمط ORM

PYTHON
from pydantic import BaseModel, ConfigDict

class ProductResponse(BaseModel):
    model_config = ConfigDict(
        from_attributes=True,  # تفعيل وضع ORM (القراءة من كائنات SQLAlchemy)
        populate_by_name=True,  # السماح باسم الحقل والاسم البديل معًا
        json_schema_extra={
            "examples": [{"id": 1, "name": "Widget", "price": 9.99}]
        },
    )

    id: int
    name: str
    price: float

# مع from_attributes=True، يمكن الإنشاء من سمات الكائن
class FakeORMObject:
    def __init__(self):
        self.id = 1
        self.name = "Widget"
        self.price = 9.99

orm_obj = FakeORMObject()
response = ProductResponse.model_validate(orm_obj)
print(response.model_dump())

الناتج:

TEXT
{'id': 1, 'name': 'Widget', 'price': 9.99}

7. مثال شامل

يتيح التحقق من الحقول والتحقق المشترك بين الحقول في Pydantic V2، مقترنًا بنمط ORM وهيكل طلب FastAPI، سير عمل كامل للتحقق من إدخال البيانات.

PYTHON
from fastapi import FastAPI
from pydantic import BaseModel, Field, field_validator, model_validator, ConfigDict

class PriceCreate(BaseModel):
    product_name: str = Field(min_length=1, max_length=100)
    price: float = Field(gt=0, description="The price must be greater than 0")
    currency: str = "USD"

    @field_validator("currency")
    @classmethod
    def validate_currency(cls, v: str) -> str:
        if v not in ("USD", "EUR", "GBP"):
            raise ValueError("currency must be USD/EUR/GBP")
        return v

class PriceRangeQuery(BaseModel):
    min_price: float = Field(ge=0)
    max_price: float = Field(ge=0)

    @model_validator(mode="after")
    def validate_range(self):
        if self.min_price > self.max_price:
            raise ValueError("min_price must <= max_price")
        return self

app = FastAPI()

@app.post("/prices")
async def create_price(data: PriceCreate):
    return data.model_dump()

الناتج:

TEXT
POST /prices {"product_name":"Widget","price":9.99} → {"product_name":"Widget","price":9.99,"currency":"USD"}
POST /prices {"product_name":"","price":-1} → 422 Validation Error

❓ أسئلة شائعة

س كيف أختار بين field_validator و model_validator؟
ج استخدم field_validator للتحقق من حقل واحد (مثل فحص التنسيق)، واستخدم model_validator للتحقق المشترك بين الحقول (مثل min_price <= max_price).
س هل لا يزال يمكن استخدام التعليق التوضيحي @validator من V1؟
ج يحتفظ V2 بطبقة توافق ولكنه يصدر تحذير إهمال. يجب أن تستخدم المشاريع الجديدة @field_validator أو @model_validator، ويجب ترحيل المشاريع الحالية في أقرب وقت ممكن.
س ماذا يفعل from_attributes=True؟
ج يسمح بإنشاء نماذج Pydantic مباشرة من كائنات ORM (مثل مثيلات نموذج SQLAlchemy)، وقراءة سمات الكائن بدلاً من قاموس. هذا إعداد رئيسي لـ FastAPI + SQLAlchemy.
س ما الفرق بين examples و json_schema_extra في Field()؟
ج examples هو قائمة من قيم الأمثلة لحقل واحد، يتم تعيينها إلى أمثلة OpenAPI؛ json_schema_extra هو خاصية مخطط إضافية على مستوى النموذج.
س ما هي المشاكل المتعلقة بالنماذج المتداخلة العميقة جدًا؟
ج النماذج المتداخلة بأكثر من 3 مستويات تزيد من زمن التحقق وتجعل تصحيح الأخطاء أكثر صعوبة. نوصي بتسوية الهيكل أو تفكيكه باستخدام نمط مركب.
س كم هو أداء Pydantic V2 مقارنة بـ V1؟
ج التحقق الأساسي أسرع بـ 5-50 مرة (تطبيق Rust)، والتسلسل أسرع بـ 2-10 مرات. هناك تحسن ملحوظ في السيناريوهات التي تتضمن ملايين نقاط البيانات.

📖ملخص


📝تمارين

  1. مسألة أساسية (صعوبة ⭐): أنشئ نموذج PriceCreate، الذي يتضمن product_id: int (> 0) و price: float (> 0)، وتحقق أن الإدخال غير الصالح يثير خطأ تحقق. تلميح: BaseModel + Field(gt=0)
  2. تمرين متقدم (صعوبة ⭐⭐): أضف إلى PriceCreate محقق @field_validator للتحقق من أن حقل currency يمكن أن يحتوي فقط على USD/EUR/GBP، وأضف @model_validator لضمان min_price <= max_price. تلميح: field_validator + model_validator(mode="after")
  3. مسألة تحدي (صعوبة ⭐⭐⭐): صمم نظام نماذج متداخلة كامل لـ PriceTracker: ProductCreate يحتوي على PriceInfo (amount + currency)؛ العملة في PriceInfo مقيدة بتعبير نمطي؛ و SKU في ProductCreate مقيد بالتنسيق في pattern. تلميح: Field(pattern=...) + BaseModel متداخل

---|

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%