ویژگی تصویر

کتابخانه sqlalchemy در پایتون

  /  پایتون   /  کتابخانه sqlalchemy در پایتون
بنر تبلیغاتی الف

کتابخانهٔ SQLAlchemy در پایتون، یک ابزار قدرتمند برای کار با پایگاه‌داده‌های رابطه‌ای است. این کتابخانه هم به عنوان «اکوسیستم SQL» (SQL Toolkit) و هم به عنوان «نگاشت آبجکت-رابطه‌ای» (ORM) شناخته شده است.

در این مقاله به بررسی کامل SQLAlchemy می‌پردازیم؛ با تمرکز بر مفاهیم برنامه‌نویسی، منطق پشت ماژول‌ها، و بهترین شیوه‌ها (best practices). این مطلب به زبان فارسی ارائه شده و برای کسانی که با پایتون و پایگاه‌داده آشنا هستند مناسب است.

چرا SQLAlchemy؟

قبل از ورود به جزئیات، بیایید ببینیم چرا SQLAlchemy انتخاب خوبی است:

  • کتابخانه‌ای سطح بالا و با قدرت بالا برای کار با SQL در پایتون.
  • قابلیت «بی‌نیازی از دیالکت» (dialect-agnostic) دارد؛ یعنی شما می‌توانید با SQLite، PostgreSQL، MySQL یا دیگر پایگاه داده‌ها کار کنید بدون اینکه منطق اصلی تغییر کند.
  • ساختار دوگانه دارد: Core و ORM، که به شما امکان می‌دهد بسته به نیازتان سطح انتزاع را انتخاب کنید.

بنابراین اگر بخواهید پروژه‌ای بنویسید که پایگاه داده داشته باشد، استفاده از SQLAlchemy می‌تواند هم خوانایی کد را بالا ببرد و هم انعطاف‌پذیری بیشتری ارائه دهد.

ساختار کلی SQLAlchemy

مهم‌ترین نکته این است که SQLAlchemy دو لایه دارد:

  • Core: لایه‌ای نزدیک به SQL واقعی، با «عبارات SQL» (SQL Expression Language) و متادیتا و جدول‌ها.
  • ORM (Object-Relational Mapper): لایه‌ای سطح بالا که کلاس‌های پایتون را به جداول پایگاه‌داده نگاشت می‌کند، و عملیات پایگاه‌داده را به صورت شیء‌گرا امکان‌پذیر می‌سازد.

در واقع، ORM روی Core بنا شده است؛ یعنی با ORM نیز در عمق، SQL تولید می‌شود اما شما از سطح بالاتر کار می‌کنید.

نصب و راه‌اندازی اولیه

برای شروع کافی است SQLAlchemy را نصب کنید:

pip install sqlalchemy

بعد از نصب می‌توان نسخه را نیز بررسی کرد:

import sqlalchemy  
print(sqlalchemy.__version__)

اگر خروجی نشان‌دهنده نسخه باشد، نصب موفق بوده است.

سپس، برای اتصال به پایگاه‌داده، از «engine» استفاده می‌کنیم، برای مثال:

from sqlalchemy import create_engine  
engine = create_engine("sqlite:///mydatabase.db")  
connection = engine.connect()

در این مثال یک فایل SQLite به نام mydatabase.db ساخته یا باز می‌شود.

کار با Core: ایجاد جدول و دستورات پایه

تعریف جدول‌ها و متادیتا

در Core، ابتدا شیئی «MetaData» می‌سازیم، سپس جداول را تعریف می‌کنیم:

from sqlalchemy import MetaData, Table, Column, Integer, String  
metadata = MetaData()  
users = Table(  
    'users', metadata,  
    Column('id', Integer, primary_key=True),  
    Column('name', String),  
    Column('email', String, unique=True)  
)  
metadata.create_all(engine)

در اینجا:

  • متادیتا برای نگهداری اطلاعات جدول‌ها است.
  • جدول users تعریف شده با سه ستون: id، name، email.
  • در انتها با create_all(engine) جدول در پایگاه‌داده ساخته می‌شود.

گرفتن و درج داده‌ها (CRUD)

برای درج رکورد:

