
في الدرس السابق، تعلمنا كيفية إنشاء أول نقطة نهاية (endpoint) في FastAPI تعيد رسالة ثابتة. لكن في عالم تطبيقات الويب الحقيقية، نادراً ما نتعامل مع بيانات ثابتة. تخيل أنك تريد بناء API لإدارة المهام (Tasks API) – كيف ستتمكن من جلب مهمة محددة برقمها؟ أو كيف ستسمح للمستخدم بتصفية المهام حسب تاريخ معين؟ هنا يأتي دور معاملات المسار (Path Parameters) و معاملات الاستعلام (Query Parameters).
ببساطة، هذه المعاملات هي الطريقة التي نرسل بها بيانات ديناميكية إلى الـ API الخاص بنا. بدونها، سيكون كل endpoint ثابتاً ولا يمكنه التعامل مع طلبات مختلفة. في هذا الدرس، سنتعلم كيفية استخدام هذين النوعين من المعاملات لجعل API الخاص بنا تفاعلياً وقوياً.
معاملات المسار (Path Parameters) هي أجزاء متغيرة من مسار URL نفسه. على سبيل المثال، في الرابط التالي:
https://example.com/items/42
الرقم 42 هو معامل مسار يمثل رقم العنصر الذي نريد الحصول عليه. في FastAPI، نحدد هذه المعاملات باستخدام الأقواس المتعرجة {} داخل مسار الـ 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 يقوم تلقائياً باستخراج القيمة من المسار وتحويلها إلى عدد صحيح.task_id ورسالة ترحيبية.لتجربة الكود:
uvicorn main:app --reloadhttp://127.0.0.1:8000/tasks/5{"task_id":5,"message":"تم استلام المهمة رقم 5"}/tasks/abc، سيعيد FastAPI خطأ تلقائياً لأننا حددنا أن task_id يجب أن يكون عدداً صحيحاً. هذا هو أحد فوائد استخدام type hints في Python مع FastAPI.
معاملات الاستعلام (Query Parameters) هي أجزاء من URL تأتي بعد علامة الاستفهام ? وتكون على شكل أزواج مفتاح=قيمة (key=value). على سبيل المثال:
https://example.com/items?category=books&page=2
هنا category و page هما معاملات استعلام. في FastAPI، نحددها ببساطة كمعاملات اختيارية في دالة الـ endpoint.
لنقم بتوسيع مثالنا السابق. سنضيف إمكانية تصفية المهام حسب الحالة (مكتملة أم لا) أو حسب الكلمة المفتاحية في العنوان.
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.لتجربة الكود:
--reload).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=تعلم – ستعيد المهام غير المكتملة التي تحتوي على كلمة "تعلم".?keyword=كتاب&completed=true وستحصل على نفس النتيجة. FastAPI يتعامل معها كمعاملات مسماة (named parameters).
من المهم فهم متى نستخدم كل نوع:
task_id هو رقم، فتأكد من تحديد النوع int. إذا أرسل المستخدم نصاً، سيعيد FastAPI خطأ 422 (Unprocessable Entity) بدلاً من خطأ 500 (Internal Server Error).None أو قيمة أخرى). وإلا سيعتبره FastAPI إجبارياً./tasks/{task_id} و /tasks/create، فقد يحدث ارتباك. من الأفضل استخدام هيكل واضح مثل /tasks/{task_id} للمهام الفردية و /tasks للقائمة.الآن حان دورك لتطبيق ما تعلمته. قم بالتالي:
main.py على المسار /items/{item_id}.item_id من نوع int.q من نوع str وقيمته الافتراضية None.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 لإنشاء نماذج بيانات أكثر تعقيداً وتحسين التحقق من صحة البيانات.
جاري تحميل التقييمات...