بناء CRUD بسيط للمهام باستخدام FastAPI
مقدمة عن CRUD ولماذا نحتاجه
الدرس الخامس عشر: بناء CRUD بسيط للمهام (Simple Tasks CRUD)
مرحباً بك في الدرس الخامس عشر من سلسلة تعلم بايثون وبناء API عملي. في الدروس السابقة، تعلمنا كيفية إنشاء endpoints بسيطة باستخدام FastAPI، وكيفية التعامل مع path parameters و query parameters، وأخيراً كيفية تعريف نماذج البيانات باستخدام Pydantic. الآن حان الوقت لتطبيق كل هذه المفاهيم معاً لبناء أول عملية CRUD كاملة.
ما هو CRUD ولماذا نحتاجه؟
CRUD هو اختصار لأربع عمليات أساسية في أي تطبيق يدير بيانات:
- Create (إنشاء): إضافة بيانات جديدة (مثل إضافة مهمة جديدة).
- Read (قراءة): عرض البيانات الموجودة (مثل عرض قائمة المهام أو مهمة محددة).
- Update (تحديث): تعديل بيانات موجودة (مثل تغيير عنوان مهمة).
- Delete (حذف): إزالة بيانات (مثل حذف مهمة منتهية).
بدون CRUD، سيكون تطبيقك مجرد شاشة عرض ثابتة. هذه العمليات هي قلب أي API ديناميكي، سواء كان موقعاً للتجارة الإلكترونية، أو تطبيقاً لإدارة المهام، أو نظاماً لإدارة المحتوى.
خطوات التنفيذ: بناء Tasks API بسيط
سنقوم ببناء API صغير يدير قائمة مهام (To-Do List). سنستخدم قائمة بايثون عادية لتخزين البيانات مؤقتاً (في الدرس القادم سننتقل إلى قاعدة بيانات). هيا بنا نبدأ.
1. إنشاء ملف المشروع
افتح مجلد مشروعك وأنشئ ملفاً جديداً باسم main.py. هذا الملف سيحتوي على كل الكود الخاص بنا.
2. كتابة الكود الأساسي
سنبدأ باستيراد FastAPI وإنشاء التطبيق، ثم تعريف نموذج Pydantic للمهمة، وأخيراً إنشاء قائمة فارغة لتخزين المهام.
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional
app = FastAPI()
# نموذج Pydantic للمهمة
class Task(BaseModel):
id: int
title: str
description: Optional[str] = None
completed: bool = False
# قاعدة بيانات مؤقتة (قائمة)
tasks_db = []
3. تنفيذ عملية Create (إنشاء مهمة)
سنستخدم طلب POST لإضافة مهمة جديدة. سنقوم بإنشاء endpoint على المسار /tasks.
@app.post("/tasks")
def create_task(task: Task):
# نضيف المهمة إلى القاعدة المؤقتة
tasks_db.append(task)
return {"message": "Task created successfully", "task": task}
شرح الكود: عندما يرسل المستخدم طلب POST مع بيانات مهمة (JSON)، يقوم FastAPI بتحويلها تلقائياً إلى كائن من نوع Task بفضل Pydantic. ثم نضيف هذا الكائن إلى قائمتنا tasks_db ونعيد رسالة نجاح.
4. تنفيذ عملية Read (قراءة المهام)
سنحتاج إلى endpoint لعرض جميع المهام، وآخر لعرض مهمة واحدة باستخدام id.
# قراءة جميع المهام
@app.get("/tasks")
def get_all_tasks():
return tasks_db
# قراءة مهمة واحدة بواسطة id
@app.get("/tasks/{task_id}")
def get_task(task_id: int):
for task in tasks_db:
if task.id == task_id:
return task
return {"error": "Task not found"}
شرح الكود: get_all_tasks يعيد القائمة كاملة. get_task يستخدم path parameter (task_id) ويبحث في القائمة عن مهمة تطابق هذا الرقم. إذا لم يجد، يعيد رسالة خطأ.
5. تنفيذ عملية Update (تحديث مهمة)
لتحديث مهمة، سنستخدم طلب PUT. سنحتاج إلى معرفة id المهمة التي نريد تحديثها، والبيانات الجديدة.
@app.put("/tasks/{task_id}")
def update_task(task_id: int, updated_task: Task):
for index, task in enumerate(tasks_db):
if task.id == task_id:
tasks_db[index] = updated_task
return {"message": "Task updated", "task": updated_task}
return {"error": "Task not found"}
شرح الكود: نستخدم enumerate للحصول على فهرس كل مهمة. عندما نجد المهمة المطلوبة، نستبدل العنصر في القائمة بالبيانات الجديدة.
6. تنفيذ عملية Delete (حذف مهمة)
للحذف، سنستخدم طلب DELETE.
@app.delete("/tasks/{task_id}")
def delete_task(task_id: int):
for index, task in enumerate(tasks_db):
if task.id == task_id:
del tasks_db[index]
return {"message": "Task deleted"}
return {"error": "Task not found"}
شرح الكود: مشابه للتحديث، لكننا نستخدم del لحذف العنصر من القائمة.
الكود الكامل للتطبيق
الآن، إليك الكود الكامل الذي يجب أن يكون في ملف main.py:
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional
app = FastAPI()
class Task(BaseModel):
id: int
title: str
description: Optional[str] = None
completed: bool = False
tasks_db = []
@app.post("/tasks")
def create_task(task: Task):
tasks_db.append(task)
return {"message": "Task created successfully", "task": task}
@app.get("/tasks")
def get_all_tasks():
return tasks_db
@app.get("/tasks/{task_id}")
def get_task(task_id: int):
for task in tasks_db:
if task.id == task_id:
return task
return {"error": "Task not found"}
@app.put("/tasks/{task_id}")
def update_task(task_id: int, updated_task: Task):
for index, task in enumerate(tasks_db):
if task.id == task_id:
tasks_db[index] = updated_task
return {"message": "Task updated", "task": updated_task}
return {"error": "Task not found"}
@app.delete("/tasks/{task_id}")
def delete_task(task_id: int):
for index, task in enumerate(tasks_db):
if task.id == task_id:
del tasks_db[index]
return {"message": "Task deleted"}
return {"error": "Task not found"}
تشغيل التطبيق واختباره
لتشغيل التطبيق، استخدم الأمر التالي في terminal:
uvicorn main:app --reload
ثم افتح المتصفح على http://127.0.0.1:8000/docs لتجربة جميع العمليات عبر واجهة Swagger الجميلة.
{"id": 1, "title": "تعلم FastAPI", "description": "إكمال الدروس", "completed": false}
ثم استخدم GET لرؤية المهمة. بعد ذلك، جرب تحديثها أو حذفها.
أخطاء شائعة وكيفية تجنبها
- نسيان تشغيل الخادم: تأكد من أنك في المجلد الصحيح وأن الخادم يعمل قبل إرسال الطلبات.
- إرسال id مكرر: في هذا المثال البسيط، لا يوجد تحقق من أن
idفريد. إذا أرسلت مهمتين بنفسid، فسيتم تخزينهما معاً. هذا شيء سنعالجه في الدروس القادمة عند استخدام قاعدة بيانات. - عدم تحديث جميع الحقول: في عملية Update، نحن نستبدل المهمة بأكملها. إذا أرسلت بيانات ناقصة (مثل إرسال
titleفقط)، فستفقد الحقول الأخرى. هذا يُسمى "PUT كامل". سنتعلم لاحقاً كيفية عمل تحديث جزئي (PATCH).
تمرين صغير
الآن حان دورك لتطبيق ما تعلمته. قم بالتالي:
- أضف حقلاً جديداً لنموذج
Taskباسمpriorityمن نوعint(قيمته الافتراضية 1). - عدّل دالة
create_taskلتتحقق من أنidالمهمة الجديدة ليس مكرراً. إذا كان مكرراً، أعد رسالة خطأ مناسبة. - اختبر التعديلات باستخدام Swagger UI.
هذا التمرين سيساعدك على فهم كيفية تعديل النماذج وإضافة منطق بسيط للتحقق من البيانات. في الدرس القادم، سنتعلم كيفية تنظيم هذا المشروع بشكل أفضل.
جاري تحميل التقييمات...