from sqlalchemy import insert  
query = insert(users).values(name="Ali", email="ali@example.com")  
result = connection.execute(query)

برای انتخاب رکوردها:

from sqlalchemy import select  
query = select(users)  # معادل SELECT * FROM users  
result = connection.execute(query)  
for row in result:  
    print(row)

برای فیلتر کردن:

from sqlalchemy import select  
query = select(users).where(users.c.name == "Ali")  
result = connection.execute(query)  
print(result.fetchall())

در این مثال‌ها:

  • insert برای درج یک رکورد استفاده شده است.
  • select برای انتخاب رکوردها.
  • where برای شرط‌ها.

مزایا و نکات کلیدی Core

  • کنترل دقیق‌تر روی SQL: زمانیکه نیاز دارید queryهای خاص بنویسید، Core انعطاف‌پذیر است.
  • مناسب برای تیم‌های دیتابیس-محور یا زمانی که می‌خواهید بهینه‌سازی دقیق انجام دهید.
  • اما از طرف دیگر، نیاز به نوشتن بیشتر کد دارد نسبت به ORM.

کار با ORM: نگاشت کلاس-ها و مدل‌ها

تعریف مدل‌ها

در ORM، یک کلاس پایتون تعریف می‌شود و به جدول پایگاه داده نگاشت می‌گردد. برای مثال:

from sqlalchemy.orm import declarative_base, Session  
from sqlalchemy import Column, Integer, String, create_engine  

Base = declarative_base()

class User(Base):
**tablename** = 'users'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
email = Column(String, unique=True)

engine = create_engine("sqlite:///orm_example.db")
Base.metadata.create_all(engine)

session = Session(engine)
new_user = User(name="Sara", email="[sara@example.com](mailto:sara@example.com)")
session.add(new_user)
session.commit()

شرح:

  • Base = declarative_base() پایگاه تعریف مدل‌ها است.
  • کلاس User با __tablename__ تعیین می‌کند که نام جدول users باشد.
  • ستون‌ها با Column تعریف شده‌اند.
  • بعد از ایجاد engine، با Base.metadata.create_all(engine) جدول ساخته می‌شود.
  • سپس یک session باز می‌کنیم، شیء جدید ایجاد، به session اضافه و commit می‌کنیم.

خواندن، به‌روزرسانی و حذف در ORM

خواندن:

user = session.query(User).filter_by(name="Sara").first()  
print(user.email)

به‌روزرسانی:

user.email = "sara_new@example.com"  
session.commit()

حذف:

session.delete(user)  
session.commit()

این‌ها روش‌های معمول CRUD با ORM هستند.

مزایا و نکات کلیدی ORM

  • خوانایی بیشتر: کد پایتون-محور نوشته می‌شود، نه SQL خام.
  • سرعت توسعه: مخصوصاً برای پروژه‌های کوچک تا متوسط، ORM سرعت توسعه را بالا می‌برد.
  • روابط (relationships) را به‌شکل آسان‌تر می‌توانید مدل کنید (مثل one-to-many، many-to-many).
  • اما نکته: برای پروژه‌های خیلی پیچیده با بهینه‌سازی شدید، ممکن است لازم شود به Core رجوع کنید.

بهترین شیوه‌ها (Best Practices) در استفاده از SQLAlchemy

  • استفاده از Session ‌به‌صورت درست: هر زمان که تغییری ایجاد می‌کنید، session.commit() لازم است. بهتر است یک session برای هر «کار واحد» (unit of work) داشته باشید، نه استفادهٔ طولانی چندین کار.
  • مدیریت اتصال (engine) مناسب: engine را می‌بایست یکبار ساخت و در سراسر برنامه استفاده کرد، نه اینکه هر بار اتصال جدید بسازید.
  • مدل‌سازی روابط به‌درستی: مثلا اگر یک رابطه‌ی many-to-many دارید، از Table کمکی (association table) استفاده کنید.
  • اجتناب از نوشتن SQL خام: اگر هدف سطح بالا است، از ORM یا Expression Language استفاده کنید تا انعطاف‌پذیری حفظ شود.
  • پیشگیری از N+1 query: زمانی که روابط دارید، از lazy loading آگاه باشید یا explicit join انجام دهید.
  • انجام به‌روزرسانی عمده (bulk) با احتیاط: در ORM به‌روزرسانی دسته‌ای ممکن است منجر به رفتار متفاوت شود؛ گاهی Core بهتر است.
  • انتقال بین پایگاه داده‌ها: اگر زمانی نیاز به مهاجرت بین SQLite و PostgreSQL داشتید، هدف SQLAlchemy همین است؛ پس طراحی را مستقل از پایگاه داده نگهدارید.

