FA-TOOLS — Header Component
آموزش ORM در پایتون — SQLAlchemy کامل

آموزش ORM در پایتون — SQLAlchemy کامل

رفیق برنامه‌نویس! اگه دنبال ابزارهای خفن و آماده پایتون می‌گردی که پروژه‌هات رو مثل موشک پرتاب کنی، دیگه وقتشه یه سر به فروشگاه ابزارهای آماده ما بزنی. از اسنیپت‌های جادویی پایتون گرفته تا کد‌های HTML و CSS، همه چی اونجاست تا کارت رو راه بندازه.

برای مشاوره یا راهنمایی، فقط کافیه زنگ بزنی: 09202232789

✨ نقشه راه سریع به دنیای SQLAlchemy ✨

💡 ORM چیه؟

پل بین کد پایتون و دیتابیس رابطه‌ای.

🚀 چرا SQLAlchemy؟

انعطاف، قدرت و کارایی بالا در مدیریت داده‌ها.

🛠️ نصب و راه‌اندازی

با pip ساده و سریع شروع کن.

⚙️ هسته (Core) vs ORM

انتخاب بین کنترل کامل SQL یا انتزاع شی‌گرا.

🔄 CRUD عملیات

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

🔗 رابطه‌ها

مدیریت ارتباط بین جداول (یک-به-چند، چند-به-چند).

🚨 عیب‌یابی

راه‌حل‌های مشکلات رایج در SQLAlchemy.

❓ سوالات متداول

پاسخ به ابهامات کلیدی در مورد SQLAlchemy.

رفقا، اگه تا حالا با دیتابیس‌ها ور رفتید، حتماً می‌دونید که کار با SQL خام و کوئری‌های گیج‌کننده می‌تونه چقدر زمان‌بر و خطاساز باشه. اینجا دقیقا جاییه که ORM (Object-Relational Mapping) وارد بازی میشه و زندگی ما برنامه‌نویس‌ها رو نجات میده. تو این مقاله قراره با هم بریم سراغ یکی از قوی‌ترین و محبوب‌ترین ORM‌ها تو دنیای پایتون، یعنی SQLAlchemy و قدم به قدم یاد بگیریم چطور باهاش کار کنیم.

ORM چیه و چرا اصلا بهش نیاز داریم؟

آموزش ORM در پایتون — SQLAlchemy کامل — تصویر 1

تصور کن توی پایتون کلی کلاس و آبجکت داری که هر کدومشون کلی ویژگی و رفتار دارن. حالا اگه بخوای این آبجکت‌ها رو توی یه دیتابیس رابطه‌ای (مثل PostgreSQL، MySQL یا SQLite) ذخیره کنی، باید هی بشینی کد SQL بنویسی که آبجکت رو به سطر تبدیل کنه و برعکس. خب این فرآیند پر از تکراره و ممکنه کلی اشتباه توش پیش بیاد.

ORM میاد اینجا نقش یک مترجم رو بازی می‌کنه. در واقع، ORM یه پل ارتباطی بین دنیای آبجکت‌گرای پایتون و دنیای جدول‌ها و سطرهای دیتابیسه. با استفاده از ORM، می‌تونی با آبجکت‌های پایتون کار کنی، متدهاشون رو صدا بزنی و ORM خودش این کارها رو به دستورات SQL ترجمه می‌کنه. یعنی به جای نوشتن `SELECT * FROM users WHERE id = 1;`، می‌نویسی `session.query(User).filter_by(id=1).first()` و بوم!

مزایای استفاده از ORM:

  • کاهش کدنویسی تکراری: دیگه لازم نیست کلی SQL خام بنویسی.
  • افزایش خوانایی: کد پایتون آبجکت‌گراتر و قابل فهم‌تر میشه.
  • امنیت بیشتر: ORM‌ها معمولاً از حملات SQL Injection جلوگیری می‌کنن.
  • استقلال از دیتابیس: می‌تونی به راحتی دیتابیس رو عوض کنی بدون اینکه کل کدت رو بازنویسی کنی.

چرا SQLAlchemy؟ قهرمان ORMهای پایتون

آموزش ORM در پایتون — SQLAlchemy کامل — تصویر 2

