معاملات المسار ومعاملات الاستعلام — تصميم التوجيه الدقيق
تصميم التوجيه كتخطيط طرق المدينة — معاملات المسار كعناوين الشوارع (تحديد دقيق)، ومعاملات الاستعلام كمعايير التصفية (تضييق النطاق)؛ فقط بتعاونهما معاً يمكنك الوصول إلى وجهتك بسرعة.
1. ما ستتعلمه
- معاملات المسار: التحويل التلقائي للأنواع، التحقق، وقيود
Path()(gt/ge/lt/le) - معاملات الاستعلام: اختيارية/مطلوبة، قيمة افتراضية، تحقق متقدم بـ
Query()(alias/description/deprecated) - قواعد ترتيب组合 المعاملات المتعددة والأخطاء الشائعة
- معامل تعداد السلاسل النصية: تطبيقات
Enum— في المسارات والاستعلامات - سيناريو Alice في PriceTracker: البحث عن الأسعار حسب معرّف المنتج؛ التصفية حسب الفئة ونطاق السعر
2. القصة الحقيقية لـ Alice
(1) نقطة الألم: معاملات API استعلام المنتجات مربكة
يحتاج PriceTracker الخاص بـ Alice لدعم طرق استعلام متعددة: استعلامات دقيقة حسب معرّف المنتج، والتصفية حسب الفئة ونطاق السعر، والتصفح المُصفَّى بالترتيب. أرسل Bob من الواجهة الأمامية category=electronics&min_price=10&max_price=999، لكن كود Flask الخاص بـ Alice يحلل كل معامل يدوياً. تحويلات الأنواع عرضة للأخطاء، ولا يوجد تحقق من الأسعار السالبة أو حقول الترتيب غير الصالحة، مما يؤدي إلى حوادث متكررة في الإنتاج.
(2) حلول التحقق من المعاملات في FastAPI
يستخدم FastAPI تلميحات الأنواع لتحليل المعاملات والتحقق منها تلقائياً. يوفر Path() وQuery() قيوداً تصريحية، والمعاملات غير الصالحة تُطلق تلقائياً خطأ 422.
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/products/{product_id}")
async def get_product(
product_id: int = Path(gt=0, description="معرّف المنتج يجب أن يكون موجباً"),
category: str | None = Query(None, max_length=50),
):
return {"product_id": product_id, "category": category}
(3) العائد
انخفض كود التحقق من المعاملات من 30 سطراً إلى 3 أسطر، وأصبحت استجابات خطأ 422 تتضمن الآن تلقائياً تفاصيل محددة حول فشل التحقق، مما يسمح لـ Bob بتحديد أي معامل غير صالح فوراً. كما يعرض توثيق API تلقائياً جميع القيود.
3. شرح مفصّل لمعاملات المسار
(1) معاملات المسار الأساسية
معاملات المسار جزء من مسار URL وتُعرَّف باستخدام بناء {param}؛ يحوّلها FastAPI تلقائياً بناءً على تلميحات الأنواع.
sequenceDiagram
participant Client
participant Router as موجه FastAPI
participant Converter as محوّل الأنواع
participant Validator as مُحقِّق المسار
participant Handler as دالة العرض
Client->>Router: GET /products/42
Router->>Converter: استخراج "42" من المسار
Converter->>Converter: int("42") → 42
Converter->>Validator: product_id=42 (int)
Validator->>Validator: فحص gt=0 → 42 > 0 ✓
Validator->>Handler: get_product(product_id=42)
Handler-->>Client: {"product_id": 42}
| نوع معامل المسار | مثال URL | نوع Python | التحويل التلقائي |
|---|---|---|---|
| عدد صحيح | /products/42 |
int |
"42" → 42 |
| عدد عشري | /prices/9.99 |
float |
"9.99" → 9.99 |
| سلسلة نصية | /categories/electronics |
str |
كما هي |
| مسار | /files/src/main.py |
Path |
سلسلة نصية تحتوي على / |
(1) ▶ مثال: معاملات المسار الأساسية وتحويل الأنواع
from fastapi import FastAPI
app = FastAPI()
@app.get("/products/{product_id}")
async def get_product(product_id: int):
# FastAPI يحوّل تلقائياً "42" إلى int(42)
# إذا أرسل المستخدم /products/abc ← خطأ 422
return {"product_id": product_id, "type": str(type(product_id))}
الناتج:
INFO: 127.0.0.1:50123 - "GET /api/items HTTP/1.1" 200 OK
Output (
/products/42):
{"product_id": 42, "type": "<class 'int'>"}
(2) التحقق من قيود Path()
يضيف Path() قيوداً مثل النطاقات العددية وأطوال السلاسل النصية لمعاملات المسار؛ وتنعكس هذه تلقائياً في توثيق OpenAPI.
| معامل القيد | النوع المطبَّق | المعنى |
|---|---|---|
gt |
int/float | أكبر من (>) |
ge |
int/float | أكبر من أو يساوي (>=) |
lt |
int/float | أصغر من (<) |
le |
int/float | أصغر من أو يساوي (<=) |
min_length |
str | الحد الأدنى للطول |
max_length |
str | الحد الأقصى للطول |
pattern |
str | مطابقة تعبير منتظم |
description |
الكل | وصف OpenAPI |
(2) ▶ مثال: قيود الأرقام في Path()
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/products/{product_id}")
async def get_product(
product_id: int = Path(
gt=0,
le=1000000,
description="معرّف المنتج: عدد صحيح موجب، الحد الأقصى مليون",
),
):
return {"product_id": product_id}
الناتج:
INFO: 127.0.0.1:50123 - "GET /api/items HTTP/1.1" 200 OK
Output (
/products/-1يُرجع 422):
{
"detail": [
{
"loc": ["path", "product_id"],
"msg": "Input should be greater than 0",
"type": "greater_than"
}
]
}
4. شرح مفصّل لمعاملات الاستعلام
(1) معاملات الاستعلام الأساسية
معاملات الاستعلام هي أزواج المفتاح-القيمة التي تلي ? في URL. تُعرَّف كمعاملات دالة، والمعاملات ذات القيم الافتراضية تكون اختيارية.
| نوع معامل الاستعلام | طريقة التعريف | مطلوب |
|---|---|---|
| مطلوب | category: str |
نعم |
| اختياري (افتراضي) | category: str = "all" |
لا |
| اختياري (None) | `category: str | None = None` |
(1) ▶ مثال: أساسيات معاملات الاستعلام
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/prices")
async def search_prices(
category: str | None = Query(None, max_length=50, description="فئة المنتج"),
min_price: float = Query(0.0, ge=0, description="الحد الأدنى للسعر بالدولار"),
max_price: float = Query(999999.0, le=999999, description="الحد الأقصى للسعر بالدولار"),
):
return {
"category": category,
"price_range": f"${min_price} - ${max_price}",
}
الناتج:
INFO: 127.0.0.1:50123 - "GET /api/items HTTP/1.1" 200 OK
Output (
/prices?category=electronics&min_price=10&max_price=500):
{"category": "electronics", "price_range": "$10.0 - $500.0"}
(2) خيارات Query() المتقدمة
| الخيار | الوظيفة | مثال |
|---|---|---|
alias |
أسماء مستعارة للمعامل (مثل تحويل camelCase إلى snake_case) | Query(alias="minPrice") |
deprecated |
وضع علامة مهمل | Query(deprecated=True) |
title |
عنوان OpenAPI | Query(title="Category Filter") |
description |
وصف OpenAPI | Query(description="...") |
examples |
قيمة نموذجية | Query(examples=["electronics"]) |
(2) ▶ مثال: alias وdeprecated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/products")
async def list_products(
sort_by: str = Query(
"name",
alias="sortBy",
description="حقل الترتيب: name، price، created_at",
),
old_filter: str | None = Query(
None,
deprecated=True,
description="استخدم sort_by بدلاً من ذلك",
),
):
return {"sort_by": sort_by}
الناتج:
# تم تعريف الدالة بنجاح
5. معاملات التعداد
(1) تقييد القيم الممكنة في تعداد سلسلة نصية
عندما لا يمكن أن يأخذ معامل إلا مجموعة ثابتة من القيم، استخدم قيد Enum، وسيعرض FastAPI تلقائياً قائمة منسدلة في التوثيق.
(1) ▶ مثال: تعداد معاملات المسار
from enum import Enum
from fastapi import FastAPI
class Category(str, Enum):
electronics = "electronics"
clothing = "clothing"
food = "food"
books = "books"
app = FastAPI()
@app.get("/categories/{category}")
async def get_category(category: Category):
return {
"category": category,
"value": category.value,
"label": category.name,
}
الناتج:
INFO: 127.0.0.1:50123 - "GET /api/items HTTP/1.1" 200 OK
Output (
/categories/electronics):
{"category": "electronics", "value": "electronics", "label": "electronics"}
(2) ▶ مثال: تعداد معاملات الاستعلام والترتيب
from enum import Enum
from fastapi import FastAPI, Query
class SortOrder(str, Enum):
asc = "asc"
desc = "desc"
app = FastAPI()
@app.get("/products")
async def list_products(
sort_order: SortOrder = Query(SortOrder.asc),
limit: int = Query(20, ge=1, le=100),
offset: int = Query(0, ge=0),
):
return {
"sort": sort_order.value,
"limit": limit,
"offset": offset,
}
الناتج:
# تم تعريف الدالة بنجاح
6. دمج المعاملات المتعددة والأخطاء الشائعة
(1) قواعد ترتيب تعريف المعاملات
قواعد FastAPI لتحديد أنواع المعاملات: أي {param} في المسار هو معامل مسار؛ وإلا فهو معامل استعلام (المعاملات ذات تعليقات الأنواع تكون مطلوبة، وتلك ذات القيم الافتراضية تكون اختيارية).
| الترتيب | نوع المعامل | المعيار |
|---|---|---|
| 1 | معامل المسار | URL يحتوي على {param} |
| 2 | معاملات الاستعلام (مطلوبة) | بدون قيمة افتراضية؛ غير مضمنة في المسار |
| 3 | معاملات الاستعلام (اختيارية) | لها قيمة افتراضية أو None |
(1) ▶ مثال: دمج معاملات متعددة — بحث منتجات PriceTracker
from fastapi import FastAPI, Path, Query
from enum import Enum
class Category(str, Enum):
electronics = "electronics"
clothing = "clothing"
food = "food"
app = FastAPI()
@app.get("/products/{product_id}/prices")
async def get_product_prices(
product_id: int = Path(gt=0, description="معرّف المنتج"),
category: Category | None = Query(None, description="تصفية حسب الفئة"),
min_price: float = Query(0.0, ge=0, description="الحد الأدنى للسعر بالدولار"),
max_price: float = Query(99999.0, ge=0, description="الحد الأقصى للسعر بالدولار"),
sort: str = Query("date", pattern="^(date|price)$"),
limit: int = Query(20, ge=1, le=100),
offset: int = Query(0, ge=0),
):
return {
"product_id": product_id,
"category": category,
"price_range": [min_price, max_price],
"sort": sort,
"pagination": {"limit": limit, "offset": offset},
}
الناتج:
# تم تعريف الدالة بنجاح
(2) أخطاء شائعة
| الخطأ | الصياغة الخاطئة | الصياغة الصحيحة |
|---|---|---|
| معامل المسار اختياري | product_id: int = None |
معامل المسار مطلوب دائماً |
| القيمة الافتراضية تتعارض مع Query | limit: int = 20, Query(ge=1) |
limit: int = Query(20, ge=1) |
| المعاملات الاختيارية: None | category: str = None |
`category: str |
التعدادات لا تستخدم الفئة الأساسية str |
class Cat(Enum): |
class Cat(str, Enum): |
7. مثال شامل
معاملات المسار ومعاملات الاستعلام وقيود التعداد هي أساس بناء واجهات API مرنة. فيما يلي ندمج قيود المسار وتصفح الاستعلام وتصفية التعداد.
from fastapi import FastAPI, Path, Query
from enum import Enum
app = FastAPI()
class SortOrder(str, Enum):
asc = "asc"
desc = "desc"
PRODUCTS = [{"id": i, "name": f"Product-{i}", "price": i * 10.0} for i in range(1, 101)]
@app.get("/products/{product_id}")
async def get_product(
product_id: int = Path(gt=0, description="معرّف المنتج"),
sort: SortOrder = Query(SortOrder.asc),
limit: int = Query(10, ge=1, le=100),
offset: int = Query(0, ge=0),
):
return {
"product_id": product_id,
"sort": sort.value,
"limit": limit,
"offset": offset,
}
الناتج:
GET /products/5?sort=desc&limit=20&offset=10 → {"product_id":5,"sort":"desc","limit":20,"offset":10}
GET /products/0 → 422 Validation Error (product_id must be > 0)
❓أسئلة شائعة
category: str مطلوب، بينما category: str = "all" اختياري. يمكنك أيضاً وضع علامة صريحة على المعامل كمطلوب باستخدام Query(...).gt=0 يعني أن القيمة يجب أن تكون > 0 (باستثناء 0)، بينما ge=0 يعني >= 0 (بما في ذلك 0). معاملات نوع المعرّف تستخدم عموماً gt=0، ومعاملات نوع السعر تستخدم ge=0.Query(max_length=N) لتقييد طول السلاسل النصية، وQuery(ge=N, le=M) لتقييد النطاقات العددية.📖ملخص
- معاملات المسار تُعرَّف في URL باستخدام
{param}؛ يحوّلها FastAPI ويتحقق منها تلقائياً بناءً على تلميحات الأنواع. - يضيف
Path()قيود نطاق عددي (gt/ge/lt/le) وقيود سلسلة نصية (min_length/max_length) - معاملات الاستعلام تُعرَّف كمعاملات دالة؛ تلك ذات القيم الافتراضية اختيارية، وتلك بدون قيم افتراضية مطلوبة.
- يدعم
Query()خيارات متقدمة مثلalias/deprecated/description/examplesوغيرها - معاملات التعداد (
str, Enum) تقيّد القيم المتاحة وتعرض تلقائياً قائمة منسدلة في التوثيق
📝تمارين
- مسألة أساسية (الصعوبة ⭐): أنشئ نقطة نهاية GET في
/items/{item_id}تأخذitem_id(عدد صحيح موجب) كمدخل وتُرجع{"item_id": item_id}. تلميح:item_id: int = Path(gt=0) - مسألة متقدمة (الصعوبة ⭐⭐): أنشئ نقطة نهاية استعلام
/productsلـ PriceTracker، تدعم ثلاثة معاملات استعلام:category(سلسلة نصية اختيارية، حتى 50 حرفاً)،min_price(≥0)، وmax_price(≤999999). تلميح:Query(None, max_length=50) - مسألة تحدٍ (الصعوبة: ⭐⭐⭐): أنشئ نقطة نهاية
/products/{product_id}/pricesوباستخدام معاملات المسار (product_id > 0) ومعاملات استعلام التعداد (SortOrder: asc/desc) ومعاملات التصفح (limit 1-100، offset ≥ 0)، تحقق أن الإدخال غير الصالح يُرجع خطأ 422. تلميح: عرّفSortOrder(str, Enum)ومعاملاتQuery()متعددة.
---|



