الرئيسية تعليم لغات البرمجة التعامل مع قواعد البيانات في بايثون عبر SQLite و SQLAlchemy باحترافية

التعامل مع قواعد البيانات في بايثون عبر SQLite و SQLAlchemy باحترافية

amr gamal أكتوبر 03, 2026

في المشاريع البرمجية المتقدمة، نادرًا ما نعتمد على تخزين البيانات في ملفات نصية أو هياكل بيانات مؤقتة تفقد محتواها بمجرد إيقاف تشغيل التطبيق. الانتقال إلى المستوى الاحترافي في بايثون يتطلب فهمًا عميقًا لكيفية إدارة قواعد البيانات العلائقية وضمان استقرار البيانات وتكاملها. في هذا الدرس، سنتجاوز الاستخدامات المبتدئة لمكتبة SQLite المضمنة، وننتقل إلى توظيفها بجانب SQLAlchemy، وهو نظام تعيين الكائنات في العلاقات (ORM) الأكثر قوة ومرونة في بيئة بايثون، لنبني طبقة بيانات مرنة، قابلة للصيانة، وآمنة.

لماذا ننتقل من SQL الخام إلى SQLAlchemy؟ عندما نعمل على تطبيقات صغرى أو سكربتات بسيطة، قد تبدو وحدة sqlite3 المدمجة كافية لتنفيذ استعلامات SQL بشكل مباشر عبر تمرير النصوص البرمجية. لكن مع نمو حجم المشروع، تصبح كتابة استعلامات SQL النصية عرضة لأخطاء الأمان مثل ثغرات الحقن (SQL Injection)، وتفتقر إلى المرونة عند الحاجة لتعديل هيكل الجداول أو نقل التطبيق إلى محرك قواعد بيانات آخر مثل PostgreSQL أو MySQL.

قوم SQLAlchemy بدور الوسيط الذكي؛ فهو يتيح لك التعامل مع قاعدة البيانات وكأنك تتعامل مع كائنات وهياكل بيانات بايثون بحتة (Python Objects). هذا لا يوفر وقتاً طويلاً في الكتابة والتنقيح فحسب، بل يمنحك طبقة تجريد هندسية تتيح لك تغيير محرك قواعد البيانات الأساسي دون الحاجة لإعادة كتابة منطق التطبيق البرمجي أو تعديل مئات الأسطر من الاستعلامات اليدوية.

إعداد البيئة وبناء نموذج البيانات الأول

للبدء، سنحتاج إلى تثبيت مكتبة SQLAlchemy. رغم أن بايثون تأتي بمكتبة sqlite3 افتراضيًا، إلا أن SQLAlchemy ستتولى دور المحرك والوسيط للتعامل مع هذا الملف. تأكد من تثبيت الإصدار الحديث عبر مدير الحزم في بيئتك الافتراضية:

pip install sqlalchemy

في المستوى الاحترافي، ننظم هيكل الكود بعناية لتجنب الفوضى. سنقوم بإنشاء اتصال بقاعدة بيانات محلية، وتحديد هيكل الجداول باستخدام نظام التصريح الحديث (Declarative Base) المعتمد في الإصدارات الحديثة من المكتبة، مع الاستفادة من ميزات التلميح الثابت لأنواع البيانات:

from sqlalchemy import create_engine, String, Integer, DateTime
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, sessionmaker
from datetime import datetime

# إنشاء محرك قاعدة البيانات SQLite محلية
engine = create_engine("sqlite:///sitegeeky_app.db", echo=False)

# تعريف القاعدة الأساسية للنماذج
class Base(DeclarativeBase):
    pass

# تعريف نموذج جدول المستخدمين باستخدام نوع البيانات الموجه (Mapped)
class User(Base):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    username: Mapped[str] = mapped_column(String(50), unique=True, nullable=False)
    email: Mapped[str] = mapped_column(String(120), unique=True, nullable=False)
    created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)

    def __repr__(self) -> str:
        return f"User(id={self.id!r}, username={self.username!r}, email={self.email!r})"