پایتون ORMهای مختلفی داره (مثل Django ORM، Peewee و …)، اما SQLAlchemy یه چیز دیگه است. این کتابخونه دو تا بخش اصلی داره:

  • SQLAlchemy Core: این بخش بهت اجازه میده کوئری‌های SQL رو به صورت پایتونی و ساختاریافته بسازی. یه جورایی SQL رو با پایتون می‌نویسی اما با کنترل کامل.
  • SQLAlchemy ORM: این همون بخشیه که بهت اجازه میده دیتابیس رو مثل آبجکت‌های پایتون ببینی و باهاشون کار کنی.

SQLAlchemy به خاطر انعطاف‌پذیری فوق‌العاده، قدرت بالا در مدیریت ترنزاکشن‌ها و رابطه‌ها، و کارایی (Performance) عالی‌اش، توی پروژه‌های بزرگ و جدی خیلی استفاده میشه. فرقی نمیکنه دیتابیست SQLite باشه یا PostgreSQL یا Oracle، SQLAlchemy باهاش کنار میاد.

نصب SQLAlchemy: بریم شروع کنیم!

آموزش ORM در پایتون — SQLAlchemy کامل — تصویر 3

اولین قدم مثل همیشه نصب پکیج با `pip` هست. مطمئن شو که یه محیط مجازی (virtual environment) فعال داری:


pip install SQLAlchemy

اگه می‌خوای با دیتابیس‌های خاصی کار کنی (مثلاً PostgreSQL یا MySQL)، باید درایورهای مربوطه رو هم نصب کنی. مثلاً برای PostgreSQL:


pip install psycopg2-binary
# یا برای MySQL
pip install pymysql

برای یاد‌گیری بیشتر در مورد پکیج‌ها و اسنیپت‌های پایتون، می‌تونی یه سر به کدهای آماده پایتون ما بزنی.

مفاهیم کلیدی در SQLAlchemy ORM

قبل از اینکه بریم سراغ کد، چند تا مفهوم مهم رو با هم مرور کنیم:

  • Engine: نقطه شروع ارتباط با دیتابیسه. موتور دیتابیس رو مشخص می‌کنه (مثلاً SQLite, PostgreSQL) و مسئول برقراری اتصال‌هاست.
  • Base (Declarative Base): کلاسیه که تمام مدل‌های ORM ما ازش ارث‌بری می‌کنن. این کلاس به SQLAlchemy میگه که مدل‌های ما قراره به چه جدول‌هایی در دیتابیس نگاشت بشن.
  • Model (Table Class): هر کلاس پایتونی که از `Base` ارث‌بری کنه، نماینده یک جدول در دیتابیس و هر نمونه (instance) از اون کلاس، نماینده یک سطر در اون جدوله.
  • Session: شیء اصلی برای تعامل با دیتابیسه. تمام عملیات CRUD (ایجاد، خواندن، به‌روزرسانی، حذف) از طریق سشن انجام میشه. سشن یک واحد کاریه که تغییرات رو ردیابی می‌کنه و اجازه میده چندین عملیات رو با هم کامیت (commit) یا رول‌بک (rollback) کنی.

عملیات CRUD با SQLAlchemy ORM (قدم به قدم)

قدم اول: اتصال به دیتابیس و تعریف مدل‌ها

بریم یه مثال ساده با دیتابیس SQLite بزنیم. اول باید Engine رو بسازیم و Base رو تعریف کنیم.


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

# 1. ساخت Engine (اتصال به دیتابیس SQLite در یک فایل)
# echo=True یعنی کوئری‌های SQL تولید شده رو در کنسول نشون بده. خیلی بدردبخوره برای دیباگ!
engine = create_engine('sqlite:///my_database.db', echo=True)

# 2. تعریف Base (مدل‌های ما از این ارث‌بری می‌کنن)
Base = declarative_base()

# 3. تعریف مدل‌ها (کلاس‌هایی که نماینده جداول دیتابیس هستن)
class User(Base):
    __tablename__ = 'users' # نام جدول در دیتابیس

    id = Column(Integer, primary_key=True)
    name = Column(String(50), nullable=False)
    email = Column(String(100), unique=True, nullable=False)

    # تعریف رابطه: یک کاربر می‌تونه چندین پست داشته باشه
    posts = relationship("Post", back_populates="author", cascade="all, delete-orphan")

    def __repr__(self):
        return f""

