تكرار السؤال: …
الإجابة المختصرة
الـ Session تطبق نمط Unit of Work وIdentity Map: تتتبع الكائنات المحمّلة والمعدلة وتكتبها كلها عند flush أو commit داخل معاملة. يجب أن تعيش الجلسة لطلب أو مهمة واحدة فقط لأنها ليست thread-safe وتحتفظ بحالة.
الشرح
يضمن الـ Identity Map وجود كائن واحد لكل صف داخل الجلسة. تُنهي commit المعاملة وتجعل الكائنات منتهية الصلاحية (expire_on_commit) فيُعاد تحميلها عند الوصول التالي. الجلسة العامة المشتركة بين الطلبات تسبب تسرب بيانات وأخطاء تزامن. في SQLAlchemy 2.0 أصبحت الصيغة الموحدة select() مع session.execute و scalars، والـ Mapped و mapped_column للأنواع. وفي الكود غير المتزامن استخدم AsyncSession وانتبه أن التحميل الكسول للعلاقات لا يعمل ضمنيًا، فحمّلها بـ selectinload.
مثال بالكود
from sqlalchemy import select
from sqlalchemy.orm import Session, selectinload
def rename_user(engine, user_id: int, name: str) -> None:
with Session(engine) as session, session.begin(): # commit أو rollback تلقائيًا
user = session.get(User, user_id)
user.name = name # يُتتبع تلقائيًا
def list_authors(session: Session):
stmt = select(Author).options(selectinload(Author.books)).where(Author.active)
return session.scalars(stmt).all()أخطاء شائعة
- استخدام جلسة واحدة عامة على مستوى التطبيق لكل الطلبات.
- الوصول إلى علاقة كسولة بعد إغلاق الجلسة فيُرمى DetachedInstanceError.