تنظيم مشروع FastAPI بشكل احترافي

تنظيم مشروع FastAPI بشكل احترافي

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

لماذا نحتاج لتنظيم المشروع؟

الفصل الأول: لماذا نحتاج إلى تنظيم المشروع؟

في الدروس السابقة، قمنا ببناء تطبيق FastAPI بسيط لإدارة المهام (Tasks API) داخل ملف واحد. ربما لاحظت أنه مع إضافة المزيد من الوظائف، أصبح الملف أطول وأكثر تعقيداً. تخيل أنك تريد إضافة قاعدة بيانات، أو نظام مصادقة، أو واجهات برمجة إضافية. في هذه الحالة، سيكون من المستحيل تقريباً العمل بملف واحد. هنا يأتي دور تنظيم المشروع.

ما هو تنظيم المشروع؟

تنظيم المشروع يعني تقسيم الكود إلى ملفات ومجلدات منظمة، بحيث يكون لكل جزء من التطبيق مكانه الخاص. هذا يشبه تنظيم منزل: لديك غرفة للنوم، وأخرى للطعام، وأخرى للاستقبال. بنفس الطريقة، في مشروع FastAPI، لدينا:

  • ملفات للنماذج (Models): لتعريف هياكل البيانات.
  • ملفات للمسارات (Routes): لتعريف نقاط النهاية (Endpoints).
  • ملفات للإعدادات (Config): لإعدادات التطبيق مثل قاعدة البيانات.
  • ملفات للخدمات (Services): للمنطق التجاري (Business Logic).

لماذا نحتاج إلى تنظيم المشروع؟

هناك عدة أسباب تجعل تنظيم المشروع ضرورياً، خاصة عندما تبدأ في بناء تطبيقات حقيقية:

  • سهولة الصيانة: عندما يكون الكود منظمًا، يسهل العثور على الأخطاء وإصلاحها. بدلاً من البحث في 500 سطر داخل ملف واحد، تذهب مباشرة إلى الملف المخصص للمشكلة.
  • قابلية التوسع: عندما تريد إضافة ميزة جديدة، مثل إضافة مستخدمين أو مهام متقدمة، يمكنك إضافة ملفات جديدة دون المساس بالكود الموجود.
  • العمل الجماعي: إذا كنت تعمل مع فريق، يمكن لكل مطور العمل على جزء مختلف من التطبيق دون التعارض مع الآخرين.
  • إعادة الاستخدام: يمكنك إعادة استخدام أجزاء من الكود في مشاريع أخرى، مثل نماذج البيانات أو دوال المساعدة.

المشكلة: مشروعنا الحالي (ملف واحد)

لنراجع بسرعة شكل مشروعنا الحالي من الدرس السابق. كان لدينا ملف واحد اسمه 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) الذي أنشأناه في الدرس السابق إلى هذا الهيكل. اتبع الخطوات التالية:

الخطوة 1: إنشاء المجلدات والملفات

قم بإنشاء المجلدات والملفات التالية في مجلد مشروعك:

  • مجلد app
  • داخل app: ملف __init__.py (فارغ)
  • داخل app: ملف main.py
  • داخل app: ملف schemas.py
  • داخل app: ملف database.py
  • داخل app: ملف crud.py
  • داخل app: مجلد routers
  • داخل routers: ملف __init__.py (فارغ)
  • داخل routers: ملف tasks.py

الخطوة 2: نقل نماذج Pydantic إلى schemas.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

الخطوة 3: إنشاء دوال CRUD في crud.py

في ملف 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

الخطوة 4: إنشاء نقاط النهاية في routers/tasks.py

في ملف 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"}

الخطوة 5: ربط كل شيء في main.py

في ملف 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!"}

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

  • نسيان ملف __init__.py: بدون هذا الملف، لن يتمكن Python من استيراد المجلدات كحزم. تأكد من وجوده في كل مجلد تريد استيراد منه.
  • أخطاء في مسارات الاستيراد: عند استخدام from app.schemas import Task، تأكد من أنك تعمل من المجلد الرئيسي للمشروع (حيث يوجد مجلد app).
  • عدم تحديث ملف requirements.txt: بعد إضافة مكتبات جديدة، قم بتحديث هذا الملف باستخدام الأمر pip freeze > requirements.txt.

تمرين صغير

التمرين: قم بتوسيع المشروع المنظم بإضافة مسار جديد (Router) للمستخدمين (Users).

المطلوب:

  • أنشئ ملف app/routers/users.py.
  • أضف نقطة نهاية GET واحدة في المسار /users/ تعيد قائمة بأسماء مستخدمين وهميين (مثل ["Alice", "Bob"]).
  • استخدم APIRouter مع prefix="/users".
  • قم بتضمين هذا المسار في app/main.py باستخدام app.include_router().

تلميح: اتبع نفس نمط ملف tasks.py ولكن بدون الحاجة إلى قاعدة بيانات.

بهذا نكون قد حولنا مشروعنا من ملف واحد فوضوي إلى هيكل منظم واحترافي. في الدروس القادمة، سنبني على هذا الهيكل لربط التطبيق بقاعدة بيانات حقيقية وإضافة المزيد من الميزات.

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

    تنظيم مشروع FastAPI بشكل احترافي | الأكاديمية التعليمية للذكاء الاصطناعي | AI Tools Oasis