# إنشاء الجداول الفعلية في قاعدة البيانات إذا لم تكن موجودة
Base.metadata.create_all(engine)

في الكود السابق، استخدمنا ميزة Mapped و mapped_column التي أُدخلت في الإصدارات الحديثة من SQLAlchemy (الإصدار 2.0 وما بعده). تتيح هذه الطريقة الاستفادة الكاملة من ميزات التدقيق الثابت لأنواع البيانات (Type Hinting) في بايثون، مما يقلل الأخطاء البرمجية قبل تشغيل الكود ويمنح محررات الاكواد القدرة على إكمال النصوص البرمجية بدقة.

إدارة الجلسات (Sessions) وعمليات الإدخال

التعامل مع قاعدة البيانات في SQLAlchemy يتم من خلال ما يُعرف بـ Session أو الجلسة. الجلسة هي المساحة التي تتتبع التغييرات التي تجريها على الكائنات، وتتولى مزامنتها مع قاعدة البيانات الفعلية عبر معاملات (Transactions) تضمن إما نجاح العملية بالكامل أو التراجع عنها في حال حدوث أي خطأ.

لنفترض أننا نريد إضافة مستخدمين جدد إلى النظام واستعلامهم بشكل آمن عبر تفعيل إدارة السياق:

# إنشاء مصنع الجلسات
SessionLocal = sessionmaker(bind=engine)

def create_user(username: str, email: str):
    # استخدام السياق لضمان إغلاق الجلسة تلقائيًا
    with SessionLocal() as session:
        try:
            new_user = User(username=username, email=email)
            session.add(new_user)
            # تأكيد المعاملة (Commit) لحفظ التغييرات نهائيًا
            session.commit()
            print(f"تم بنجاح إضافة المستخدم: {username}")
        except Exception as e:
            # التراجع عن التغييرات في حال حدوث أي خطأ (مثل تكرار البريد الإلكتروني)
            session.rollback()
            print(f"حدث خطأ أثناء الإدخال: {e}")

# تنفيذ الدالة لإضافة بيانات تجريبية
create_user("geek_coder", "coder@sitegeeky.com")
create_user("python_pro", "pro@sitegeeky.com")

توضيح الكود: قمنا بإنشاء جلسة عمل مؤقتة باستخدام مدير السياق with، وهو الأسلوب الأفضل هندسيًا لأنه يمنع تسرب اتصالات الذاكرة ويغلق الجلسة تلقائيًا حتى لو حدث استثناء (Exception) داخل الكتلة البرمجية. استخدام session.rollback() عند حدوث استثناء يحمي قاعدة البيانات من الوصول إلى حالة غير متسقة (Inconsistent State) ويضمن عدم حفظ بيانات جزئية أو تالفة.

الاستعلام عن البيانات والتصفية المتقدمة

الآن بعد أن أصبحت البيانات مخزنة ومنظمة، نحتاج إلى استرجاعها وتصفيتها بناءً على شروط محددة. في الإصدار 2.0 من SQLAlchemy، أصبح أسلوب الاستعلام يعتمد على دالة select الصريحة بدلاً من الطرق القديمة التي تم إهمالها:

from sqlalchemy import select

def get_user_by_username(username: str):
    with SessionLocal() as session:
        # بناء جملة الاستعلام بشكل صريح وآمن
        stmt = select(User).where(User.username == username)
        # تنفيذ الاستعلام وجلب نتيجة واحدة أو None
        user = session.scalars(stmt).first()
        return user

# تجربة البحث
target_user = get_user_by_username("geek_coder")
if target_user:
    print(f"تم العثور على العضو: {target_user}")
else:
    print("المستخدم غير موجود.")

استخدام select() مع session.scalars() يعيد كائنات بايثون مباشرة بدلاً من صفوف بيانات خام، مما يسهل الوصول إلى خصائص الكائن مثل target_user.email بشكل مباشر ونظيف يتماشى مع مفاهيم البرمجة كائنية التوجه.