class Post(Base):
    __tablename__ = 'posts' # نام جدول در دیتابیس

    id = Column(Integer, primary_key=True)
    title = Column(String(100), nullable=False)
    content = Column(String)
    user_id = Column(Integer, ForeignKey('users.id')) # کلید خارجی برای لینک به جدول کاربران

    # تعریف رابطه: هر پست یک نویسنده (کاربر) داره
    author = relationship("User", back_populates="posts")

    def __repr__(self):
        return f""

# 4. ایجاد جداول در دیتابیس (بر اساس مدل‌هایی که تعریف کردیم)
Base.metadata.create_all(engine)

# 5. ساخت Session (برای تعامل با دیتابیس)
Session = sessionmaker(bind=engine)

قدم دوم: اضافه کردن داده (Create)

حالا که مدل‌هامون رو داریم، می‌تونیم آبجکت بسازیم و اونا رو به دیتابیس اضافه کنیم.


session = Session()

# ساخت آبجکت‌های User
user1 = User(name='علی احمدی', email='ali.ahmadi@example.com')
user2 = User(name='سارا محمدی', email='sara.mohammadi@example.com')

# اضافه کردن به سشن
session.add(user1)
session.add(user2)

# اضافه کردن پست‌ها
post1 = Post(title='مقدمه‌ای بر پایتون', content='پایتون یه زبان برنامه‌نویسی عالیه!', author=user1)
post2 = Post(title='SQLAlchemy برای مبتدیان', content='با ORM‌ها راحت‌تر کد بزن.', author=user1)
post3 = Post(title='رازهای طراحی وب', content='بهینه‌سازی برای موبایل و دسکتاپ.', author=user2)

session.add_all([post1, post2, post3])

# کامیت کردن تغییرات به دیتابیس
session.commit()
print("داده‌ها با موفقیت اضافه شدند.")

session.close() # همیشه سشن رو ببند

قدم سوم: خواندن داده‌ها (Read)

برای خوندن داده‌ها، از متد `query()` سشن استفاده می‌کنیم.


session = Session()

# 1. خواندن همه کاربران
print("n--- همه کاربران ---")
users = session.query(User).all()
for user in users:
    print(user)

# 2. خواندن یک کاربر بر اساس ID
print("n--- کاربر با ID = 1 ---")
user_by_id = session.query(User).get(1) # get فقط با primary key کار می‌کنه
if user_by_id:
    print(user_by_id)

# 3. خواندن کاربران با فیلتر (مثلاً بر اساس نام)
print("n--- کاربران با نام 'علی احمدی' ---")
ali_users = session.query(User).filter_by(name='علی احمدی').all()
for user in ali_users:
    print(user)

# 4. خواندن پست‌های یک کاربر خاص
print("n--- پست‌های علی احمدی ---")
ali_posts = session.query(Post).join(User).filter(User.name == 'علی احمدی').all()
for post in ali_posts:
    print(post)

# 5. خواندن با فیلتر پیچیده‌تر (استفاده از `and_`, `or_`, `like`)
from sqlalchemy import or_
print("n--- کاربران با نام 'علی' یا ایمیل شامل 'sara' ---")
filtered_users = session.query(User).filter(
    or_(User.name.like('%علی%'), User.email.like('%sara%'))
).all()
for user in filtered_users:
    print(user)

session.close()

قدم چهارم: به‌روزرسانی داده‌ها (Update)

برای آپدیت کردن، اول آبجکت مورد نظرت رو از دیتابیس بخون، تغییرات رو اعمال کن و بعد `commit()` کن.


session = Session()

# پیدا کردن کاربر با ID = 1
user_to_update = session.query(User).get(1)

if user_to_update:
    user_to_update.name = 'علی رضایی' # تغیر نام
    user_to_update.email = 'ali.rezaei@example.com'
    session.commit() # کامیت تغییرات
    print(f"کاربر {user_to_update.id} به‌روزرسانی شد: {user_to_update.name}")
else:
    print("کاربر پیدا نشد.")

session.close()

قدم پنجم: حذف داده‌ها (Delete)

برای حذف هم مشابه به‌روزرسانی، آبجکت رو پیدا کن و با `session.delete()` حذفش کن.


session = Session()

# پیدا کردن کاربری که می‌خواهیم حذف کنیم (مثلاً کاربر با ID = 2)
user_to_delete = session.query(User).get(2)

if user_to_delete:
    session.delete(user_to_delete)
    session.commit()
    print(f"کاربر {user_to_delete.name} با موفقیت حذف شد.")
else:
    print("کاربر برای حذف پیدا نشد.")

session.close()

یادت باشه، به خاطر `cascade=”all, delete-orphan”` که توی رابطه `User` تعریف کردیم، وقتی یک `User` حذف میشه، تمام `Post`های مربوط به اون هم خودکار حذف میشن. این یک امکان قدرتمند برای مدیریت روابطه.

مقایسه SQLAlchemy Core و ORM

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

مقایسه SQLAlchemy Core و SQLAlchemy ORM
SQLAlchemy Core SQLAlchemy ORM
  • سطح پایین‌تر: کنترل کامل بر روی SQL
  • کارایی بالا: بهینه برای کوئری‌های پیچیده و حجیم
  • انعطاف‌پذیری: عالی برای کوئری‌های داینامیک و خاص
  • یادگیری سخت‌تر: نیاز به درک عمیق‌تر SQL
  • نمونه استفاده: ابزارهای گزارش‌گیری، ETL، کوئری‌های سنگین
  • سطح بالاتر: کار با آبجکت‌های پایتون
  • راحتی توسعه: سریع‌تر و با خطای کمتر برای CRUD
  • مدیریت رابطه: پشتیبانی قدرتمند از روابط بین جداول
  • انتزاع بیشتر: SQL تولید شده توسط ORM است
  • نمونه استفاده: اپلیکشن‌های وب (مانند Flask، FastAPI)، APIها

عیب‌یابی سریع (Troubleshooting)

توی مسیر کار با SQLAlchemy، ممکنه با چند تا مشکل رایج روبرو بشی. اینجا چند تا از اونا و راه‌حل‌هاشون رو با هم بررسی می‌کنیم:

1. مشکل: `No such table: `

علت: این ارور یعنی SQLAlchemy نتونسته جدول مورد نظر رو توی دیتابیس پیدا کنه. معمولاً دو دلیل داره:

  • شاید فراموش کردی `Base.metadata.create_all(engine)` رو صدا بزنی تا جداول ایجاد بشن.
  • یا اسم جدول توی `__tablename__` مدل با اسم جدول توی دیتابیس همخونی نداره.
  • ممکنه فایل دیتابیس (مثلاً `my_database.db`) رو پاک کرده باشی ولی فکر کنی جداول هنوز هستن.

راه‌حل: مطمئن شو که `Base.metadata.create_all(engine)` بعد از تعریف تمام مدل‌ها و قبل از اولین تعامل با دیتابیس اجرا شده. همچنین `__tablename__` رو دقیق چک کن. اگه دیتابیس رو دستی ساختید، مطمئن باشید نام‌ها دقیقاً مطابقت دارند.

2. مشکل: `PendingRollbackError` یا `IntegrityError`

علت: این ارورها معمولاً وقتی پیش میان که توی یه ترنزاکشن، یه مشکلی پیش اومده (مثلاً یه فیلد `NOT NULL` رو خالی گذاشتی، یا یه `unique` فیلد رو تکراری وارد کردی) و `session` توی وضعیت خطا گیر کرده.

  • `IntegrityError`: نقض قوانین دیتابیس مثل `NOT NULL`، `UNIQUE` یا کلید خارجی.
  • `PendingRollbackError`: سشن قبلی به دلیل ارور کامیت نشده و در حالت Rollback معلق مونده.

راه‌حل:

  • برای `IntegrityError`، داده‌هایی رو که داری وارد می‌کنی چک کن که با تعریف مدل و محدودیت‌های دیتابیس (مثل `nullable=False`, `unique=True`) مطابقت داشته باشن.
  • برای `PendingRollbackError`، همیشه بعد از هر خطا در سشن، `session.rollback()` رو فراخوانی کن تا سشن به حالت تمیز برگرده.
    
            try:
                # کدهای عملیات دیتابیس
                session.add(some_object)
                session.commit()
            except Exception as e:
                session.rollback() # برگشت به وضعیت قبل از خطا
                print(f"خطا: {e}")
            finally:
                session.close()
            

3. مشکل: مدیریت Session

علت: فراموش کردن `session.close()` یا مدیریت نادرست سشن‌ها در اپلیکیشن‌های بزرگ می‌تونه باعث مشکلاتی مثل deadlock یا مصرف بیش از حد منابع دیتابیس بشه.

راه‌حل: همیشه سشن رو بعد از اتمام کار ببند. بهترین روش استفاده از `try…except…finally` یا استفاده از `context manager` برای سشن‌هاست.


    # تعریف یک تابع برای گرفتن سشن که خودش بسته میشه
    from contextlib import contextmanager

    @contextmanager
    def get_session():
        s = Session()
        try:
            yield s
            s.commit()
        except Exception:
            s.rollback()
            raise
        finally:
            s.close()

    # استفاده از سشن
    with get_session() as session:
        new_user = User(name="مریم رستمی", email="maryam.rostami@example.com")
        session.add(new_user)
        # نیازی به session.commit() یا session.close() نیست، خودش مدیریت میشه
    

سوالات متداول (FAQ)

Q: آیا SQLAlchemy فقط برای دیتابیس‌های رابطه‌ای کار می‌کنه؟

A: بله، SQLAlchemy یک ORM برای دیتابیس‌های رابطه‌ای (SQL) مثل PostgreSQL, MySQL, SQLite, Oracle و SQL Server هست. برای دیتابیس‌های NoSQL، باید از کتابخانه‌های مخصوص خودشون استفاده کنی.

Q: چطور میشه روابط پیچیده (مثلاً Many-to-Many) رو توی SQLAlchemy مدیریت کرد؟

A: SQLAlchemy با استفاده از یک جدول واسط (Association Table) به خوبی از روابط Many-to-Many پشتیبانی می‌کنه. کافیه این جدول رو تعریف کنی و بعد توی مدل‌ها، از `secondary` برای لینک دادن به این جدول استفاده کنی.

Q: آیا برای هر کوئری جدید باید یه سشن جدید بسازم؟

A: نه لزوماً. یک سشن معمولاً برای یک واحد کاری (Unit of Work) استفاده میشه، که می‌تونه شامل چندین عملیات `add`, `update`, `delete` و `query` باشه. در اپلیکیشن‌های وبی مثل Flask یا FastAPI، معمولاً یک سشن برای هر درخواست HTTP ایجاد میشه و بعد از اتمام درخواست بسته میشه.

Q: آیا SQLAlchemy بهینه است؟ آیا کندتر از SQL خام نیست؟

A: SQLAlchemy یکی از بهینه‌ترین ORM‌هاست و در بسیاری از موارد، افت کارایی نسبت به SQL خام ناچیزه. در سناریوهای خیلی خاص و پیچیده که نیاز به بهینه‌سازی حداکثری داری، می‌تونی از SQLAlchemy Core یا حتی SQL خام برای اون بخش استفاده کنی. ابزارهای اسنیپت هم میتونن در بهینه‌سازی و استفاده از کدهای آماده کمکت کنن.

رفقای عزیز، امیدوارم این مقاله یه دید کامل و عملیاتی در مورد SQLAlchemy بهتون داده باشه. یادتون باشه که تسلط بر ORM‌ها، به‌خصوص SQLAlchemy، یه مهارت کلیدیه که می‌تونه سرعت توسعه پروژه‌هاتون رو چندین برابر کنه و کدتون رو هم خواناتر و قابل نگهداری‌تر کنه. اگه به دنبال کدهای آماده و اسنیپت‌های مفید برای توسعه پروژه‌های پایتونتون هستید، حتماً به بخش اسنیپت‌های پایتون ما یه سری بزنید. همچنین می‌تونید به اسنیپت‌های CSS، اسنیپت‌های JavaScript، اسنیپت‌های HTML و اسنیپت‌های وردپرس هم سر بزنید و از اون‌ها در پروژه‌هاتون استفاده کنید.

Table of Contents

آخرین نوشته‌ها