مسار و Query Parameters في FastAPI

مسار و Query Parameters في FastAPI

45 دقيقة
١٦ يونيو ٢٠٢٦
المرحلة 1 من 6

مقدمة: لماذا نحتاج path و query parameters؟

الفصل الأول: مقدمة: لماذا نحتاج إلى Path Parameters و Query Parameters؟

في الدرس السابق، تعلمنا كيفية إنشاء أول نقطة نهاية (endpoint) في FastAPI تعيد رسالة ثابتة. لكن في عالم تطبيقات الويب الحقيقية، نادراً ما نتعامل مع بيانات ثابتة. تخيل أنك تريد بناء API لإدارة المهام (Tasks API) – كيف ستتمكن من جلب مهمة محددة برقمها؟ أو كيف ستسمح للمستخدم بتصفية المهام حسب تاريخ معين؟ هنا يأتي دور معاملات المسار (Path Parameters) و معاملات الاستعلام (Query Parameters).

ببساطة، هذه المعاملات هي الطريقة التي نرسل بها بيانات ديناميكية إلى الـ API الخاص بنا. بدونها، سيكون كل endpoint ثابتاً ولا يمكنه التعامل مع طلبات مختلفة. في هذا الدرس، سنتعلم كيفية استخدام هذين النوعين من المعاملات لجعل API الخاص بنا تفاعلياً وقوياً.

ما هي Path Parameters؟

معاملات المسار (Path Parameters) هي أجزاء متغيرة من مسار URL نفسه. على سبيل المثال، في الرابط التالي:

https://example.com/items/42

الرقم 42 هو معامل مسار يمثل رقم العنصر الذي نريد الحصول عليه. في FastAPI، نحدد هذه المعاملات باستخدام الأقواس المتعرجة {} داخل مسار الـ endpoint.

مثال عملي: إنشاء Endpoint بمعامل مسار

لنقم بإنشاء endpoint بسيط يستقبل رقم مهمة ويعيد تفاصيلها. افتح ملف main.py الذي أنشأناه في الدرس السابق وأضف الكود التالي:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"message": "Hello World"}

@app.get("/tasks/{task_id}")
def read_task(task_id: int):
    return {"task_id": task_id, "message": f"تم استلام المهمة رقم {task_id}"}

شرح الكود:

  • @app.get("/tasks/{task_id}"): هذا هو الـ decorator الذي يحدد أن هذا الـ endpoint يستجيب لطلبات GET على المسار /tasks/ متبوعاً بقيمة متغيرة نسميها task_id.
  • def read_task(task_id: int):: هنا نعرف دالة تأخذ معامل اسمه task_id من النوع int. FastAPI يقوم تلقائياً باستخراج القيمة من المسار وتحويلها إلى عدد صحيح.
  • الدالة تعيد قاموساً (dictionary) يحتوي على task_id ورسالة ترحيبية.

لتجربة الكود:

  1. شغل الخادم باستخدام الأمر: uvicorn main:app --reload
  2. افتح المتصفح على الرابط: http://127.0.0.1:8000/tasks/5
  3. سترى النتيجة: {"task_id":5,"message":"تم استلام المهمة رقم 5"}
ملاحظة مهمة: إذا حاولت إدخال قيمة غير رقمية مثل /tasks/abc، سيعيد FastAPI خطأ تلقائياً لأننا حددنا أن task_id يجب أن يكون عدداً صحيحاً. هذا هو أحد فوائد استخدام type hints في Python مع FastAPI.

ما هي Query Parameters؟

معاملات الاستعلام (Query Parameters) هي أجزاء من URL تأتي بعد علامة الاستفهام ? وتكون على شكل أزواج مفتاح=قيمة (key=value). على سبيل المثال:

https://example.com/items?category=books&page=2

هنا category و page هما معاملات استعلام. في FastAPI، نحددها ببساطة كمعاملات اختيارية في دالة الـ endpoint.

مثال عملي: إضافة Query Parameters

لنقم بتوسيع مثالنا السابق. سنضيف إمكانية تصفية المهام حسب الحالة (مكتملة أم لا) أو حسب الكلمة المفتاحية في العنوان.

from fastapi import FastAPI

app = FastAPI()

# قاعدة بيانات وهمية (سنستبدلها لاحقاً بقاعدة بيانات حقيقية)
fake_tasks_db = [
    {"id": 1, "title": "شراء حليب", "completed": False},
    {"id": 2, "title": "قراءة كتاب", "completed": True},
    {"id": 3, "title": "تعلم FastAPI", "completed": False},
]

@app.get("/tasks/{task_id}")
def read_task(task_id: int):
    # البحث عن المهمة في القائمة
    for task in fake_tasks_db:
        if task["id"] == task_id:
            return task
    return {"error": "المهمة غير موجودة"}