العلاقات المعقدة بين الجداول (Relationships)

قواعد البيانات الحقيقية لا تتكون من جدول واحد، بل تعتمد على علاقات ربط متعددة مثل One-to-Many أو Many-to-Many. لنفترض أن كل مستخدم في موقعنا يمكن أن يمتلك عدة مقالات (Posts). سنقوم بتحديث هيكل البيانات لربط الجدولين ببعضهما عبر المفاتيح الخارجية (Foreign Keys):

from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship

class Post(Base):
    __tablename__ = "posts"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    title: Mapped[str] = mapped_column(String(200), nullable=False)
    content: Mapped[str] = mapped_column(String, nullable=False)
    user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"))

    # إنشاء علاقة عكسية للوصول إلى صاحب المقال بسهولة
    author: Mapped["User"] = relationship(back_populates="posts")

    def __repr__(self) -> str:
        return f"Post(title={self.title!r})"

# تحديث نموذج المستخدم ليشمل قائمة المقالات
User.posts = relationship("Post", back_populates="author", cascade="all, delete-orphan")

# إعادة إنشاء الجداول لتطبيق التعديلات الهيكلية
Base.metadata.create_all(engine)

إضافة الخاصية cascade="all, delete-orphan" تعني أنه إذا تم حذف مستخدم معين من قاعدة البيانات، فإن النظام سيقوم تلقائيًا بحذف جميع المقالات المرتبطة به، مما يمنع وجود بيانات يتيمة (Orphan Data) بلا مرجع سليم داخل قاعدة البيانات ويحافظ على نظافة الجداول.

أخطاء شائعة وممارسات خاطئة يجب تجنبها

عند التعامل مع SQLite و SQLAlchemy في بيئات الإنتاج، يقع المطورون أحيانًا في مطبات هندسية تؤثر على أداء واستقرار التطبيق:

  • إهمال إدارة الجلسات (Session Leakage): فتح الجلسات دون إغلاقها يؤدي بمرور الوقت إلى استنزاف موارد الاتصال المتاحة لقاعدة البيانات، خاصة في التطبيقات الكبيرة أو الخوادم السحابية التي تستقبل آلاف الطلبات.
  • تجاهل استخدام الفهارس (Indexes): عندما يتجاوز عدد السطور في الجدول آلاف السطور، تصبح عمليات البحث بـ WHERE بطيئة للغاية إذا لم تقم بتحديد أعمدة البحث المتكررة كـ index=True، مما يتسبب في إجراء عملية مسح كامل للجدول (Full Table Scan).
  • التعامل مع ملفات SQLite في بيئات التوزيع الموزعة: SQLite خيار ممتاز للتطبيقات المحلية، السكربتات، والمواقع ذات حركة المرور الخفيفة والمتوسطة. لكن إذا كان تطبيقك سيعمل على عدة خوادم متزامنة (Horizontal Scaling) تكتب جميعها على نفس ملف قاعدة البيانات، فستواجه أخطاء قفل الملف (Database is locked). في هذه الحالة، يجب الانتقال إلى خادم مستقل مثل PostgreSQL أو MySQL.

خلاصة الدرس

التعامل الاحترافي مع قواعد البيانات عبر SQLAlchemy يحرر المطور من قيود كتابة استعلامات SQL اليدوية المعقدة ويمنحه نظام تحكم قوي وآمن يعتمد على كائنات بايثون الصرفة. من خلال بناء نماذج دقيقة، إدارة جلسات آمنة، وفهم كيفية ربط الجداول والعلاقات، تصبح قادرًا على هندسة طبقة بيانات متينة لأي تطبيق برمجي قادم. في الدرس القادم، سنبني على هذه القاعدة لنرى كيف ندمج طبقة البيانات هذه مع أطر عمل الويب المتقدمة.

الكاتب
عمرو جمال

محرر متخصص في تطوير قوالب بلوجر وووردبريس وتقديم شروحات التقنية. أقدم حلولاً مبتكرة لتصميم مواقع عصرية وجذابة.