
في الدروس السابقة، قمنا ببناء تطبيق FastAPI بسيط لإدارة المهام (Tasks API) داخل ملف واحد. ربما لاحظت أنه مع إضافة المزيد من الوظائف، أصبح الملف أطول وأكثر تعقيداً. تخيل أنك تريد إضافة قاعدة بيانات، أو نظام مصادقة، أو واجهات برمجة إضافية. في هذه الحالة، سيكون من المستحيل تقريباً العمل بملف واحد. هنا يأتي دور تنظيم المشروع.
تنظيم المشروع يعني تقسيم الكود إلى ملفات ومجلدات منظمة، بحيث يكون لكل جزء من التطبيق مكانه الخاص. هذا يشبه تنظيم منزل: لديك غرفة للنوم، وأخرى للطعام، وأخرى للاستقبال. بنفس الطريقة، في مشروع FastAPI، لدينا:
هناك عدة أسباب تجعل تنظيم المشروع ضرورياً، خاصة عندما تبدأ في بناء تطبيقات حقيقية:
لنراجع بسرعة شكل مشروعنا الحالي من الدرس السابق. كان لدينا ملف واحد اسمه main.py يحتوي على كل شيء: تعريف التطبيق، نماذج Pydantic، دوال CRUD، ونقاط النهاية. هذا هو شكل الكود التقريبي:
# 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.get("/tasks")
def get_tasks():
return tasks_db
@app.post("/tasks")
def create_task(task: Task):
tasks_db.append(task)
return task
# ... المزيد من الدوال
هذا الكود يعمل بشكل جيد، لكنه يصبح فوضوياً بسرعة. تخيل إضافة 10 نقاط نهاية جديدة، أو ربط قاعدة بيانات، أو إضافة التحقق من الأخطاء. سيكون الملف طويلاً جداً ويصعب قراءته.
سنقوم الآن بإعادة هيكلة مشروعنا ليصبح أكثر احترافية. الهيكل الذي سنستخدمه هو الأكثر شيوعاً في مشاريع FastAPI المتوسطة والكبيرة. إليك الهيكل النهائي الذي سنبنيه:
my_fastapi_project/
│
├── app/
│ ├── __init__.py
│ ├── main.py
│ ├── models.py
│ ├── schemas.py
│ ├── database.py
│ ├── crud.py
│ └── routers/
│ ├── __init__.py
│ └── tasks.py
│
├── requirements.txt
└── .env
دعنا نشرح كل ملف ومجلد:
app/: المجلد الرئيسي للتطبيق.__init__.py: ملف يجعل المجلد "حزمة" (Package) قابلة للاستيراد.main.py: نقطة الدخول للتطبيق (حيث ننشئ تطبيق FastAPI).models.py: نماذج قاعدة البيانات (إذا استخدمنا ORM مثل SQLAlchemy).schemas.py: نماذج Pydantic للتحقق من صحة البيانات المدخلة والمخرجة.database.py: إعدادات الاتصال بقاعدة البيانات.crud.py: دوال إنشاء وقراءة وتحديث وحذف (CRUD) من قاعدة البيانات.routers/: مجلد لتقسيم نقاط النهاية حسب الوظيفة (مثلاً، ملف للمهام، وآخر للمستخدمين).requirements.txt: قائمة بالمكتبات المطلوبة للمشروع..env: ملف لتخزين المتغيرات السرية مثل كلمة مرور قاعدة البيانات.سنقوم الآن بتحويل مشروع المهام (Tasks API) الذي أنشأناه في الدرس السابق إلى هذا الهيكل. اتبع الخطوات التالية:
قم بإنشاء المجلدات والملفات التالية في مجلد مشروعك:
appapp: ملف __init__.py (فارغ)app: ملف main.pyapp: ملف schemas.pyapp: ملف database.pyapp: ملف crud.pyapp: مجلد routersrouters: ملف __init__.py (فارغ)routers: ملف tasks.pyفي ملف app/schemas.py، ضع كل نماذج Pydantic الخاصة بالمهام:
# app/schemas.py
from pydantic import BaseModel
from typing import Optional
class TaskBase(BaseModel):
title: str
description: Optional[str] = None
completed: bool = False
class TaskCreate(TaskBase):
pass
class Task(TaskBase):
id: int
class Config:
orm_mode = True
في ملف app/crud.py، ضع دوال التعامل مع قاعدة البيانات (سنستخدم قائمة مؤقتة الآن):
# app/crud.py
from app.schemas import Task, TaskCreate
# قاعدة بيانات مؤقتة (قائمة)
tasks_db = []
task_id_counter = 1
def get_tasks():
return tasks_db
def create_task(task: TaskCreate):
global task_id_counter
new_task = Task(id=task_id_counter, **task.dict())
tasks_db.append(new_task)
task_id_counter += 1
return new_task
def get_task(task_id: int):
for task in tasks_db:
if task.id == task_id:
return task
return None
def update_task(task_id: int, task: TaskCreate):
for index, existing_task in enumerate(tasks_db):
if existing_task.id == task_id:
updated_task = Task(id=task_id, **task.dict())
tasks_db[index] = updated_task
return updated_task
return None
def delete_task(task_id: int):
global tasks_db
tasks_db = [task for task in tasks_db if task.id != task_id]
return True
في ملف app/routers/tasks.py، ضع نقاط النهاية الخاصة بالمهام:
# app/routers/tasks.py
from fastapi import APIRouter, HTTPException
from app.schemas import Task, TaskCreate
from app.crud import get_tasks, create_task, get_task, update_task, delete_task
router = APIRouter(prefix="/tasks", tags=["tasks"])
@router.get("/", response_model=list[Task])
def read_tasks():
return get_tasks()
@router.post("/", response_model=Task)
def create_new_task(task: TaskCreate):
return create_task(task)
@router.get("/{task_id}", response_model=Task)
def read_task(task_id: int):
db_task = get_task(task_id)
if db_task is None:
raise HTTPException(status_code=404, detail="Task not found")
return db_task
@router.put("/{task_id}", response_model=Task)
def update_existing_task(task_id: int, task: TaskCreate):
db_task = update_task(task_id, task)
if db_task is None:
raise HTTPException(status_code=404, detail="Task not found")
return db_task
@router.delete("/{task_id}")
def delete_existing_task(task_id: int):
delete_task(task_id)
return {"message": "Task deleted successfully"}
في ملف app/main.py، قم بإنشاء تطبيق FastAPI وتضمين المسارات:
# app/main.py
from fastapi import FastAPI
from app.routers import tasks
app = FastAPI(title="Tasks API", description="A simple tasks management API")
app.include_router(tasks.router)
@app.get("/")
def root():
return {"message": "Welcome to the Tasks API!"}
from app.schemas import Task، تأكد من أنك تعمل من المجلد الرئيسي للمشروع (حيث يوجد مجلد app).pip freeze > requirements.txt.التمرين: قم بتوسيع المشروع المنظم بإضافة مسار جديد (Router) للمستخدمين (Users).
المطلوب:
app/routers/users.py./users/ تعيد قائمة بأسماء مستخدمين وهميين (مثل ["Alice", "Bob"]).APIRouter مع prefix="/users".app/main.py باستخدام app.include_router().تلميح: اتبع نفس نمط ملف tasks.py ولكن بدون الحاجة إلى قاعدة بيانات.
بهذا نكون قد حولنا مشروعنا من ملف واحد فوضوي إلى هيكل منظم واحترافي. في الدروس القادمة، سنبني على هذا الهيكل لربط التطبيق بقاعدة بيانات حقيقية وإضافة المزيد من الميزات.
جاري تحميل التقييمات...