@app.get("/tasks")
def list_tasks(completed: bool = None, keyword: str = None):
    """
    تعيد قائمة بالمهام، مع إمكانية التصفية حسب:
    - completed: إذا كان True تعيد المهام المكتملة، إذا كان False تعيد غير المكتملة
    - keyword: تعيد المهام التي تحتوي على الكلمة المفتاحية في العنوان
    """
    result = fake_tasks_db
    
    if completed is not None:
        result = [task for task in result if task["completed"] == completed]
    
    if keyword:
        result = [task for task in result if keyword.lower() in task["title"].lower()]
    
    return result

شرح الكود:

  • @app.get("/tasks"): هذا endpoint جديد لاستعراض جميع المهام.
  • def list_tasks(completed: bool = None, keyword: str = None):: نعرف معاملين اختياريين. completed من نوع bool (قيمته الافتراضية None تعني عدم التصفية)، و keyword من نوع str.
  • داخل الدالة، نطبق منطق التصفية بناءً على القيم المستلمة.

لتجربة الكود:

  1. أعد تشغيل الخادم (إذا كان يعمل، سيتم إعادة التحميل تلقائياً بسبب --reload).
  2. جرب الروابط التالية في المتصفح:
    • http://127.0.0.1:8000/tasks – ستعيد جميع المهام.
    • http://127.0.0.1:8000/tasks?completed=true – ستعيد المهام المكتملة فقط.
    • http://127.0.0.1:8000/tasks?keyword=كتاب – ستعيد المهام التي تحتوي على كلمة "كتاب".
    • http://127.0.0.1:8000/tasks?completed=false&keyword=تعلم – ستعيد المهام غير المكتملة التي تحتوي على كلمة "تعلم".
نصيحة عملية: لاحظ أن ترتيب المعاملات في URL لا يهم. يمكنك كتابة ?keyword=كتاب&completed=true وستحصل على نفس النتيجة. FastAPI يتعامل معها كمعاملات مسماة (named parameters).

الفرق بين Path Parameters و Query Parameters

من المهم فهم متى نستخدم كل نوع:

  • Path Parameters: تستخدم لتحديد مورد محدد (مثل: مهمة معينة، مستخدم معين). هي إجبارية وتكون جزءاً لا يتجزأ من هيكل الـ API.
  • Query Parameters: تستخدم للتصفية، الفرز، أو الصفحات (pagination). هي اختيارية بشكل عام وتضيف مرونة للـ endpoint.

أخطاء شائعة وكيفية تجنبها

  • الخلط بين الأنواع: إذا توقعت أن task_id هو رقم، فتأكد من تحديد النوع int. إذا أرسل المستخدم نصاً، سيعيد FastAPI خطأ 422 (Unprocessable Entity) بدلاً من خطأ 500 (Internal Server Error).
  • نسيان القيم الافتراضية: إذا كان المعامل اختيارياً (مثل query parameter)، يجب أن تعطيه قيمة افتراضية (None أو قيمة أخرى). وإلا سيعتبره FastAPI إجبارياً.
  • تعارض المسارات: تأكد من أن مساراتك لا تتعارض. على سبيل المثال، إذا كان لديك /tasks/{task_id} و /tasks/create، فقد يحدث ارتباك. من الأفضل استخدام هيكل واضح مثل /tasks/{task_id} للمهام الفردية و /tasks للقائمة.

تمرين صغير

الآن حان دورك لتطبيق ما تعلمته. قم بالتالي:

  1. أنشئ endpoint جديد في نفس ملف main.py على المسار /items/{item_id}.
  2. يجب أن يستقبل هذا الـ endpoint معامل مسار item_id من نوع int.
  3. أضف معامل استعلام اختياري اسمه q من نوع str وقيمته الافتراضية None.
  4. يجب أن تعيد الدالة قاموساً يحتوي على item_id وإذا تم توفير q، أضف مفتاحاً اسمه query يحمل قيمة q.

مثال: عند زيارة /items/10?q=test يجب أن تحصل على: {"item_id":10, "query":"test"}.

حاول كتابة الكود بنفسك قبل النظر إلى الحل أدناه.

الحل (اضغط للعرض)
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
    result = {"item_id": item_id}
    if q:
        result["query"] = q
    return result

بهذا نكون قد تعلمنا كيفية جعل API الخاص بنا ديناميكياً باستخدام Path Parameters و Query Parameters. في الدرس القادم، سنتعلم كيفية استخدام Pydantic لإنشاء نماذج بيانات أكثر تعقيداً وتحسين التحقق من صحة البيانات.

جاري تحميل التقييمات...