مقایسهٔ Core vs ORM

ویژگیCoreORM
سطح انتزاعپایین‌تر – نزدیک به SQLبالا – کد شیءگرا
خوانایی برای توسعه سریعمتوسطبالاتر
کنترل بر SQL تولید شدهبسیار زیادکمی محدودتر
مدل‌سازی روابط پیچیدهباید دستی‌تر کار شودساده‌تر با کلاس‌ها
بهینه‌سازی‌های دقیقمناسب‌تردر شرایط خاص مناسب نیست

مثال کامل: ترکیب Core و ORM

فرض کنید می‌خواهیم دو جدول: کاربران (User) و مقالات (Article) داشته باشیم، با رابطه «یک به چند» (یک کاربر چند مقاله دارد).

from sqlalchemy import create_engine, Column, Integer, String, ForeignKey  
from sqlalchemy.orm import declarative_base, relationship, Session  

Base = declarative_base()

class User(Base):
**tablename** = 'users'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
articles = relationship("Article", back_populates="author")

class Article(Base):
**tablename** = 'articles'
id = Column(Integer, primary_key=True)
title = Column(String, nullable=False)
content = Column(String)
user_id = Column(Integer, ForeignKey('users.id'))
author = relationship("User", back_populates="articles")

engine = create_engine("sqlite:///blog.db", echo=True)
Base.metadata.create_all(engine)

with Session(engine) as session:
user = User(name="Reza")
article1 = Article(title="اولین مقاله", content="محتوا …", author=user)
session.add(user)
session.add(article1)
session.commit()

```
# خواندن  
rez_user = session.query(User).filter_by(name="Reza").first()  
for art in rez_user.articles:  
    print(art.title)

“`

شرح مطلب:

  • کلاس User و Article تعریف شده‌اند و رابطهٔ دوطرفه با relationship برقرار گردیده است.
  • ForeignKey روی user_id در Article وجود دارد تا ارتباط با users.id برقرار شود.
  • در engine مقدار echo=True برای چاپ SQL تولید شده است (برای اهداف آموزشی).
  • سپس در session، یک کاربر و یک مقاله ایجاد، اضافه و commit شدند.
  • با query رشته را خواندیم و رابطهٔ articles چاپ شد.

نکات پیشرفته و بهینه‌سازی

در پروژه‌های بزرگ‌تر، به نکات زیر توجه کنید:

  • شاخص‌ها (Indexes): برای ستون‌هایی که مرتباً فیلتر می‌شوند، ایندکس بگذارید.
  • بارگذاری بهینهٔ رابطه‌ها: از «joined loading» یا «selectin loading» استفاده کنید تا N+1 query نشود.
  • مهاجرت‌های اسکیمای پایگاه‌داده: به کمک ابزارهایی مانند Alembic، اسکیمای بانک را به صورت کنترل‌شده تغییر دهید.
  • پیش‌بینی تایپ‌ها (Type Hints) و پشتیبانی از async: در نسخه‌های جدید SQLAlchemy (مثلاً ۲.۰) از امکانات مدرن پایتون بهره‌مند شده است.
  • پایداری تراکنش‌ها (Transactions): از session.begin() یا context-manager برای تضمین rollback در صورت خطا استفاده کنید.

جمع‌بندی

در این مقاله، با SQLAlchemy آشنا شدیم، ساختار Core و ORM را بررسی کردیم، بهترین شیوه‌ها را مرور نمودیم و مثالی عملی از تعریف رابطه‌ها ارائه کردیم. هر پروژه بنابر نیازش ممکن است یکی از دو سطح را انتخاب کند یا ترکیبی استفاده کند.

آیا این مطلب برای شما مفید بود ؟

خیر
بله
موضوعات شما در انجمن: