flask

SQLAlchemy در Flask

یک ORM (Object-Relational Mapper) قدرتمند در پایتون

مقدمه

در این سرفصل جامع، به بررسی عمیق استفاده از SQLAlchemy در فریمورک Flask خواهیم پرداخت. SQLAlchemy یک ORM (Object-Relational Mapper) قدرتمند در پایتون است که به شما امکان می‌دهد با استفاده از اشیاء پایتون با پایگاه داده‌های رابطه‌ای تعامل داشته باشید. در کنار Flask، یک ترکیب بسیار قدرتمند برای ساخت وب‌اپلیکیشن‌های پیچیده فراهم می‌کند.

بخش اول: مفاهیم پایه

  • ORM چیست و چرا از آن استفاده می‌کنیم؟

    • ORM مخفف Object-Relational Mapper است. به زبان ساده، ORM پل ارتباطی بین دنیای برنامه‌نویسی شی‌گرا (OOP) و پایگاه داده‌های رابطه‌ای (RDBMS) است. این ابزار به شما اجازه می‌دهد تا با استفاده از اشیاء و کلاس‌ها در زبان برنامه‌نویسی خود، با پایگاه داده کار کنید و دیگر نیازی به نوشتن دستورات SQL خام نداشته باشید.

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

      • سادگی و خوانایی کد:
        • به جای نوشتن کوئری‌های SQL پیچیده، شما با اشیاء و متدهای آن‌ها کار می‌کنید. این باعث می‌شود کد شما خواناتر و قابل نگهداری‌تر شود.
      • افزایش بهره‌وری:
        • ORM بسیاری از کارهای تکراری و خسته‌کننده در کار با پایگاه داده را به صورت خودکار انجام می‌دهد، مانند ایجاد و مدیریت جداول، ایجاد روابط بین جداول و … .
      • کاهش خطا:
        • با استفاده از ORM، احتمال بروز خطاهای دستوری در SQL کاهش می‌یابد، زیرا ORM بسیاری از این بررسی‌ها را به صورت خودکار انجام می‌دهد.
      • انتقال‌پذیری:
        • بسیاری از ORM‌ها از چندین پایگاه داده پشتیبانی می‌کنند. بنابراین، با تغییر پایگاه داده، نیاز به تغییرات گسترده در کد نخواهید داشت.
      • ابزارهای قدرتمند:
        • ORM‌ها معمولاً ابزارهای قدرتمندی برای مدیریت رابطه‌ها، پرس‌وجوهای پیچیده، و عملیات‌های پیشرفته بر روی داده‌ها ارائه می‌دهند.

      مثال ساده:

      فرض کنید می‌خواهید یک جدول کاربران در پایگاه داده خود داشته باشید. با استفاده از ORM، می‌توانید یک کلاس User تعریف کنید و به آن فیلدهایی مانند id, username, email و … اضافه کنید. سپس با استفاده از این کلاس، می‌توانید به راحتی کاربران جدید ایجاد کنید، اطلاعات آن‌ها را به روزرسانی کنید و یا آن‌ها را حذف کنید.

      ORM در پایتون

      در پایتون، دو ORM محبوب وجود دارد:

      • SQLAlchemy: یکی از قدرتمندترین و انعطاف‌پذیرترین ORM‌های پایتون است که از طیف گسترده‌ای از پایگاه داده‌ها پشتیبانی می‌کند.
      • Django ORM: مخصوص فریمورک Django طراحی شده است و با آن یکپارچه عمل می‌کند.

      در کل، ORM یک ابزار بسیار مفید برای توسعه‌دهندگان است که به آن‌ها اجازه می‌دهد تا با تمرکز بر منطق کسب‌وکار خود، به جای نگرانی در مورد جزئیات پایگاه داده، اپلیکیشن‌های خود را توسعه دهند.

       

    • مقایسه ORM با نوشتن SQL خام

      ORM چیست؟

      همانطور که در پاسخ قبلی توضیح دادیم، ORM (Object-Relational Mapper) یک لایه انتزاعی بین کد برنامه‌نویسی شما و پایگاه داده رابطه‌ای فراهم می‌کند. این لایه به شما اجازه می‌دهد تا با استفاده از اشیاء و کلاس‌ها در زبان برنامه‌نویسی خود، با پایگاه داده کار کنید، بدون آنکه نیاز به نوشتن دستورات SQL خام داشته باشید.

      نوشتن SQL خام چیست؟

      نوشتن SQL خام به معنای نوشتن دستورات SQL به صورت مستقیم برای برقراری ارتباط با پایگاه داده است. این روش به شما کنترل بیشتری بر روی پایگاه داده می‌دهد، اما در عین حال پیچیده‌تر و مستعد خطا است.

      مقایسه:

      ویژگی ORM نوشتن SQL خام
      سادگی ساده‌تر و خواناتر پیچیده‌تر و نیازمند دانش SQL
      بهره‌وری معمولاً کارآمد است، اما ممکن است در برخی موارد کندتر از SQL خام باشد بسیار کارآمد، اما نیاز به بهینه‌سازی دارد
      انعطاف‌پذیری ممکن است در برخی موارد انعطاف‌پذیری کمتری نسبت به SQL خام داشته باشد بسیار انعطاف‌پذیر
      امنیت معمولاً از حملات تزریق SQL جلوگیری می‌کند مستعد حملات تزریق SQL
      قابلیت نگهداری کد قابل نگهداری‌تر است کد ممکن است پیچیده و سخت نگهداری باشد
      سرعت توسعه سرعت توسعه را افزایش می‌دهد ممکن است سرعت توسعه را کاهش دهد

      چه زمانی از ORM استفاده کنیم؟

      • وقتی سرعت توسعه برایتان مهم است.
      • وقتی می‌خواهید کدتان خواناتر و قابل نگهداری‌تر باشد.
      • وقتی می‌خواهید از حملات تزریق SQL جلوگیری کنید.
      • وقتی با یک تیم بزرگ کار می‌کنید و می‌خواهید یک سبک کدنویسی یکپارچه داشته باشید.

      چه زمانی از SQL خام استفاده کنیم؟

      • وقتی به عملکرد بسیار بالایی نیاز دارید و آماده‌ی نوشتن کد پیچیده‌تری هستید.
      • وقتی می‌خواهید از ویژگی‌های خاصی از پایگاه داده استفاده کنید که توسط ORM پشتیبانی نمی‌شوند.
      • وقتی می‌خواهید کنترل کاملی بر روی پایگاه داده داشته باشید.

      جمع‌بندی

      انتخاب بین ORM و نوشتن SQL خام به عوامل مختلفی مانند اندازه پروژه، پیچیدگی پایگاه داده، تجربه تیم توسعه و اولویت‌های پروژه بستگی دارد. در بسیاری از موارد، استفاده از ORM یک انتخاب هوشمندانه است، اما در برخی موارد خاص، نوشتن SQL خام ممکن است بهترین گزینه باشد.

      به طور کلی، ORM یک ابزار قدرتمند است که می‌تواند بهره‌وری توسعه‌دهندگان را به طور قابل توجهی افزایش دهد. با این حال، درک اصول پایه‌ای SQL همچنان برای هر توسعه‌دهنده‌ای ضروری است.

       

  • معرفی SQLAlchemy

    • ویژگی‌های اصلی SQLAlchemy در Flask

      SQLAlchemy یکی از قدرتمندترین و محبوب‌ترین ORMها (Object-Relational Mappers) در پایتون است که به شما امکان می‌دهد با استفاده از اشیاء پایتون با پایگاه داده‌های رابطه‌ای تعامل داشته باشید. در ادامه به برخی از ویژگی‌های اصلی SQLAlchemy می‌پردازیم:

      ۱. انعطاف‌پذیری و قابلیت سفارشی‌سازی بالا:

      • پشتیبانی از انواع مختلف پایگاه داده: SQLAlchemy از طیف گسترده‌ای از پایگاه داده‌ها مانند PostgreSQL، MySQL، SQLite و بسیاری دیگر پشتیبانی می‌کند.
      • کنترل کامل بر روی SQL: در صورت نیاز، می‌توانید به طور مستقیم دستورات SQL را اجرا کنید و یا کوئری‌های پیچیده را با استفاده از امکانات SQLAlchemy ایجاد کنید.
      • تعریف انواع داده‌های سفارشی: می‌توانید انواع داده‌های خاص خود را تعریف کنید و از آن‌ها در مدل‌های خود استفاده کنید.

      ۲. مدیریت رابطه‌ها:

      • انواع مختلف رابطه: SQLAlchemy از انواع مختلف رابطه بین اشیاء مانند One-to-One، One-to-Many و Many-to-Many پشتیبانی می‌کند.
      • تعریف رابطه‌ها به صورت شهودی: تعریف رابطه‌ها در SQLAlchemy بسیار ساده و شهودی است.

      ۳. پرس‌وجوهای قدرتمند:

      • QueryBuilder: SQLAlchemy یک سیستم قدرتمند برای ساختن کوئری‌های پیچیده ارائه می‌دهد.
      • ORM Query: می‌توانید از زبان پرس‌وجوی ORM برای نوشتن کوئری‌هایی که به صورت طبیعی‌تر به زبان پایتون خوانده می‌شوند، استفاده کنید.

      ۴. Migration:

      • مدیریت تغییرات در ساختار پایگاه داده: SQLAlchemy به شما کمک می‌کند تا تغییرات در ساختار پایگاه داده خود را به صورت ایمن مدیریت کنید.
      • ابزار Alembic: SQLAlchemy با ابزار Alembic یکپارچه شده است که به شما امکان می‌دهد تغییرات را به صورت نسخه‌های کنترل‌شده مدیریت کنید.

      ۵. Session:

      • مدیریت تراکنش‌ها: SQLAlchemy از مفهوم Session برای مدیریت تراکنش‌ها استفاده می‌کند.
      • ردیابی تغییرات: Session تغییراتی را که در اشیاء ایجاد شده است، ردیابی می‌کند و در زمان مناسب آن‌ها را به پایگاه داده می‌نویسد.

      ۶. Event System:

      • سفارشی‌سازی رفتار SQLAlchemy: شما می‌توانید با استفاده از سیستم رویداد SQLAlchemy، رفتار پیش‌فرض آن را سفارشی‌سازی کنید.

      ۷. Performance Optimization:

      • بهینه‌سازی کوئری‌ها: SQLAlchemy ابزارهایی برای بهینه‌سازی کوئری‌ها و بهبود عملکرد برنامه ارائه می‌دهد.
      • Caching: SQLAlchemy از مکانیزم کشینگ برای بهبود عملکرد استفاده می‌کند.

      ۸. یکپارچگی با Flask:

      • Flask-SQLAlchemy: این افزونه به شما امکان می‌دهد به راحتی SQLAlchemy را با فریمورک Flask یکپارچه کنید.

      ۹. جامعه بزرگ و مستندات کامل:

      • جامعه فعال: SQLAlchemy یک جامعه بزرگ و فعال دارد که به شما کمک می‌کند تا سوالات خود را پاسخ دهید و مشکلات خود را حل کنید.
      • مستندات جامع: مستندات SQLAlchemy بسیار کامل و جامع است و به شما کمک می‌کند تا به راحتی با این ابزار کار کنید.

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

       

    • نصب و پیکربندی SQLAlchemy در Flask

      مقدمه

      SQLAlchemy یک ORM (Object-Relational Mapper) قدرتمند در پایتون است که به شما امکان می‌دهد با استفاده از اشیاء پایتون با پایگاه داده‌های رابطه‌ای تعامل داشته باشید. Flask-SQLAlchemy نیز یک افزونه برای Flask است که کار با SQLAlchemy را در محیط Flask ساده‌تر می‌کند. در این راهنما، مراحل نصب و پیکربندی SQLAlchemy در Flask را به طور کامل توضیح می‌دهیم.

      مراحل نصب

      1. ایجاد یک محیط مجازی (Virtual Environment): این کار برای جدا نگه داشتن وابستگی‌های پروژه شما از سایر پروژه‌ها توصیه می‌شود. می‌توانید از دستور زیر برای ایجاد یک محیط مجازی استفاده کنید:

        Bash
        python -m venv my_env
        

        سپس محیط مجازی را فعال کنید:

        Bash
        source my_env/bin/activate  # برای سیستم‌های Unix-like
        
      2. نصب Flask و Flask-SQLAlchemy:

        Bash
        pip install Flask Flask-SQLAlchemy
        

      پیکربندی SQLAlchemy در Flask

      1. ایجاد یک فایل app.py: این فایل نقطه شروع برنامه Flask شما خواهد بود.
      2. ایجاد یک نمونه Flask:
        Python
        from flask import Flask
        app = Flask(__name__)
        
      3. پیکربندی رشته اتصال به پایگاه داده: شما باید یک رشته اتصال به پایگاه داده خود را تعریف کنید. این رشته حاوی اطلاعاتی مانند نوع پایگاه داده، نام میزبان، نام کاربری، رمز عبور و نام پایگاه داده است.
        Python
        app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///mydatabase.db'  # برای SQLite
        # یا برای PostgreSQL:
        # app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://user:password@host/mydatabase'
        
      4. ایجاد یک نمونه SQLAlchemy:
        Python
        from flask_sqlalchemy import SQLAlchemy
        db = SQLAlchemy(app)
        

      مثال کامل:

      Python
      from flask import Flask
      from flask_sqlalchemy import SQLAlchemy
      
      app = Flask(__name__)
      app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///mydatabase.db'
      db = SQLAlchemy(app)
      
      class User(db.Model):
          id = db.Column(db.Integer, primary_key=True)
          username = db.Column(db.String(۸۰), unique=True, nullable=False)
          email = db.Column(db.String(۱۲۰), unique=True, nullable=False)
      
          def __repr__(self):
              return '<User %r>' % self.username
      
      if __name__ == '__main__':
          db.create_all()
          app.run(debug=True)
      

      توضیح کد:

      • app.config['SQLALCHEMY_DATABASE_URI']: رشته اتصال به پایگاه داده را تنظیم می‌کند.
      • db = SQLAlchemy(app): یک نمونه از SQLAlchemy ایجاد می‌کند و آن را به برنامه Flask متصل می‌کند.
      • class User(db.Model): یک کلاس مدل برای نمایش جدول کاربران تعریف می‌کند.
      • db.create_all(): تمام جداول تعریف شده را در پایگاه داده ایجاد می‌کند.

      نکات مهم:

      • تغییر تنظیمات SQLAlchemy: می‌توانید تنظیمات دیگری مانند SQLALCHEMY_TRACK_MODIFICATIONS را برای کنترل رفتار SQLAlchemy تغییر دهید.
      • مدیریت مهاجرت: برای مدیریت تغییرات در ساختار پایگاه داده، می‌توانید از ابزارهایی مانند Alembic استفاده کنید.
      • روابط بین مدل‌ها: SQLAlchemy به شما امکان می‌دهد روابط مختلفی بین مدل‌ها (One-to-One، One-to-Many، Many-to-Many) تعریف کنید.

    • ساخت یک برنامه Flask ساده با SQLAlchemy

      مقدمه

      در این آموزش، قصد داریم یک برنامه Flask ساده ایجاد کنیم که از SQLAlchemy برای مدیریت پایگاه داده استفاده می‌کند. این برنامه یک لیست ساده از کاربران را مدیریت خواهد کرد.

      مراحل ساخت

      1. ایجاد محیط مجازی: برای جدا نگه داشتن وابستگی‌های پروژه از سایر پروژه‌ها، ابتدا یک محیط مجازی ایجاد می‌کنیم:

        Bash
        python -m venv my_env
        

        سپس محیط مجازی را فعال می‌کنیم:

        Bash
        source my_env/bin/activate  # برای سیستم‌های Unix-like
        
      2. نصب وابستگی‌ها: در محیط مجازی فعال، پکیج‌های Flask و Flask-SQLAlchemy را نصب می‌کنیم:

        Bash
        pip install Flask Flask-SQLAlchemy
        
      3. ایجاد فایل app.py: یک فایل با نام app.py ایجاد کرده و کد زیر را در آن قرار می‌دهیم:

        Python
        from flask import Flask
        from flask_sqlalchemy import SQLAlchemy
        
        app = Flask(__name__)
        app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
        db = SQLAlchemy(app)
        
        class User(db.Model):
            id = db.Column(db.Integer, primary_key=True)
            username = db.Column(db.String(۸۰), unique=True, nullable=False)
            email = db.Column(db.String(۱۲۰), unique=True, nullable=False)
        
            def __repr__(self):
                return '<User %r>' % self.username
        
        @app.route('/')
        def index():
            users = User.query.all()
            return render_template('users.html', users=users)
        
        if __name__ == '__main__':
            db.create_all()
            app.run(debug=True)
        

      توضیح کد:

      • ایجاد برنامه Flask: یک نمونه از کلاس Flask ایجاد می‌کنیم.
      • پیکربندی پایگاه داده: با استفاده از SQLALCHEMY_DATABASE_URI به یک پایگاه داده SQLite به نام users.db متصل می‌شویم.
      • تعریف مدل کاربر: کلاس User یک مدل برای جدول کاربران تعریف می‌کند. هر کاربر دارای یک شناسه منحصر به فرد، نام کاربری و ایمیل است.
      • ایجاد مسیر اصلی: مسیر / به تابع index هدایت می‌شود که همه کاربران را از پایگاه داده دریافت کرده و آن‌ها را به قالب users.html ارسال می‌کند.
      • ایجاد جدول: با اجرای db.create_all()، جدول users در پایگاه داده ایجاد می‌شود.
      1. ایجاد قالب HTML (users.html): یک فایل با نام templates/users.html ایجاد کرده و کد زیر را در آن قرار دهید:

        HTML
        <!DOCTYPE html>
        <html>
        <head>
            <title>لیست کاربران</title>
        </head>
        <body>
            <h1>لیست کاربران</h1>
            <ul>
                {% for user in users %}
                <li>{{ user.username }} ({{ user.email }})</li>
                {% endfor %}
            </ul>
        </body>
        </html>
        

      اجرای برنامه

      برای اجرای برنامه، دستور زیر را در ترمینال اجرا کنید:

      Bash
      python app.py
      

      با باز کردن مرورگر و رفتن به آدرس http://127.0.0.1:5000/، لیست کاربران نمایش داده می‌شود.

      نکات اضافی:

      • افزودن کاربر جدید: برای افزودن کاربر جدید، می‌توانید یک فرم ایجاد کرده و داده‌های فرم را به پایگاه داده اضافه کنید.
      • ویرایش و حذف کاربر: با ایجاد مسیرهای جدید و استفاده از روش‌های query.get()، session.add(), session.delete() و session.commit()، می‌توانید عملیات ویرایش و حذف را انجام دهید.
      • استفاده از پایگاه داده‌های دیگر: می‌توانید به جای SQLite از پایگاه داده‌های دیگری مانند PostgreSQL، MySQL و … استفاده کنید.
      • بهینه‌سازی: برای پروژه‌های بزرگ‌تر، می‌توانید از تکنیک‌های بهینه‌سازی مانند caching و indexing استفاده کنید.

      نتیجه‌گیری

      در این آموزش، یک برنامه Flask ساده ایجاد کردیم که از SQLAlchemy برای مدیریت پایگاه داده استفاده می‌کند. این برنامه یک پایه محکم برای ساخت برنامه‌های وب پیچیده‌تر با استفاده از Flask و SQLAlchemy فراهم می‌کند.

       

  • مدل‌های داده در SQLAlchemy در Flask

    • تعریف کلاس‌های مدل در SQLAlchemy

      در SQLAlchemy، کلاس‌های مدل (Model Classes) نقش بسیار مهمی در نگاشت اشیاء پایتون به جداول پایگاه داده ایفا می‌کنند. هر کلاس مدل به یک جدول در پایگاه داده متناظر است و هر ویژگی (attribute) در کلاس، به یک ستون در آن جدول نگاشت می‌شود.

      ساختار کلی یک کلاس مدل

      Python
      from sqlalchemy import Column, Integer, String
      from sqlalchemy.ext.declarative import declarative_base
      
      Base = declarative_base()
      
      class User(Base):
          __tablename__ = 'users'
          id = Column(Integer, primary_key=True)
          username = Column(String(۸۰), unique=True, nullable=False)
          email = Column(String(۱۲۰), unique=True, nullable=False)
      

      توضیح اجزای کلاس مدل:

      • Base: این کلاس پایه توسط declarative_base() ایجاد می‌شود و به SQLAlchemy می‌گوید که کلاس‌های تعریف شده زیر آن، کلاس‌های مدل هستند.
      • __tablename__: نام جدولی را که این کلاس به آن نگاشت می‌شود مشخص می‌کند.
      • Column: هر ستون در جدول با استفاده از یک شیء Column تعریف می‌شود.
      • Integer, String: انواع داده‌های ستون‌ها را مشخص می‌کنند. انواع داده‌های دیگری نیز وجود دارند مانند Float, DateTime و …
      • primary_key=True: ستونی را به عنوان کلید اصلی جدول مشخص می‌کند.
      • unique=True: مشخص می‌کند که مقدار این ستون باید در کل جدول منحصر به فرد باشد.
      • nullable=False: مشخص می‌کند که این ستون نمی‌تواند مقدار تهی داشته باشد.

      مزایای استفاده از کلاس‌های مدل:

      • سادگی و خوانایی کد: به جای نوشتن دستورات SQL پیچیده، می‌توانید با استفاده از اشیاء پایتون با پایگاه داده کار کنید.
      • افزایش بهره‌وری: بسیاری از کارهای تکراری و خسته‌کننده در کار با پایگاه داده به صورت خودکار انجام می‌شود.
      • کاهش خطا: احتمال بروز خطاهای دستوری در SQL کاهش می‌یابد.
      • قابلیت نگهداری: کد قابل نگهداری‌تر است.

      مثال‌های بیشتر:

      • مدل پست وبلاگ:
        Python
        class Post(Base):
            id = Column(Integer, primary_key=True)
            title = Column(String(۱۰۰), nullable=False)
            body = Column(Text)
            created_at = Column(DateTime, default=datetime.utcnow)
        
      • مدل رابطه چند به چند (مثلاً کاربران و گروه‌ها):
        Python
        user_groups = db.Table('user_groups',
            db.Column('user_id', db.Integer, db.ForeignKey('users.id')),
            db.Column('group_id', db.Integer, db.ForeignKey('groups.id'))
        )
        
        class User(Base):
            # ...
        
        class Group(Base):
            # ...
        
            users = db.relationship('User', secondary=user_groups, backref='groups')
        

      نکات مهم:

      • انواع داده‌ها: برای انتخاب نوع داده مناسب برای هر ستون، به نوع داده‌ای که در پایگاه داده ذخیره می‌شود توجه کنید.
      • روابط بین مدل‌ها: SQLAlchemy از انواع مختلف رابطه بین مدل‌ها مانند One-to-One، One-to-Many و Many-to-Many پشتیبانی می‌کند.
      • مهاجرت‌ها: برای ایجاد تغییرات در ساختار جدول، از ابزارهایی مانند Alembic استفاده می‌شود.

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

       

    • ستون‌ها و انواع داده‌ها در پایگاه داده

      در پایگاه داده، ستون‌ها (Column) مانند سلول‌های یک جدول عمل می‌کنند و هر یک برای نگهداری نوع خاصی از داده استفاده می‌شوند. انتخاب نوع داده مناسب برای هر ستون بسیار مهم است، زیرا این کار بر نحوه ذخیره‌سازی، جستجو و بازیابی داده‌ها تأثیر می‌گذارد.

      انواع داده‌ها

      انواع داده‌ها به دو دسته کلی تقسیم می‌شوند:

      ۱. انواع داده عددی (Numeric Data Types)

      • اعداد صحیح: برای ذخیره اعداد صحیح بدون اعشار استفاده می‌شود.
        • INT: برای اعداد صحیح بزرگ
        • SMALLINT: برای اعداد صحیح کوچک
        • TINYINT: برای اعداد صحیح بسیار کوچک
        • BIGINT: برای اعداد صحیح بسیار بزرگ
      • اعداد اعشاری: برای ذخیره اعداد با اعشار استفاده می‌شود.
        • FLOAT: برای اعداد با دقت تقریبی
        • DECIMAL: برای اعداد با دقت دقیق
      • اعداد پولی: برای ذخیره مقادیر پولی استفاده می‌شود.
        • MONEY: برای ذخیره مقادیر پولی
        • SMALLMONEY: برای ذخیره مقادیر پولی کوچک

      ۲. انواع داده رشته‌ای (Character Data Types)

      • رشته‌های با طول ثابت: برای ذخیره رشته‌هایی با طول مشخص استفاده می‌شود.
        • CHAR: برای رشته‌های با طول ثابت
      • رشته‌های با طول متغیر: برای ذخیره رشته‌هایی با طول متغیر استفاده می‌شود.
        • VARCHAR: برای رشته‌های با طول متغیر
        • TEXT: برای ذخیره متن‌های طولانی

      ۳. انواع داده تاریخ و زمان (Date and Time Data Types)

      • تاریخ: برای ذخیره تاریخ استفاده می‌شود.
        • DATE: برای ذخیره تاریخ
      • زمان: برای ذخیره زمان استفاده می‌شود.
        • TIME: برای ذخیره زمان
      • تاریخ و زمان: برای ذخیره هم تاریخ و هم زمان استفاده می‌شود.
        • DATETIME: برای ذخیره تاریخ و زمان

      ۴. انواع داده منطقی (Boolean Data Types)

      • بولین: برای ذخیره مقادیر درست یا غلط استفاده می‌شود.
        • BOOLEAN: برای ذخیره مقادیر بولین

      ۵. انواع داده باینری (Binary Data Types)

      • بایت‌ها: برای ذخیره داده‌های باینری مانند تصاویر، فایل‌ها و … استفاده می‌شود.
        • VARBINARY: برای ذخیره داده‌های باینری با طول متغیر

      انتخاب نوع داده مناسب

      انتخاب نوع داده مناسب برای هر ستون به عوامل زیر بستگی دارد:

      • نوع داده‌ای که قرار است ذخیره شود: آیا عدد است، متن است، تاریخ است یا …؟
      • حجم داده: چقدر داده قرار است در این ستون ذخیره شود؟
      • دقت مورد نیاز: آیا به دقت بالایی نیاز است یا یک مقدار تقریبی کافی است؟
      • عملکرد: نوع داده انتخابی بر روی سرعت جستجو و بازیابی داده‌ها تأثیر می‌گذارد.

      مثال

      فرض کنید می‌خواهیم جدولی برای ذخیره اطلاعات کاربران ایجاد کنیم. ستون‌های این جدول ممکن است به صورت زیر باشند:

      • id: عدد صحیح (INT)، کلید اصلی و خودکار
      • username: رشته با طول متغیر (VARCHAR)
      • email: رشته با طول متغیر (VARCHAR)
      • is_active: بولین (BOOLEAN)
      • created_at: تاریخ و زمان (DATETIME)

      اهمیت انتخاب نوع داده مناسب

      • صرفه جویی در فضا: انتخاب نوع داده مناسب باعث می‌شود تا فضای کمتری در پایگاه داده اشغال شود.
      • افزایش سرعت: نوع داده مناسب باعث می‌شود تا عملیات جستجو و بازیابی داده‌ها سریع‌تر انجام شود.
      • کاهش خطا: انتخاب نوع داده مناسب باعث می‌شود تا از وارد شدن داده‌های نامعتبر به پایگاه داده جلوگیری شود.
      • بهبود عملکرد پرس و جوها: نوع داده مناسب باعث می‌شود تا پرس و جوهای SQL بهینه شوند.

      در انتخاب نوع داده مناسب برای ستون‌های خود، به دقت به نوع داده‌ای که قرار است ذخیره شود و نیازهای برنامه کاربردی خود توجه کنید.

       

    • کلیدهای اصلی و خارجی در Flask و SQLAlchemy

      کلید اصلی (Primary Key)

      کلید اصلی یک ستون یا ترکیبی از ستون‌ها در یک جدول است که به طور منحصر به فرد هر سطر را شناسایی می‌کند. در SQLAlchemy، کلید اصلی با استفاده از آرگومان primary_key=True در تعریف ستون مشخص می‌شود.

      Python
      from sqlalchemy import Column, Integer
      
      class User(db.Model):
          id = Column(Integer, primary_key=True)
          # ... سایر ستون‌ها
      

      در مثال بالا، ستون id به عنوان کلید اصلی جدول users تعریف شده است.

      کلید خارجی (Foreign Key)

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

      Python
      from sqlalchemy import Column, Integer, ForeignKey
      
      class Post(db.Model):
          id = Column(Integer, primary_key=True)
          user_id = Column(Integer, ForeignKey('user.id'))
          # ... سایر ستون‌ها
      
      class User(db.Model):
          id = Column(Integer, primary_key=True)
          # ... سایر ستون‌ها
      
          posts = db.relationship('Post', backref='author', lazy='dynamic')
      

      در مثال بالا، ستون user_id در جدول Post به ستون id در جدول User ارجاع می‌دهد. این نشان می‌دهد که هر پست به یک کاربر خاص تعلق دارد.

      کاربرد کلیدهای اصلی و خارجی

      • ایجاد ارتباط بین جداول: کلیدهای خارجی به شما اجازه می‌دهند تا روابط مختلفی مانند یک به یک، یک به چند و چند به چند بین جداول ایجاد کنید.
      • حفظ تمامیت داده‌ها: کلیدهای خارجی به شما کمک می‌کنند تا اطمینان حاصل کنید که داده‌های شما سازگار و صحیح هستند. به عنوان مثال، نمی‌توانید پستی ایجاد کنید که به کاربری که وجود ندارد ارجاع دهد.
      • بهبود عملکرد پرس و جوها: با استفاده از کلیدهای خارجی، می‌توانید پرس و جوهای پیچیده‌تری را به صورت کارآمدتری اجرا کنید.

      انواع روابط بین جداول

      • یک به یک (One-to-One): هر سطر در یک جدول به حداکثر یک سطر در جدول دیگر مرتبط است.
      • یک به چند (One-to-Many): هر سطر در یک جدول می‌تواند به چندین سطر در جدول دیگر مرتبط باشد.
      • چند به چند (Many-to-Many): هر سطر در یک جدول می‌تواند به چندین سطر در جدول دیگر و بالعکس مرتبط باشد.

      نکات مهم

      • شاخص‌گذاری: برای بهبود عملکرد پرس و جوها، بهتر است روی ستون‌های کلید اصلی و خارجی شاخص ایجاد کنید.
      • آبشار حذف (Cascade Delete): در برخی موارد، ممکن است بخواهید هنگامی که یک سطر با کلید اصلی حذف می‌شود، سطرهای مرتبط با آن در جداول دیگر نیز حذف شوند. این کار با استفاده از آرگومان ondelete در تعریف رابطه قابل انجام است.
      • بهینه‌سازی رابطه‌ها: برای روابط پیچیده، ممکن است نیاز به بهینه‌سازی ساختار پایگاه داده و استفاده از تکنیک‌هایی مانند جداول واسط داشته باشید.

      مثال عملی

      فرض کنید می‌خواهیم یک وبلاگ ساده با Flask ایجاد کنیم. ما به دو جدول نیاز داریم: users و posts. هر پست به یک کاربر تعلق دارد.

      Python
      # ... کدهای قبلی
      
      class Post(db.Model):
          id = Column(Integer, primary_key=True)
          title = Column(String(۱۰۰), nullable=False)
          body = Column(Text)
          user_id = Column(Integer, ForeignKey('user.id'), nullable=False)
          user = db.relationship('User', backref='posts')
      
      class User(db.Model):
          id = Column(Integer, primary_key=True)
          username = Column(String(۸۰), unique=True, nullable=False)
          # ...
      

      در این مثال، user_id در جدول Post یک کلید خارجی است که به id در جدول User ارجاع می‌دهد. رابطه backref به ما اجازه می‌دهد تا از یک پست به کاربر مربوطه دسترسی پیدا کنیم.

       

    • روابط بین مدل‌ها در SQLAlchemy

      در SQLAlchemy، روابط بین مدل‌ها (که هر کدام نماینده یک جدول در پایگاه داده هستند) به ما اجازه می‌دهند تا داده‌های مرتبط را به صورت منطقی سازماندهی کنیم. SQLAlchemy از سه نوع رابطه اصلی پشتیبانی می‌کند:

      ۱. رابطه یک به یک (One-to-One)

      در این نوع رابطه، هر نمونه از یک کلاس (مدل) تنها می‌تواند به یک نمونه از کلاس دیگر مرتبط باشد و بالعکس. به عنوان مثال، یک کاربر ممکن است فقط یک پروفایل داشته باشد و یک پروفایل نیز تنها به یک کاربر تعلق داشته باشد.

      Python
      class User(db.Model):
          id = db.Column(db.Integer, primary_key=True)
          # ...
      
      class Profile(db.Model):
          id = db.Column(db.Integer, primary_key=True)
          user_id = db.Column(db.Integer, ForeignKey('user.id'), unique=True)
          user = db.relationship('User', backref='profile', uselist=False)
      
      • uselist=False مشخص می‌کند که user یک شیء از نوع User است نه یک لیست.

      ۲. رابطه یک به چند (One-to-Many)

      در این نوع رابطه، یک نمونه از یک کلاس می‌تواند به چندین نمونه از کلاس دیگر مرتبط باشد، اما هر نمونه از کلاس دوم تنها به یک نمونه از کلاس اول مرتبط است. به عنوان مثال، یک نویسنده می‌تواند چندین کتاب بنویسد، اما هر کتاب تنها به یک نویسنده تعلق دارد.

      Python
      class Author(db.Model):
          id = db.Column(db.Integer, primary_key=True)
          # ...
      
      class Book(db.Model):
          id = db.Column(db.Integer, primary_key=True)
          author_id = db.Column(db.Integer, ForeignKey('author.id'))
          author = db.relationship('Author', backref='books')
      

      ۳. رابطه چند به چند (Many-to-Many)

      در این نوع رابطه، یک نمونه از یک کلاس می‌تواند به چندین نمونه از کلاس دیگر و بالعکس مرتبط باشد. به عنوان مثال، یک کاربر می‌تواند به چندین گروه تعلق داشته باشد و یک گروه نیز می‌تواند چندین کاربر داشته باشد.

      برای پیاده‌سازی رابطه چند به چند، از یک جدول واسط استفاده می‌شود.

      Python
      post_tags = db.Table('post_tags',
          db.Column('post_id', db.Integer, db.ForeignKey('post.id')),
          db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'))
      )
      
      class Post(db.Model):
          id = db.Column(db.Integer, primary_key=True)
          # ...
          tags = db.relationship('Tag', secondary=post_tags, backref='posts')
      
      class Tag(db.Model):
          id = db.Column(db.Integer, primary_key=True)
          # ...
      

      نکات مهم

      • backref: ویژگی backref به شما اجازه می‌دهد تا از جهت مخالف رابطه نیز دسترسی داشته باشید.
      • lazy loading: با استفاده از lazy='dynamic'، روابط به صورت تنبل بارگذاری می‌شوند، یعنی تا زمانی که به آن‌ها نیاز نباشد، از پایگاه داده بارگذاری نمی‌شوند.
      • cascade: با استفاده از cascade می‌توانید مشخص کنید که هنگام حذف یا به روزرسانی یک شیء، چه اتفاقی برای اشیاء مرتبط بیفتد.

      انتخاب نوع رابطه مناسب

      انتخاب نوع رابطه مناسب به ساختار داده‌های شما و نحوه ارتباط بین اشیاء بستگی دارد. درک صحیح این روابط به شما کمک می‌کند تا مدل‌های داده‌ای کارآمد و قابل نگهداری ایجاد کنید.

       

بخش دوم: کار با پایگاه داده

  • Session:

    • مفهوم Session در SQLAlchemy

      Session در SQLAlchemy به عنوان یک رابط بین برنامه شما و پایگاه داده عمل می‌کند. این شیء به شما اجازه می‌دهد تا تغییراتی را در پایگاه داده ایجاد کنید، مانند افزودن، ویرایش و حذف سطرها، و سپس این تغییرات را به صورت یکجا به پایگاه داده ارسال کنید.

      چرا از Session استفاده می‌کنیم؟

      • مدیریت تراکنش‌ها: Session به شما اجازه می‌دهد تا یک مجموعه از عملیات را به عنوان یک تراکنش در نظر بگیرید. اگر در طول تراکنش خطایی رخ دهد، تمام تغییرات لغو می‌شود.
      • بهبود عملکرد: با استفاده از Session، می‌توانید تعداد تعاملات با پایگاه داده را کاهش داده و در نتیجه عملکرد برنامه را بهبود بخشید.
      • تسهیل در کار با اشیاء: Session به شما اجازه می‌دهد تا با اشیاء پایتون کار کنید و SQLAlchemy به صورت خودکار تغییرات را به پایگاه داده نگاشت می‌کند.

      نحوه ایجاد و استفاده از Session:

      Python
      from sqlalchemy import create_engine
      from sqlalchemy.orm import sessionmaker
      
      # ایجاد موتور پایگاه داده
      engine = create_engine('sqlite:///mydatabase.db')
      
      # ایجاد SessionMaker
      Session = sessionmaker(bind=engine)
      
      # ایجاد یک Session
      session = Session()
      
      # افزودن یک شیء به Session
      new_user = User(name='Ali', email='ali@example.com')
      session.add(new_user)
      
      # انجام تغییرات
      session.commit()
      

      عملیات رایج با Session:

      • افزودن (add): برای افزودن یک شیء جدید به پایگاه داده.
      • حذف (delete): برای حذف یک شیء از پایگاه داده.
      • ویرایش (update): برای ویرایش یک شیء موجود.
      • پرس و جو (query): برای بازیابی داده‌ها از پایگاه داده.

      مثال: بازیابی همه کاربران

      Python
      users = session.query(User).all()
      for user in users:
          print(user.name)
      

      مثال: به روز رسانی یک کاربر

      Python
      user = session.query(User).filter_by(id=۱).first()
      user.name = 'Ali Reza'
      session.commit()
      

      نکات مهم:

      • commit: برای اعمال تغییرات در پایگاه داده، باید از session.commit() استفاده کنید.
      • rollback: اگر بخواهید تغییرات را لغو کنید، از session.rollback() استفاده کنید.
      • close: پس از اتمام کار با Session، باید آن را با استفاده از session.close() ببندید.
      • lazy loading: به طور پیش فرض، روابط بین اشیاء به صورت تنبل بارگذاری می‌شوند. این بدان معنی است که تنها زمانی که به داده‌های مرتبط نیاز داشته باشید، از پایگاه داده بارگذاری می‌شوند.

      مفاهیم پیشرفته:

      • flush: برای ارسال تغییرات به پایگاه داده بدون commit کردن.
      • detached objects: اشیایی که از Session جدا شده‌اند.
      • sessionmaker: برای ایجاد چندین Session با تنظیمات یکسان.

      جمع‌بندی:

      Session در SQLAlchemy یک ابزار قدرتمند برای مدیریت تراکنش‌ها و تعامل با پایگاه داده است. با استفاده از Session، می‌توانید به راحتی داده‌ها را به پایگاه داده اضافه، ویرایش و حذف کنید و همچنین پرس و جوهای پیچیده‌ای را انجام دهید.

       

    • افزودن، حذف و به‌روزرسانی اشیاء در SQLAlchemy

      در SQLAlchemy، برای اضافه کردن، حذف و به‌روزرسانی اشیاء در پایگاه داده، از شیء Session استفاده می‌شود. Session به عنوان یک رابط بین برنامه شما و پایگاه داده عمل می‌کند و به شما اجازه می‌دهد تا تغییراتی را در پایگاه داده ایجاد کنید و سپس این تغییرات را به صورت یکجا به پایگاه داده ارسال کنید.

      افزودن یک شیء جدید

      برای افزودن یک شیء جدید به پایگاه داده، ابتدا آن را ایجاد کرده و سپس آن را به Session اضافه می‌کنیم. در نهایت با فراخوانی session.commit(), تغییرات را به پایگاه داده اعمال می‌کنیم.

      Python
      from sqlalchemy import create_engine
      from sqlalchemy.orm import sessionmaker
      
      # ... (کدهای ایجاد موتور و Session)
      
      # ایجاد یک شیء جدید
      new_user = User(name='علی', email='ali@example.com')
      
      # اضافه کردن شیء به Session
      session.add(new_user)
      
      # اعمال تغییرات
      session.commit()
      

      حذف یک شیء

      برای حذف یک شیء، ابتدا آن را از پایگاه داده بازیابی کرده و سپس آن را از Session حذف می‌کنیم.

      Python
      # بازیابی شیء
      user_to_delete = session.query(User).filter_by(id=۱).first()
      
      # حذف شیء از Session
      session.delete(user_to_delete)
      
      # اعمال تغییرات
      session.commit()
      

      به‌روزرسانی یک شیء

      برای به‌روزرسانی یک شیء، ابتدا آن را از پایگاه داده بازیابی کرده و سپس ویژگی‌های آن را تغییر می‌دهیم. نیازی به فراخوانی session.add() نیست، زیرا شیء از قبل به Session اضافه شده است.

      Python
      # بازیابی شیء
      user_to_update = session.query(User).filter_by(id=۱).first()
      
      # تغییر ویژگی
      user_to_update.name = 'علی رضا'
      
      # اعمال تغییرات
      session.commit()
      

      نکته‌های مهم

      • Session.commit(): برای اعمال تغییرات در پایگاه داده، حتما باید session.commit() را فراخوانی کنید.
      • Session.rollback(): اگر بخواهید تغییرات را لغو کنید، از session.rollback() استفاده کنید.
      • Session.close(): پس از اتمام کار با Session، باید آن را با استفاده از session.close() ببندید.
      • Lazy loading: به طور پیش‌فرض، روابط بین اشیاء به صورت تنبل بارگذاری می‌شوند. این بدان معنی است که تنها زمانی که به داده‌های مرتبط نیاز داشته باشید، از پایگاه داده بارگذاری می‌شوند.
      • Detached objects: اشیایی که از Session جدا شده‌اند، دیگر توسط SQLAlchemy مدیریت نمی‌شوند.

      مثال کامل‌تر

      Python
      from sqlalchemy import create_engine
      from sqlalchemy.orm import sessionmaker
      
      # ایجاد موتور پایگاه داده
      engine = create_engine('sqlite:///mydatabase.db')
      
      # ایجاد SessionMaker
      Session = sessionmaker(bind=engine)
      
      # ایجاد یک Session
      session = Session()
      
      # افزودن چند کاربر
      users = [
          User(name='علی', email='ali@example.com'),
          User(name='حسن', email='hasan@example.com'),
      ]
      session.add_all(users)
      session.commit()
      
      # بازیابی یک کاربر و تغییر نام او
      user = session.query(User).filter_by(id=۱).first()
      user.name = 'علی جدید'
      session.commit()
      
      # حذف کاربری
      user_to_delete = session.query(User).filter_by(id=۲).first()
      session.delete(user_to_delete)
      session.commit()
      
      session.close()
      

      جمع‌بندی

      Session در SQLAlchemy به شما امکان می‌دهد تا به سادگی با اشیاء پایتون کار کنید و تغییرات را به پایگاه داده اعمال کنید. با درک مفاهیم اولیه افزودن، حذف و به‌روزرسانی اشیاء، می‌توانید به راحتی برنامه‌های کاربردی خود را با استفاده از SQLAlchemy توسعه دهید.

       

    • Commit کردن تغییرات در SQLAlchemy

      Commit کردن در SQLAlchemy به معنای اعمال دائمی تغییرات ایجاد شده در اشیاء به پایگاه داده است. به عبارت دیگر، زمانی که شما یک شیء جدید اضافه می‌کنید، یک شیء را حذف می‌کنید یا ویژگی‌های یک شیء را تغییر می‌دهید، این تغییرات ابتدا در حافظه (در Session) نگهداری می‌شوند و تا زمانی که session.commit() را فراخوانی نکنید، به پایگاه داده منتقل نمی‌شوند.

      چرا Commit مهم است؟

      • ثبات داده‌ها: تا زمانی که تغییرات commit نشوند، در صورت بروز خطا یا قطع شدن برنامه، تغییرات از بین می‌روند.
      • کنترل تراکنش‌ها: با استفاده از commit می‌توانید چندین عملیات را به عنوان یک تراکنش در نظر بگیرید. اگر در طول تراکنش خطایی رخ دهد، تمام تغییرات لغو می‌شود.

      فرایند Commit کردن

      1. ایجاد تغییرات: شما اشیاء را ایجاد، حذف یا به‌روزرسانی می‌کنید.
      2. اضافه کردن به Session: اشیاء جدید را به Session اضافه می‌کنید.
      3. Commit کردن: با فراخوانی session.commit(), تغییرات به پایگاه داده ارسال می‌شود.

      مثال:

      Python
      from sqlalchemy import create_engine
      from sqlalchemy.orm import sessionmaker
      
      # ... (کدهای ایجاد موتور و Session)
      
      # ایجاد یک شیء جدید
      new_user = User(name='علی', email='ali@example.com')
      
      # اضافه کردن شیء به Session
      session.add(new_user)
      
      # اعمال تغییرات
      session.commit()
      

      نکات مهم درباره Commit

      • Rollback: اگر بخواهید تغییرات را لغو کنید، از session.rollback() استفاده کنید.
      • Flush: تفاوت بین flush و commit این است که flush تغییرات را به پایگاه داده می‌فرستد اما تراکنش را commit نمی‌کند. این می‌تواند در برخی موارد برای بهینه‌سازی عملکرد مفید باشد.
      • Autocommit: در برخی تنظیمات، Session ممکن است به صورت خودکار تغییرات را commit کند. با این حال، بهتر است به صورت دستی commit کنید تا کنترل بیشتری بر روی تراکنش‌ها داشته باشید.
      • Context Manager: برای مدیریت بهتر تراکنش‌ها، می‌توانید از context manager استفاده کنید:
        Python
        with session.begin():
            # عملیات های مختلف
        

        در این حالت، اگر در داخل بلوک with خطایی رخ دهد، به صورت خودکار rollback انجام می‌شود.

      مثال با استفاده از Context Manager

      Python
      with session.begin():
          new_user = User(name='علی', email='ali@example.com')
          session.add(new_user)
          # عملیات های دیگر
      

      چه زمانی باید Commit کنیم؟

      • بعد از هر عملیات: اگر می‌خواهید هر تغییر به صورت جداگانه به پایگاه داده ارسال شود، می‌توانید بعد از هر عملیات commit کنید.
      • در پایان یک واحد کار: اگر چندین عملیات مرتبط با هم دارید، می‌توانید آن‌ها را در یک تراکنش قرار داده و در پایان تراکنش commit کنید.

      انتخاب روش مناسب به طراحی برنامه و نیازهای عملکردی شما بستگی دارد.

       

    • Rollback کردن تغییرات در SQLAlchemy

      Rollback کردن در SQLAlchemy به معنای لغو تمام تغییراتی است که از زمان آخرین commit یا آغاز یک تراکنش جدید روی داده‌اند. این عمل بسیار مفید است زمانی که می‌خواهید تغییراتی را که به طور تصادفی ایجاد شده‌اند یا با خطا مواجه شده‌اند، لغو کنید.

      چرا Rollback مهم است؟

      • جلوگیری از بروز خطا: اگر در طی یک عملیات، خطایی رخ دهد، با rollback کردن می‌توانید از اعمال تغییرات ناقص در پایگاه داده جلوگیری کنید.
      • کنترل تراکنش‌ها: rollback به شما اجازه می‌دهد تا یک تراکنش را به صورت دستی لغو کنید و به نقطه قبلی برگردید.
      • آزمایش تغییرات: می‌توانید تغییراتی را ایجاد کنید، آن‌ها را تست کنید و در صورت عدم رضایت، با rollback آن‌ها را لغو کنید.

      نحوه Rollback کردن

      برای rollback کردن تغییرات، از متد rollback() شیء Session استفاده می‌کنیم.

      Python
      from sqlalchemy import create_engine
      from sqlalchemy.orm import sessionmaker
      
      # ... (کدهای ایجاد موتور و Session)
      
      try:
          # ایجاد تغییرات
          new_user = User(name='علی', email='ali@example.com')
          session.add(new_user)
      
          # ایجاد تغییر دیگری که ممکن است با خطا مواجه شود
          # ...
      
          session.commit()  # اگر همه چیز درست پیش برود، commit می‌کنیم
      except Exception as e:
          session.rollback()  # در صورت بروز خطا، rollback می‌کنیم
          print("خطایی رخ داده است:", e)
      

      مثال با استفاده از Context Manager

      Python
      with session.begin():
          try:
              # ایجاد تغییرات
              new_user = User(name='علی', email='ali@example.com')
              session.add(new_user)
      
              # ایجاد تغییر دیگری که ممکن است با خطا مواجه شود
              # ...
          except Exception as e:
              print("خطایی رخ داده است:", e)
      

      در این مثال، اگر در داخل بلوک with خطایی رخ دهد، به صورت خودکار rollback انجام می‌شود.

      نکات مهم درباره Rollback

      • Rollback کامل: rollback تمام تغییرات را از زمان آخرین commit یا آغاز تراکنش لغو می‌کند.
      • Savepoint: اگر می‌خواهید بخشی از تغییرات را لغو کنید و بقیه را حفظ کنید، می‌توانید از savepoint استفاده کنید.
      • Rollback ضمنی: در برخی موارد، مانند بسته شدن Session بدون commit کردن، rollback به صورت خودکار انجام می‌شود.

      چه زمانی باید Rollback کنیم؟

      • هنگام بروز خطا: اگر در طی یک عملیات خطایی رخ دهد، باید rollback کنید تا از بروز داده‌های نادرست جلوگیری کنید.
      • هنگام آزمایش تغییرات: قبل از commit کردن تغییرات مهم، می‌توانید آن‌ها را در یک محیط تست آزمایش کنید و در صورت نیاز rollback کنید.
      • هنگام لغو یک تراکنش: اگر می‌خواهید تمام تغییرات یک تراکنش را لغو کنید، می‌توانید rollback کنید.

      جمع‌بندی

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

       

  • Query:

    • نوشتن پرس‌وجوهای ساده و پیچیده در Flask با استفاده از SQLAlchemy

      Flask یک فریمورک وب پایتون است که برای ایجاد وب‌اپلیکیشن‌ها استفاده می‌شود. SQLAlchemy یک ORM (Object-Relational Mapper) قدرتمند است که به شما اجازه می‌دهد با پایگاه داده به صورت شیء‌گرا کار کنید. با ترکیب این دو، می‌توانید پرس‌جوهای پیچیده را به سادگی در برنامه Flask خود بنویسید.

      ساختار پایه یک پرس‌جو در Flask و SQLAlchemy

      Python
      from flask import Flask
      from flask_sqlalchemy import SQLAlchemy
      
      app = Flask(__name__)
      app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///mydatabase.db'
      db = SQLAlchemy(app)
      
      class User(db.Model):
          id = db.Column(db.Integer, primary_key=True)
          username = db.Column(db.String(۸۰), unique=True, nullable=False)
          email = db.Column(db.String(۱۲۰), unique=True, nullable=False)
      
      # ...
      
      @app.route('/users')
      def get_all_users():
          users = User.query.all()
          return render_template('users.html', users=users)
      

      در این مثال:

      • User یک مدل است که نماینده جدول users در پایگاه داده است.
      • db.session.query(User) یک پرس‌جو برای جدول User ایجاد می‌کند.
      • all() تمام سطرهای جدول را برمی‌گرداند.

      پرس‌جوهای ساده

      • یافتن همه سطرها:
        Python
        users = User.query.all()
        
      • یافتن یک سطر بر اساس شرط:
        Python
        user = User.query.filter_by(username='admin').first()
        
      • شمارش سطرها:
        Python
        count = User.query.count()
        

      پرس‌جوهای پیچیده

      • مرتب‌سازی:
        Python
        users = User.query.order_by(User.username.desc()).all()  # مرتب‌سازی بر اساس username به صورت نزولی
        
      • فیلتر کردن بر اساس چندین شرط:
        Python
        users = User.query.filter(User.username.like('%admin%'), User.is_active==True).all()
        
      • محاسبات:
        Python
        average_age = db.session.query(db.func.avg(User.age)).scalar()
        
      • رابطه بین جداول:
        Python
        # فرض کنید یک رابطه یک به چند بین User و Post وجود دارد
        posts = User.query.filter_by(id=۱).first().posts
        
      • Join:
        Python
        # فرض کنید جداول User و Post مرتبط هستند
        users_with_posts = db.session.query(User, Post).join(Post, User.id == Post.user_id).all()
        

      استفاده از متدهای مختلف Query

      • filter(): برای اضافه کردن شرط‌های فیلتر کردن
      • order_by(): برای مرتب‌سازی نتایج
      • limit(): برای محدود کردن تعداد نتایج
      • offset(): برای پرش از تعداد مشخصی نتیجه
      • group_by(): برای گروه‌بندی نتایج
      • having(): برای اعمال شرط بر روی گروه‌ها
      • distinct(): برای حذف سطرهای تکراری

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

      Python
      from datetime import datetime, timedelta
      
      active_users_last_month = db.session.query(User) \
          .join(Post, User.id == Post.user_id) \
          .filter(Post.created_at > datetime.now() - timedelta(days=۳۰)) \
          .distinct(User.id).all()
      

      نکات مهم

      • عملکرد: برای پرس‌جوهای پیچیده، بهینه سازی پرس‌جوها بسیار مهم است. از شاخص‌ها، joinهای مناسب و محدود کردن نتایج استفاده کنید.
      • ORM: SQLAlchemy ORM به شما امکان می‌دهد با اشیاء پایتون کار کنید و به طور خودکار پرس‌جوهای SQL را ایجاد کند.
      • SQLAlchemy Core: برای کنترل بیشتر بر پرس‌جوها، می‌توانید از SQLAlchemy Core استفاده کنید.

       

       

    • استفاده از فیلترها، مرتب‌سازی و محدود کردن نتایج در پرس‌جوهای SQLAlchemy

      در SQLAlchemy، برای دستیابی به نتایج دقیق و مورد نظر، از ابزارهای قدرتمندی مانند فیلترها، مرتب‌سازی و محدود کردن نتایج استفاده می‌شود. این ابزارها به شما اجازه می‌دهند تا داده‌های مورد نظر خود را از پایگاه داده استخراج کرده و آن‌ها را به شکل دلخواه مرتب و نمایش دهید.

      فیلتر کردن (Filtering)

      فیلتر کردن به شما این امکان را می‌دهد تا نتایج پرس‌جو را بر اساس شرایط خاصی محدود کنید. از متد filter() برای اعمال فیلترها استفاده می‌شود.

      Python
      # یافتن همه کاربرانی که نام آن‌ها با 'علی' شروع می‌شود
      users = User.query.filter(User.name.like('علی%')).all()
      
      • عملگرهای مقایسه‌ای: ==, !=, >, <, >=, <=
      • عملگرهای منطقی: and, or, not
      • عملگرهای رشته‌ای: like, ilike (برای مقایسه بدون در نظر گرفتن حروف بزرگ و کوچک)
      • توابع: in_, not_in, between, is_

      مرتب‌سازی (Ordering)

      برای مرتب‌سازی نتایج پرس‌جو، از متد order_by() استفاده می‌شود.

      Python
      # مرتب‌سازی کاربران بر اساس نام به صورت نزولی
      users = User.query.order_by(User.name.desc()).all()
      
      • ترتیب صعودی: column.asc()
      • ترتیب نزولی: column.desc()
      • چندین مرتب‌سازی: order_by(Column1.asc(), Column2.desc())

      محدود کردن نتایج (Pagination)

      برای محدود کردن تعداد نتایج و ایجاد صفحات، از متدهای limit() و offset() استفاده می‌شود.

      Python
      # نمایش ۱۰ نتیجه از صفحه دوم (یعنی از رکورد یازدهم)
      users = User.query.order_by(User.id).limit(۱۰).offset(۱۰).all()
      
      • limit(n): محدود کردن نتایج به n مورد
      • offset(n): پرش از n نتیجه اولیه

      مثال جامع

      Python
      # یافتن ۵ پست اخیر نوشته شده توسط کاربران با نام خانوادگی 'احمدی'، مرتب شده بر اساس تاریخ ایجاد
      posts = Post.query.filter(Post.user.has(last_name='احمدی')) \
                         .order_by(Post.created_at.desc()) \
                         .limit(۵).all()
      

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

      شما می‌توانید این ابزارها را به صورت ترکیبی برای ایجاد پرس‌جوهای پیچیده استفاده کنید.

      Python
      # یافتن ۱۰ محصول پرفروش‌ترین در ماه گذشته، مرتب شده بر اساس تعداد فروش
      products = Product.query.join(Order, Product.id == Order.product_id) \
                              .filter(Order.created_at > datetime.now() - timedelta(days=۳۰)) \
                              .group_by(Product.id) \
                              .order_by(db.func.count(Order.id).desc()) \
                              .limit(۱۰).all()
      

      نکات مهم

      • عملکرد: برای پرس‌جوهای پیچیده، بهینه سازی پرس‌جوها بسیار مهم است. از شاخص‌ها، joinهای مناسب و محدود کردن نتایج استفاده کنید.
      • ORM: SQLAlchemy ORM به شما امکان می‌دهد با اشیاء پایتون کار کنید و به طور خودکار پرس‌جوهای SQL را ایجاد کند.
      • SQLAlchemy Core: برای کنترل بیشتر بر پرس‌جوها، می‌توانید از SQLAlchemy Core استفاده کنید.

      با استفاده از این ابزارها، می‌توانید پرس‌جوهای پیچیده و قدرتمندی را در برنامه‌های Flask خود بنویسید و داده‌های مورد نظر خود را به طور دقیق و کارآمد استخراج کنید.

       

    • عملیات Join و Subquery در SQLAlchemy و Flask

      مقدمه

      SQLAlchemy یک ORM قدرتمند است که تعامل با پایگاه داده را در برنامه‌های پایتون، از جمله Flask، ساده‌تر می‌کند. عملیات Join و Subquery دو تکنیک اساسی برای بازیابی داده‌های پیچیده هستند.

      Join (پیوند)

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

      الف. Join ضمنی:

      • روابط بین مدل‌ها را با استفاده از سینتکس اعلامی SQLAlchemy تعریف کنید:
      Python
      from flask_sqlalchemy import SQLAlchemy
      
      db = SQLAlchemy()
      
      class User(db.Model):
          id = db.Column(db.Integer, primary_key=True)
          name = db.Column(db.String(۸۰), unique=True, nullable=False)
          posts = db.relationship('Post', backref='author', lazy='dynamic')
      
      class Post(db.Model):
          id = db.Column(db.Integer, primary_key=True)
          title = db.Column(db.String(۸۰), unique=True, nullable=False)
          body = db.Column(db.Text, nullable=False)
          user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
      
      • از ویژگی relationship برای انجام Join ضمنی استفاده کنید:
      Python
      posts = User.query.join(Post).filter(Post.title.like('%SQLAlchemy%')).all()
      

      ب. Join صریح:

      • از متد join() روی یک شیء query استفاده کنید:
      Python
      posts = db.session.query(User, Post).join(Post, User.id == Post.user_id).all()
      

      Subquery (زیر پرس‌جو)

      Subquery پرس‌جوهایی هستند که در داخل پرس‌جوی دیگری قرار می‌گیرند. آن‌ها می‌توانند برای فیلتر کردن، گروه‌بندی یا جمع‌آوری داده‌ها استفاده شوند.

      الف. Subquery همبسته:

      • از متد subquery() برای ایجاد یک Subquery استفاده کنید:
      Python
      subquery = (
          db.session.query(func.count(Post.id).label('post_count'))
          .filter(Post.user_id == User.id)
          .subquery()
      )
      
      users_with_post_count = db.session.query(User, subquery.c.post_count).all()
      

      ب. Subquery غیر همبسته:

      • یک Subquery را به صورت مستقل ایجاد کرده و از آن در پرس‌جوی اصلی استفاده کنید:
      Python
      recent_posts = (
          db.session.query(Post.id)
          .order_by(Post.timestamp.desc())
          .limit(۱۰)
          .subquery()
      )
      
      popular_users = db.session.query(User).filter(User.id.in_(recent_posts)).all()
      

      نکات کلیدی

      • عملکرد: به تأثیر عملکرد، به ویژه هنگام کار با مجموعه داده‌های بزرگ، توجه داشته باشید. از شاخص‌ها استفاده کنید و پرس‌جوها را بهینه کنید.
      • خوانایی: پرس‌جوهای واضح و مختصر بنویسید. از القاب برای Join‌ها و Subquery‌های پیچیده استفاده کنید تا خوانایی را بهبود بخشید.
      • مدیریت خطا: خطاهای احتمالی مانند مشکلات اتصال به پایگاه داده یا شکست پرس‌جو را به طور مناسب مدیریت کنید.
      • تست: پرس‌جوهای خود را به طور کامل آزمایش کنید تا اطمینان حاصل کنید که نتایج صحیح را تولید می‌کنند.

      با تسلط بر Join و Subquery در SQLAlchemy، می‌توانید به طور مؤثری داده‌های پیچیده را در برنامه‌های Flask خود بازیابی و دستکاری کنید.

       

    • استفاده از Aggregations (COUNT, SUM, AVG, …) در SQLAlchemy و Flask

      Aggregations در SQL برای جمع‌بندی داده‌ها استفاده می‌شوند. SQLAlchemy به شما اجازه می‌دهد تا به سادگی از این توابع در پرس‌جوهای خود استفاده کنید.

      مثال: محاسبه تعداد پست‌های هر کاربر

      Python
      from flask_sqlalchemy import SQLAlchemy
      
      db = SQLAlchemy()
      
      class User(db.Model):
          id = db.Column(db.Integer, primary_key=True)
          name = db.Column(db.String(۸۰), unique=True, nullable=False)
          posts = db.relationship('Post', backref='author', lazy='dynamic')
      
      class Post(db.Model):
          id = db.Column(db.Integer, primary_key=True)
          title = db.Column(db.String(۸۰), unique=True, nullable=False)
          body = db.Column(db.Text, nullable=False)
          user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
      
      # محاسبه تعداد پست‌های هر کاربر
      users_with_post_count = db.session.query(User, func.count(Post.id).label('post_count'))
                                          .join(Post)
                                          .group_by(User.id)
                                          .all()
      

      توابع Aggregation دیگر

      • SUM: جمع مقادیر یک ستون
      • AVG: میانگین مقادیر یک ستون
      • MIN: حداقل مقدار یک ستون
      • MAX: حداکثر مقدار یک ستون

      مثال: محاسبه میانگین طول پست‌ها

      Python
      avg_post_length = db.session.query(func.avg(func.length(Post.body)).label('avg_length')).scalar()
      

      استفاده از HAVING برای فیلتر کردن بر اساس Aggregations

      Python
      # پیدا کردن کاربران با بیش از ۵ پست
      users_with_many_posts = db.session.query(User, func.count(Post.id).label('post_count'))
                                          .join(Post)
                                          .group_by(User.id)
                                          .having(func.count(Post.id) > ۵)
                                          .all()
      

      نکات مهم

      • عملکرد: برای بهبود عملکرد، از شاخص‌ها استفاده کنید و پرس‌جوها را بهینه کنید.
      • خوانایی: پرس‌جوهای خود را واضح و مختصر بنویسید.
      • مدیریت خطا: خطاهای احتمالی را به درستی مدیریت کنید.
      • تست: پرس‌جوهای خود را به دقت تست کنید.

      با استفاده از Aggregations، می‌توانید داده‌های مفید و خلاصه‌ای از پایگاه داده خود استخراج کنید.

  • Migration:

    • مدیریت تغییرات در ساختار پایگاه داده با SQLAlchemy

      مقدمه

      پایگاه‌های داده به عنوان قلب تپنده‌ی بسیاری از برنامه‌ها، همواره در حال تغییر و تکامل هستند. اضافه کردن ستون جدید، تغییر نوع داده یک ستون، حذف یک جدول و … از جمله تغییراتی هستند که ممکن است در طول عمر یک برنامه با آن‌ها روبرو شویم. SQLAlchemy ابزاری قدرتمند برای تعریف و مدیریت مدل‌های پایگاه داده در پایتون است و به ما امکان می‌دهد تا این تغییرات را به صورت برنامه‌ریزی شده و ایمن اعمال کنیم.

      چالش‌های مدیریت تغییرات

      • تغییرات دستی در ساختار: اعمال تغییرات دستی در ساختار پایگاه داده مستعد خطا بوده و می‌تواند منجر به از دست رفتن داده‌ها شود.
      • هماهنگی با کد موجود: تغییرات در ساختار پایگاه داده باید با کد موجود هماهنگ باشد تا از بروز خطا جلوگیری شود.
      • کنترل نسخه: داشتن یک سیستم برای ردیابی تغییرات و بازگرداندن به نسخه‌های قبلی بسیار مهم است.

      راهکارهای SQLAlchemy

      SQLAlchemy ابزارهایی را برای مدیریت تغییرات در ساختار پایگاه داده فراهم می‌کند که عبارتند از:

      • تعریف مدل‌ها: با تعریف مدل‌های پایتون، ساختار پایگاه داده را به صورت شی‌گرا تعریف می‌کنیم. هر تغییر در مدل، به صورت خودکار به ساختار پایگاه داده منعکس می‌شود.
      • مهاجرت‌ها (Migrations): مهاجرت‌ها اسکریپت‌هایی هستند که تغییرات در ساختار پایگاه داده را تعریف می‌کنند. با اجرای مهاجرت‌ها، این تغییرات به صورت اتوماتیک در پایگاه داده اعمال می‌شوند.
      • Alembic: Alembic یک ابزار محبوب برای مدیریت مهاجرت‌ها در SQLAlchemy است. با استفاده از Alembic می‌توانیم مهاجرت‌ها را ایجاد، ویرایش و اجرا کنیم.

      مراحل مدیریت تغییرات

      1. تعریف مدل جدید: مدل جدید را در فایل مدل‌های SQLAlchemy تعریف کنید.
      2. ایجاد مهاجرت: با استفاده از Alembic، یک مهاجرت جدید ایجاد کنید. این مهاجرت تغییرات لازم برای اعمال در پایگاه داده را تعریف می‌کند.
      3. اجرای مهاجرت: مهاجرت ایجاد شده را اجرا کنید. Alembic به صورت خودکار تغییرات را در پایگاه داده اعمال می‌کند.

      مثال

      فرض کنید می‌خواهیم یک ستون جدید به نام is_active با نوع داده‌ی بولین به جدول User اضافه کنیم.

      Python
      # models.py
      from sqlalchemy import Column, Boolean
      
      class User(db.Model):
          # ...
          is_active = Column(Boolean, default=True)
      
      Bash
      # ترمینال
      alembic revision -m "Add is_active column to User"
      alembic upgrade head
      

      مزایای استفاده از SQLAlchemy برای مدیریت تغییرات

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

      نکات مهم

      • تست کردن: قبل از اعمال تغییرات در محیط تولید، آن‌ها را در محیط توسعه به خوبی تست کنید.
      • کنترل نسخه: تغییرات در مدل‌ها و مهاجرت‌ها را تحت کنترل نسخه قرار دهید.
      • مستندسازی: مهاجرت‌های خود را به خوبی مستند کنید تا در آینده قابل درک باشند.

      جمع‌بندی

      SQLAlchemy ابزاری قدرتمند برای مدیریت تغییرات در ساختار پایگاه داده است. با استفاده از مهاجرت‌ها و Alembic، می‌توانیم تغییرات را به صورت برنامه‌ریزی شده و ایمن اعمال کنیم و از یکپارچگی داده‌های خود محافظت کنیم.

       

    • ابزارهای Migration در SQLAlchemy

      SQLAlchemy به همراه ابزارهای قدرتمندی برای مدیریت تغییرات ساختاری در پایگاه داده ارائه می‌شود که به آنها Migration می‌گویند. این ابزارها کمک می‌کنند تا تغییرات به صورت کنترل‌شده و بدون خطا اعمال شوند.

      اصلی‌ترین ابزار Migration در SQLAlchemy: Alembic

      Alembic یک ابزار مستقل برای مدیریت تغییرات در ساختار پایگاه داده است که به طور گسترده‌ای با SQLAlchemy استفاده می‌شود. به طور کلی فرآیند کار با Alembic به این صورت است:

      1. ایجاد یک مهاجرت جدید:

        Bash
        alembic revision -m "Add a new column to the User table"
        
      2. نوشتن تغییرات در اسکریپت مهاجرت: Alembic یک فایل Python ایجاد می‌کند که در آن می‌توانیم تغییرات را به صورت دستورات SQL یا با استفاده از ORM SQLAlchemy تعریف کنیم.

      3. اجرای مهاجرت:

        Bash
        alembic upgrade head
        
      4. برگشت تغییرات (Optional):

        Bash
        alembic downgrade -1
        

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

      • کنترل نسخه تغییرات: هر مهاجرت یک نسخه خاص از ساختار پایگاه داده را نشان می‌دهد.
      • تکرارپذیری: مهاجرت‌ها می‌توانند به دفعات اجرا شوند.
      • ایمنی: تغییرات به صورت مرحله به مرحله اعمال می‌شوند و می‌توان در صورت نیاز به عقب برگشت.
      • هماهنگی با SQLAlchemy: Alembic به خوبی با SQLAlchemy ادغام می‌شود و امکان استفاده از ORM برای تعریف تغییرات را فراهم می‌کند.

      نکات مهم:

      • تست دقیق: قبل از اعمال تغییرات در محیط تولید، آن‌ها را در محیط توسعه به خوبی تست کنید.
      • کنترل نسخه: تغییرات در مدل‌ها و مهاجرت‌ها را تحت کنترل نسخه قرار دهید.
      • مستندسازی: مهاجرت‌ها را به خوبی مستند کنید تا در آینده قابل درک باشند.
      • برنامه‌ریزی تغییرات بزرگ: برای تغییرات بزرگ، ممکن است به چندین مهاجرت نیاز باشد. برنامه‌ریزی دقیق کمک می‌کند تا تغییرات به صورت مرحله‌ای و کنترل شده اعمال شوند.

      ملاحظات دیگر:

      • تغییرات داده: اگر تغییرات در ساختار پایگاه داده نیاز به تغییر داده‌ها داشته باشد، باید در اسکریپت‌های مهاجرت مربوطه کد لازم برای به‌روزرسانی داده‌ها را اضافه کنید.
      • تست واحد برای مهاجرت‌ها: اگرچه تست واحد برای مهاجرت‌ها معمول نیست، در برخی موارد ممکن است مفید باشد.

      با استفاده از Alembic و SQLAlchemy، می‌توان تغییرات در ساختار پایگاه داده را به صورت ایمن و کنترل‌شده انجام داد. همیشه به یاد داشته باشید که تست کردن و برنامه‌ریزی دقیق نقش مهمی در موفقیت این فرآیند دارند.

    • ایجاد، ویرایش و حذف جداول با SQLAlchemy

      SQLAlchemy یک ORM قدرتمند در پایتون است که به شما اجازه می‌دهد تا با استفاده از کلاس‌ها و اشیاء، با پایگاه داده خود تعامل داشته باشید. یکی از قابلیت‌های مهم SQLAlchemy، امکان مدیریت ساختار پایگاه داده به صورت برنامه‌نویسی است. در این بخش، به نحوه ایجاد، ویرایش و حذف جداول با استفاده از SQLAlchemy خواهیم پرداخت.

      تعریف مدل‌ها (Tables)

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

      Python
      from sqlalchemy import Column, Integer, String
      from sqlalchemy.ext.declarative import declarative_base
      
      Base = declarative_base()
      
      class User(Base):
          __tablename__ = 'users'
          id = Column(Integer, primary_key=True)
          name = Column(String(۵۰), nullable=False)
          email = Column(String(۱۲۰), unique=True, nullable=False)
      

      در این مثال، کلاس User یک جدول به نام users را تعریف می‌کند که دارای سه ستون id, name و email است.

      ایجاد جدول

      برای ایجاد جدول در پایگاه داده، باید از تابع create_all() استفاده کنیم:

      Python
      from sqlalchemy import create_engine
      
      engine = create_engine('sqlite:///mydatabase.db')
      Base.metadata.create_all(engine)
      

      این کد یک پایگاه داده SQLite به نام mydatabase.db ایجاد می‌کند و جدول users را در آن می‌سازد.

      ویرایش جدول

      برای ویرایش یک جدول، می‌توانیم به مدل مربوطه تغییرات اعمال کنیم و سپس مجدداً create_all() را اجرا کنیم. با این حال، این روش برای تغییرات جزئی مناسب نیست و می‌تواند منجر به از دست رفتن داده‌ها شود.

      روش بهتر: استفاده از Alembic

      Alembic یک ابزار محبوب برای مدیریت مهاجرت‌ها در SQLAlchemy است. با استفاده از Alembic می‌توان تغییرات در ساختار پایگاه داده را به صورت مرحله‌ای و کنترل‌شده اعمال کرد.

      Bash
      # ایجاد یک مهاجرت جدید
      alembic revision -m "Add a new column to the User table"
      
      # ویرایش فایل مهاجرت ایجاد شده
      
      # اجرای مهاجرت
      alembic upgrade head
      

      حذف جدول

      برای حذف یک جدول، می‌توانیم از تابع drop_all() استفاده کنیم. اما بهتر است از Alembic برای این کار نیز استفاده کنیم تا تغییرات به صورت کنترل‌شده اعمال شوند.

      Bash
      # ایجاد یک مهاجرت برای حذف جدول
      alembic revision -m "Drop the User table"
      
      # ویرایش فایل مهاجرت ایجاد شده
      
      # اجرای مهاجرت
      alembic upgrade head
      

      نکات مهم:

      • تست کردن: قبل از اعمال تغییرات در محیط تولید، آن‌ها را در محیط توسعه به خوبی تست کنید.
      • کنترل نسخه: تغییرات در مدل‌ها و مهاجرت‌ها را تحت کنترل نسخه قرار دهید.
      • مستندسازی: مهاجرت‌ها را به خوبی مستند کنید تا در آینده قابل درک باشند.
      • برنامه‌ریزی تغییرات بزرگ: برای تغییرات بزرگ، ممکن است به چندین مهاجرت نیاز باشد. برنامه‌ریزی دقیق کمک می‌کند تا تغییرات به صورت مرحله‌ای و کنترل‌شده اعمال شوند.

      ملاحظات دیگر:

      • تغییرات داده: اگر تغییرات در ساختار پایگاه داده نیاز به تغییر داده‌ها داشته باشد، باید در اسکریپت‌های مهاجرت مربوطه کد لازم برای به‌روزرسانی داده‌ها را اضافه کنید.
      • تست واحد برای مهاجرت‌ها: اگرچه تست واحد برای مهاجرت‌ها معمول نیست، در برخی موارد ممکن است مفید باشد.

      نتیجه‌گیری

      SQLAlchemy به همراه Alembic ابزاری قدرتمند برای مدیریت ساختار پایگاه داده به صورت برنامه‌نویسی فراهم می‌کند. با استفاده از این ابزارها، می‌توان تغییرات در ساختار پایگاه داده را به صورت ایمن و کنترل‌شده اعمال کرد.

       

بخش سوم: رابطه‌های بین مدل‌ها

  • روابط One-to-One

    • روابط یک به یک در SQLAlchemy

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

      تعریف رابطه یک به یک

      برای تعریف رابطه یک به یک در SQLAlchemy، از ویژگی relationship() استفاده می‌کنیم. این ویژگی به ما اجازه می‌دهد تا یک مدل را به مدل دیگری مرتبط کنیم.

      Python
      from sqlalchemy import Column, Integer, ForeignKey
      from sqlalchemy.orm import relationship
      
      Base = declarative_base()
      
      class User(Base):
          __tablename__ = 'users'
          id = Column(Integer, primary_key=True)
          name = Column(String(۵۰))
          profile = relationship("Profile", back_populates="user")
      
      class Profile(Base):
          __tablename__ = 'profiles'
          id = Column(Integer, primary_key=True)
          user_id = Column(Integer, ForeignKey('users.id'))
          bio = Column(String(۲۵۵))
          user = relationship("User", back_populates="profile")
      

      در این مثال، ما دو مدل User و Profile داریم. هر کاربر (User) تنها یک پروفایل (Profile) می‌تواند داشته باشد و هر پروفایل نیز تنها به یک کاربر تعلق دارد.

      • back_populates: این آرگومان برای ایجاد یک رابطه دو طرفه استفاده می‌شود. با استفاده از آن، می‌توانیم از هر دو طرف رابطه به اطلاعات مرتبط دسترسی پیدا کنیم.

      دسترسی به داده‌های مرتبط

      بعد از تعریف رابطه، می‌توانیم به راحتی به داده‌های مرتبط دسترسی پیدا کنیم:

      Python
      # ایجاد یک کاربر و پروفایل
      user = User(name="Ali")
      profile = Profile(bio="This is my bio")
      user.profile = profile
      
      # دسترسی به پروفایل کاربر
      print(user.profile.bio)  # خروجی: This is my bio
      
      # دسترسی به کاربر از طریق پروفایل
      print(profile.user.name)  # خروجی: Ali
      

      انواع روابط یک به یک

      در SQLAlchemy، دو نوع اصلی رابطه یک به یک وجود دارد:

      • یک به یک با کلید خارجی در یک جدول: در مثال بالا، کلید خارجی در جدول Profile قرار دارد. این بدان معنی است که هر رکورد در Profile باید به یک رکورد در User اشاره کند.
      • یک به یک با کلید خارجی در هر دو جدول: در این حالت، هر دو جدول دارای یک کلید خارجی هستند که به جدول دیگر اشاره می‌کند. این نوع رابطه معمولاً زمانی استفاده می‌شود که می‌خواهیم هر دو جدول را به عنوان مرجع اصلی در نظر بگیریم.

      نکات مهم

      • کلید خارجی: کلید خارجی ستونی است که به کلید اصلی جدول دیگری اشاره می‌کند.
      • back_populates: استفاده از back_populates برای ایجاد روابط دو طرفه بسیار مهم است.
      • عملکرد: انتخاب نوع رابطه یک به یک بستگی به طراحی پایگاه داده و نیازهای عملکردی شما دارد.
      • تکرارپذیری: اطمینان حاصل کنید که رابطه یک به یک به درستی تعریف شده است تا از ایجاد داده‌های تکراری جلوگیری شود.

      جمع‌بندی

      روابط یک به یک در SQLAlchemy به شما اجازه می‌دهند تا ارتباط بین اشیاء را به صورت طبیعی و شهودی مدل‌سازی کنید. با استفاده از این روابط، می‌توانید داده‌های پیچیده را به صورت منظم و قابل مدیریت سازماندهی کنید.

       

    • استفاده از backref در SQLAlchemy

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

      چرا از backref استفاده می‌کنیم؟

      • سادگی و خوانایی کد: با استفاده از backref، شما نیازی به تعریف مجدد رابطه در کلاس دیگر ندارید.
      • کاهش خطا: backref به شما کمک می‌کند تا روابط بین اشیاء را به صورت یکپارچه مدیریت کنید و از بروز خطاهای احتمالی جلوگیری کنید.
      • بهبود خوانایی کد: استفاده از backref باعث می‌شود که کد شما خواناتر و قابل درک‌تر شود.

      مثال:

      فرض کنید می‌خواهیم یک رابطه یک به یک بین یک کاربر (User) و پروفایل (Profile) تعریف کنیم.

      Python
      from sqlalchemy import Column, Integer, ForeignKey
      from sqlalchemy.orm import relationship
      
      Base = declarative_base()
      
      class User(Base):
          __tablename__ = 'users'
          id = Column(Integer, primary_key=True)
          name = Column(String(۵۰))
          profile = relationship("Profile", backref="user")
      
      class Profile(Base):
          __tablename__ = 'profiles'
          id = Column(Integer, primary_key=True)
          user_id = Column(Integer, ForeignKey('users.id'))
          bio = Column(String(۲۵۵))
      

      در این مثال:

      • relationship("Profile", backref="user") در کلاس User به این معنی است که هر کاربر یک پروفایل دارد و می‌توانیم به آن از طریق user.profile دسترسی پیدا کنیم.
      • backref="user" یک رابطه معکوس در کلاس Profile ایجاد می‌کند. به این معنی که هر پروفایل به یک کاربر تعلق دارد و می‌توانیم به آن از طریق profile.user دسترسی پیدا کنیم.

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

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

      تفاوت بین backref و back_populates:

      • backref: یک روش قدیمی‌تر برای تعریف روابط دو طرفه است.
      • back_populates: روش جدیدتر و توصیه شده‌تر برای تعریف روابط دو طرفه است. back_populates به شما امکان می‌دهد تا روابط را به صورت دقیق‌تر و انعطاف‌پذیرتری تعریف کنید.

      توصیه می‌شود از back_populates به جای backref استفاده کنید.

      مثال با استفاده از back_populates:

      Python
      class User(Base):
          __tablename__ = 'users'
          id = Column(Integer, primary_key=True)
          name = Column(String(۵۰))
          profile = relationship("Profile", back_populates="user")
      
      class Profile(Base):
          __tablename__ = 'profiles'
          id = Column(Integer, primary_key=True)
          user_id = Column(Integer, ForeignKey('users.id'))
          bio = Column(String(۲۵۵))
          user = relationship("User", back_populates="profile")
      

      جمع‌بندی

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

       

  • روابط One-to-Many

    • روابط یک به چند در SQLAlchemy

      در پایگاه داده‌های رابطه‌ای، روابط بین جداول مختلف نقش مهمی ایفا می‌کنند. یکی از انواع رایج این روابط، رابطه یک به چند است. در این رابطه، یک رکورد در یک جدول می‌تواند به چندین رکورد در جدول دیگر مرتبط باشد. برای مثال، یک نویسنده می‌تواند چندین کتاب بنویسد، یا یک مشتری می‌تواند چندین سفارش داشته باشد.

      تعریف رابطه یک به چند در SQLAlchemy

      برای تعریف رابطه یک به چند در SQLAlchemy، از ویژگی relationship() استفاده می‌کنیم. این ویژگی به ما اجازه می‌دهد تا یک مدل را به مدل دیگری مرتبط کنیم.

      Python
      from sqlalchemy import Column, Integer, ForeignKey
      from sqlalchemy.orm import relationship
      
      Base = declarative_base()
      
      class Author(Base):
          __tablename__ = 'authors'
          id = Column(Integer, primary_key=True)
          name = Column(String(۵۰))
          books = relationship("Book", back_populates="author")
      
      class Book(Base):
          __tablename__ = 'books'
          id = Column(Integer, primary_key=True)
          title = Column(String(۱۰۰))
          author_id = Column(Integer, ForeignKey('authors.id'))
          author = relationship("Author", back_populates="books")
      

      در این مثال:

      • Author یک نویسنده را نشان می‌دهد.
      • Book یک کتاب را نشان می‌دهد.
      • هر نویسنده می‌تواند چندین کتاب داشته باشد، بنابراین در کلاس Author یک رابطه به نام books تعریف کرده‌ایم که به لیستی از اشیاء Book اشاره می‌کند.
      • هر کتاب به یک نویسنده تعلق دارد، بنابراین در کلاس Book یک ستون author_id به عنوان کلید خارجی تعریف شده است.

      دسترسی به داده‌های مرتبط

      بعد از تعریف رابطه، می‌توانیم به راحتی به داده‌های مرتبط دسترسی پیدا کنیم:

      Python
      # ایجاد یک نویسنده و چندین کتاب
      author = Author(name="J.K. Rowling")
      book1 = Book(title="Harry Potter and the Philosopher's Stone")
      book2 = Book(title="Harry Potter and the Chamber of Secrets")
      author.books.append(book1)
      author.books.append(book2)
      
      # دسترسی به کتاب‌های یک نویسنده
      for book in author.books:
          print(book.title)
      

      نکات مهم

      • کلید خارجی: کلید خارجی ستونی است که به کلید اصلی جدول دیگری اشاره می‌کند. در این مثال، author_id در جدول books کلید خارجی است.
      • back_populates: این آرگومان برای ایجاد یک رابطه دو طرفه استفاده می‌شود. با استفاده از آن، می‌توانیم از هر دو طرف رابطه به اطلاعات مرتبط دسترسی پیدا کنیم.
      • عملکرد: انتخاب نوع رابطه یک به چند بستگی به طراحی پایگاه داده و نیازهای عملکردی شما دارد.
      • تکرارپذیری: اطمینان حاصل کنید که رابطه یک به چند به درستی تعریف شده است تا از ایجاد داده‌های تکراری جلوگیری شود.

      جمع‌بندی

      روابط یک به چند در SQLAlchemy به شما اجازه می‌دهند تا ارتباط بین اشیاء را به صورت طبیعی و شهودی مدل‌سازی کنید. با استفاده از این روابط، می‌توانید داده‌های پیچیده را به صورت منظم و قابل مدیریت سازماندهی کنید.

       

    • استفاده از backref در روابط یک به چند در SQLAlchemy

      backref در SQLAlchemy ابزاری قدرتمند برای تعریف روابط دو طرفه بین اشیاء است. وقتی شما یک رابطه یک به چند را تعریف می‌کنید، backref به شما اجازه می‌دهد تا از طرف “چند” رابطه به طرف “یک” دسترسی پیدا کنید. به عبارت دیگر، با استفاده از backref شما می‌توانید به راحتی از یک شیء به اشیاء مرتبط با آن دسترسی داشته باشید.

      مثال:

      فرض کنید می‌خواهیم یک رابطه یک به چند بین یک نویسنده (Author) و کتاب‌های او (Book) تعریف کنیم:

      Python
      from sqlalchemy import Column, Integer, ForeignKey
      from sqlalchemy.orm import relationship
      
      Base = declarative_base()
      
      class Author(Base):
          __tablename__ = 'authors'
          id = Column(Integer, primary_key=True)
          name = Column(String(۵۰))
          books = relationship("Book", back_populates="author")
      
      class Book(Base):
          __tablename__ = 'books'
          id = Column(Integer, primary_key=True)
          title = Column(String(۱۰۰))
          author_id = Column(Integer, ForeignKey('authors.id'))
          author = relationship("Author", back_populates="books")
      

      در این مثال:

      • Author یک نویسنده را نشان می‌دهد.
      • Book یک کتاب را نشان می‌دهد.
      • هر نویسنده می‌تواند چندین کتاب داشته باشد، بنابراین در کلاس Author یک رابطه به نام books تعریف کرده‌ایم که به لیستی از اشیاء Book اشاره می‌کند.
      • هر کتاب به یک نویسنده تعلق دارد، بنابراین در کلاس Book یک ستون author_id به عنوان کلید خارجی تعریف شده است.

      backref چگونه کار می‌کند؟

      • back_populates="books" در کلاس Author به این معنی است که هر نویسنده یک لیست از کتاب‌ها دارد و می‌توانیم به این لیست از طریق author.books دسترسی پیدا کنیم.
      • back_populates="author" در کلاس Book به این معنی است که هر کتاب به یک نویسنده تعلق دارد و می‌توانیم به این نویسنده از طریق book.author دسترسی پیدا کنیم.

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

      • رابطه دو طرفه: با استفاده از backref، شما می‌توانید از هر دو طرف رابطه به اطلاعات مرتبط دسترسی پیدا کنید.
      • سادگی: تعریف روابط بسیار ساده‌تر می‌شود.
      • بهبود خوانایی کد: کد شما خواناتر و قابل درک‌تر می‌شود.

      چرا از backref استفاده کنیم؟

      • کاهش تکرار کد: نیازی نیست که رابطه را در هر دو طرف تعریف کنید.
      • بهبود عملکرد: در برخی موارد، استفاده از backref می‌تواند باعث بهبود عملکرد پرس‌جوها شود.
      • مدیریت بهتر روابط: backref به شما کمک می‌کند تا روابط بین اشیاء را به صورت یکپارچه مدیریت کنید.

      تفاوت بین backref و back_populates:

      • backref: یک روش قدیمی‌تر برای تعریف روابط دو طرفه است.
      • back_populates: روش جدیدتر و توصیه شده‌تر برای تعریف روابط دو طرفه است. back_populates به شما امکان می‌دهد تا روابط را به صورت دقیق‌تر و انعطاف‌پذیرتری تعریف کنید.

      توصیه می‌شود از back_populates به جای backref استفاده کنید.

      جمع‌بندی

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

       

  • روابط Many-to-Many

    • روابط چند به چند در SQLAlchemy

      تعریف رابطه چند به چند

      در پایگاه داده‌های رابطه‌ای، رابطه چند به چند به این معناست که یک رکورد در یک جدول می‌تواند به چندین رکورد در جدول دیگر مرتبط باشد و بالعکس. برای مثال، یک دانشجو می‌تواند در چندین کلاس ثبت‌نام کند و هر کلاس نیز می‌تواند چندین دانشجو داشته باشد.

      چالش در پیاده‌سازی مستقیم رابطه چند به چند

      در نگاه اول ممکن است بخواهیم این رابطه را با اضافه کردن یک کلید خارجی به هر دو جدول پیاده‌سازی کنیم. اما این روش باعث ایجاد مشکلات نرمال‌سازی و پیچیدگی‌های اضافی می‌شود.

      راه حل: جدول واسط

      برای حل این مشکل، از یک جدول واسط استفاده می‌کنیم. این جدول شامل کلیدهای اصلی هر دو جدول اصلی به عنوان کلید خارجی است. در مثال دانشجو و کلاس، جدول واسط شامل ستون‌هایی برای شناسه دانشجو و شناسه کلاس خواهد بود.

      پیاده‌سازی در SQLAlchemy

      Python
      from sqlalchemy import Column, Integer, ForeignKey
      from sqlalchemy.orm import relationship
      
      Base = declarative_base()
      
      class Student(Base):
          __tablename__ = 'students'
          id = Column(Integer, primary_key=True)
          name = Column(String(۵۰))
          courses = relationship("Course", secondary="enrollments", back_populates="students")
      
      class Course(Base):
          __tablename__ = 'courses'
          id = Column(Integer, primary_key=True)
          name = Column(String(۱۰۰))
          students = relationship("Student", secondary="enrollments", back_populates="courses")
      
      class Enrollment(Base):
          __tablename__ = 'enrollments'
          student_id = Column(Integer, ForeignKey('students.id'), primary_key=True)
          course_id = Column(Integer, ForeignKey('courses.id'), primary_key=True)
      

      در این مثال:

      • Student: جدول دانشجویان
      • Course: جدول دوره‌ها
      • Enrollment: جدول واسط که ارتباط بین دانشجو و دوره را نشان می‌دهد.
      • secondary="enrollments": به SQLAlchemy می‌گوید که از جدول Enrollment به عنوان جدول واسط برای این رابطه استفاده کند.
      • back_populates: برای ایجاد یک رابطه دو طرفه استفاده می‌شود.

      دسترسی به داده‌های مرتبط

      Python
      # ایجاد یک دانشجو و دو دوره
      student1 = Student(name="Ali")
      course1 = Course(name="Math")
      course2 = Course(name="Physics")
      
      # ثبت‌نام دانشجو در دوره‌ها
      student1.courses.append(course1)
      student1.courses.append(course2)
      
      # دسترسی به دوره‌های یک دانشجو
      for course in student1.courses:
          print(course.name)
      

      مزایای استفاده از جدول واسط

      • نرمال‌سازی داده‌ها: هر جدول یک مسئولیت مشخص دارد.
      • انعطاف‌پذیری: می‌توانید به راحتی اطلاعات اضافی مانند نمره دانشجو در هر دوره را به جدول واسط اضافه کنید.
      • جلوگیری از تکرار داده‌ها: از تکرار اطلاعات در جداول اصلی جلوگیری می‌شود.

      نکات مهم

      • کلید اصلی مرکب: در جدول واسط، ترکیبی از student_id و course_id به عنوان کلید اصلی استفاده می‌شود.
      • back_populates: برای ایجاد یک رابطه دو طرفه بین جداول اصلی و جدول واسط استفاده می‌شود.
      • عملکرد: انتخاب نوع رابطه چند به چند بستگی به طراحی پایگاه داده و نیازهای عملکردی شما دارد.

      جمع‌بندی

      روابط چند به چند در SQLAlchemy با استفاده از جدول واسط پیاده‌سازی می‌شوند. این روش بهترین راه برای مدل‌سازی روابط پیچیده بین موجودیت‌ها است و به شما اجازه می‌دهد تا داده‌های خود را به صورت منظم و قابل مدیریت سازماندهی کنید.

       

    • استفاده از جدول ارتباطی در روابط چند به چند در SQLAlchemy

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

      چرا از جدول ارتباطی استفاده می‌کنیم؟

      • نرمال‌سازی داده‌ها: هر جدول مسئولیت مشخصی دارد و از تکرار داده‌ها جلوگیری می‌شود.
      • انعطاف‌پذیری: می‌توان اطلاعات اضافی در مورد ارتباط بین دو جدول را در جدول ارتباطی ذخیره کرد (مثلاً تاریخ ثبت‌نام یک دانشجو در یک دوره).
      • مدیریت آسان‌تر: تغییرات در روابط را می‌توان به راحتی در جدول ارتباطی اعمال کرد.

      نحوه تعریف جدول ارتباطی در SQLAlchemy:

      در مثال قبلی، جدول Enrollment به عنوان جدول ارتباطی بین Student و Course تعریف شد. هر ردیف در جدول Enrollment نشان می‌دهد که یک دانشجو در یک دوره خاص ثبت‌نام کرده است.

      Python
      class Enrollment(Base):
          __tablename__ = 'enrollments'
          student_id = Column(Integer, ForeignKey('students.id'), primary_key=True)
          course_id = Column(Integer, ForeignKey('courses.id'), primary_key=True)
      
      • کلید اصلی مرکب: ترکیبی از student_id و course_id به عنوان کلید اصلی جدول Enrollment عمل می‌کند. این نشان می‌دهد که هر ترکیب منحصر به فرد از دانشجو و دوره تنها یک بار می‌تواند در جدول وجود داشته باشد.

      مزایای استفاده از جدول ارتباطی در SQLAlchemy:

      • سادگی: تعریف روابط چند به چند با استفاده از جدول ارتباطی بسیار ساده است.
      • انعطاف‌پذیری: می‌توانید به راحتی انواع مختلفی از روابط چند به چند را مدل‌سازی کنید.
      • خوانایی کد: کد شما خواناتر و قابل درک‌تر می‌شود.

      مثال پیشرفته‌تر:

      فرض کنید می‌خواهیم اطلاعات اضافی مانند نمره دانشجو در هر دوره را نیز ذخیره کنیم:

      Python
      class Enrollment(Base):
          __tablename__ = 'enrollments'
          student_id = Column(Integer, ForeignKey('students.id'), primary_key=True)
          course_id = Column(Integer, ForeignKey('courses.id'), primary_key=True)
          grade = Column(Integer)
      

      با اضافه کردن ستون grade به جدول Enrollment، می‌توانیم نمره هر دانشجو در هر دوره را ذخیره کنیم.

      جمع‌بندی

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

      نکات مهم:

      • نام‌گذاری جدول ارتباطی: نام جدول ارتباطی باید نشان دهنده ارتباط بین دو جدول اصلی باشد.
      • کلید اصلی مرکب: اطمینان حاصل کنید که کلید اصلی جدول ارتباطی به درستی تعریف شده باشد.
      • back_populates: برای ایجاد روابط دو طرفه بین جداول اصلی و جدول ارتباطی استفاده می‌شود.

       

بخش چهارم: موضوعات پیشرفته

  • Inheritance:

    • وراثت در مدل‌های SQLAlchemy

      وراثت در برنامه‌نویسی شیء‌گرا، مکانیزمی است که به کلاس‌ها اجازه می‌دهد ویژگی‌ها و رفتارهای کلاس‌های دیگر را به ارث ببرند. در SQLAlchemy نیز، این مفهوم به ما اجازه می‌دهد تا مدل‌های پیچیده‌تری را با استفاده از وراثت ایجاد کنیم.

      چرا از وراثت در SQLAlchemy استفاده می‌کنیم؟

      • کاهش تکرار کد: ویژگی‌های مشترک بین چندین مدل را می‌توان در یک کلاس پایه تعریف کرد و سپس کلاس‌های فرزند این ویژگی‌ها را به ارث ببرند.
      • سلسله مراتب کلاس: ایجاد سلسله مراتب کلاس‌ها به ما کمک می‌کند تا مدل داده‌ای را بهتر سازماندهی و درک کنیم.
      • پلی‌مورفیسم: امکان ایجاد رفتارهای مختلف برای کلاس‌های فرزند با استفاده از روش‌های یکسان را فراهم می‌کند.

      انواع وراثت در SQLAlchemy

      SQLAlchemy از سه نوع وراثت پشتیبانی می‌کند:

      1. وراثت تک جدولی (Single Table Inheritance – STI):

        • همه کلاس‌ها در یک جدول ذخیره می‌شوند.
        • یک ستون تبعیض‌گر (discriminator) برای تشخیص نوع هر رکورد استفاده می‌شود.
        • مناسب برای مواردی که تفاوت بین کلاس‌ها در تعداد کمی ستون است.
      2. وراثت جدول‌های مجزا (Joined Table Inheritance – JTI):

        • هر کلاس یک جدول مجزا دارد.
        • کلاس پایه دارای یک ستون تبعیض‌گر است.
        • کلاس‌های فرزند دارای کلید خارجی به جدول کلاس پایه هستند.
        • مناسب برای مواردی که کلاس‌های فرزند دارای ستون‌های منحصر به فرد زیادی هستند.
      3. وراثت جدول‌های بتنی (Concrete Table Inheritance – CTI):

        • هر کلاس یک جدول مجزا دارد.
        • هیچ ستون تبعیض‌گری وجود ندارد.
        • مناسب برای مواردی که کلاس‌های فرزند کاملاً مستقل هستند.

      مثال عملی (وراثت تک جدولی)

      Python
      from sqlalchemy import Column, Integer, String
      from sqlalchemy.ext.declarative import declarative_base
      
      Base = declarative_base()
      
      class User(Base):
          __tablename__ = 'users'
          id = Column(Integer, primary_key=True)
          name = Column(String(۵۰))
          type = Column(String(۲۰), nullable=False)  # ستون تبعیض‌گر
      
      class Employee(User):
          __tablename__ = None
          __mapper_args__ = {'polymorphic_identity': 'employee'}
          department = Column(String(۵۰))
      
      class Customer(User):
          __tablename__ = None
          __mapper_args__ = {'polymorphic_identity': 'customer'}
          address = Column(String(۱۰۰))
      

      در این مثال، Employee و Customer از کلاس User به ارث می‌برند. ستون type برای تشخیص اینکه یک رکورد مربوط به یک کارمند یا مشتری است استفاده می‌شود.

      نکات مهم

      • ستون تبعیض‌گر: این ستون برای تشخیص نوع هر رکورد استفاده می‌شود.
      • polymorphic_identity: این آرگومان مقدار ستون تبعیض‌گر برای هر کلاس را مشخص می‌کند.
      • کلاس‌های انتزاعی: کلاس‌هایی که فقط به عنوان پایه برای کلاس‌های دیگر استفاده می‌شوند، کلاس‌های انتزاعی نامیده می‌شوند.
      • پرس‌وجوهای چند شکل: با استفاده از وراثت، می‌توانیم پرس‌وجوهایی را بنویسیم که همزمان چندین نوع از یک کلاس را برگردانند.

      چه زمانی از وراثت استفاده کنیم؟

      • زمانی که چندین مدل دارای ویژگی‌های مشترک زیادی هستند.
      • زمانی که می‌خواهیم سلسله مراتب کلاس‌ها را ایجاد کنیم.
      • زمانی که می‌خواهیم از پلی‌مورفیسم استفاده کنیم.

      چه زمانی از وراثت استفاده نکنیم؟

      • زمانی که کلاس‌ها ارتباط نزدیکی با هم ندارند.
      • زمانی که وراثت باعث پیچیدگی بیش از حد کد می‌شود.

      جمع‌بندی

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

       

    • انواع وراثت در SQLAlchemy

      SQLAlchemy پشتیبانی می‌کند از سه نوع اصلی وراثت:

      1. Single Table Inheritance (STI):

        • ویژگی: همه کلاس‌ها در یک جدول ذخیره می‌شوند.
        • کاربرد: مناسب برای مواردی که تفاوت بین کلاس‌ها در تعداد کمی ستون است.
        • مثال:
          Python
          from sqlalchemy import Column, Integer, String
          from sqlalchemy.ext.declarative import declarative_base
          
          Base = declarative_base()
          
          class User(Base):
              __tablename__ = 'users'
              id = Column(Integer, primary_key=True)
              name = Column(String(۵۰))
              type = Column(String(۲۰), nullable=False)  # Discriminator column
          
          class Employee(User):
              __tablename__ = None
              __mapper_args__ = {'polymorphic_identity': 'employee'}
              department = Column(String(۵۰))
          
          class Customer(User):
              __tablename__ = None
              __mapper_args__ = {'polymorphic_identity': 'customer'}
              address = Column(String(۱۰۰))
          
      2. Joined Table Inheritance (JTI):

        • ویژگی: هر کلاس یک جدول مجزا دارد.
        • کاربرد: مناسب برای مواردی که کلاس‌های فرزند دارای ستون‌های منحصر به فرد زیادی هستند.
        • مثال:
          Python
          from sqlalchemy import Column, Integer, String, ForeignKey
          from sqlalchemy.orm import relationship
          
          Base = declarative_base()
          
          class User(Base):
              __tablename__ = 'users'
              id = Column(Integer, primary_key=True)
              name = Column(String(۵۰))
              type = Column(String(۲۰), nullable=False)  # Discriminator column
          
          class Employee(User):
              __tablename__ = 'employees'
              id = Column(Integer, ForeignKey('users.id'), primary_key=True)
              department = Column(String(۵۰))
          
          class Customer(User):
              __tablename__ = 'customers'
              id = Column(Integer, ForeignKey('users.id'), primary_key=True)
              address = Column(String(۱۰۰))
          
      3. Concrete Table Inheritance (CTI):

        • ویژگی: هر کلاس یک جدول مجزا دارد.
        • کاربرد: مناسب برای مواردی که کلاس‌های فرزند کاملاً مستقل هستند.
        • مثال:
          Python
          from sqlalchemy import Column, Integer, String
          
          Base = declarative_base()
          
          class User(Base):
              __tablename__ = 'users'
              id = Column(Integer, primary_key=True)
              name = Column(String(۵۰))
          
          class Employee(Base):
              __tablename__ = 'employees'
              id = Column(Integer, primary_key=True)
              name = Column(String(۵۰))
              department = Column(String(۵۰))
          
          class Customer(Base):
              __tablename__ = 'customers'
              id = Column(Integer, primary_key=True)
              name = Column(String(۵۰))
              address = Column(String(۱۰۰))
          

      Abstract Base Classes (ABCs)

      SQLAlchemy همچنین از کلاس‌های انتزاعی (abstract base classes) پشتیبانی می‌کند. این کلاس‌ها نمی‌توانند مستقیماً نمونه‌سازی شوند و به عنوان پایه برای کلاس‌های دیگر استفاده می‌شوند.

      Python
      from sqlalchemy import Column, Integer, String
      from sqlalchemy.ext.declarative import declarative_base
      from sqlalchemy.orm import declarative_base
      
      Base = declarative_base()
      
      class User(Base):
          __tablename__ = 'users'
          id = Column(Integer, primary_key=True)
          name = Column(String(۵۰))
      
          __mapper_args__ = {'polymorphic_identity': 'user'}
      
      class Employee(User):
          __tablename__ = 'employees'
          department = Column(String(۵۰))
      
      class Customer(User):
          __tablename__ = 'customers'
          address = Column(String(۱۰۰))
      

      در این مثال، User به عنوان یک کلاس پایه انتزاعی تعریف شده است. کلاس‌های Employee و Customer از User به ارث می‌برند و هر کدام جدول خود را دارند.

      انتخاب نوع وراثت مناسب:

      • STI: مناسب برای سلسله مراتب ساده و زمانی که تفاوت بین کلاس‌ها کوچک است.
      • JTI: مناسب برای سلسله مراتب پیچیده و زمانی که کلاس‌های فرزند دارای ستون‌های منحصر به فرد زیادی هستند.
      • CTI: مناسب برای کلاس‌های کاملاً مستقل که هیچ ارتباط مستقیم بین آن‌ها وجود ندارد.

      نکات مهم:

      • ستون تبعیض‌گر: در STI و JTI، ستون تبعیض‌گر برای تشخیص نوع رکورد استفاده می‌شود.
      • polymorphic_identity: این آرگومان مقدار ستون تبعیض‌گر را برای هر کلاس مشخص می‌کند.
      • پرس‌وجوهای چند شکلی: با استفاده از وراثت، می‌توانیم پرس‌وجوهایی را بنویسیم که همزمان چندین نوع از یک کلاس را برگردانند.

      با درک این مفاهیم، می‌توانید مدل‌های داده‌ای پیچیده و انعطاف‌پذیر را با استفاده از وراثت در SQLAlchemy طراحی کنید.

  • Polymorphic Queries:

    • پرس‌وجو از مدل‌های مشتق شده در SQLAlchemy

      پرس‌وجو از مدل‌های مشتق شده (Inherited Models) در SQLAlchemy به شما اجازه می‌دهد تا به طور مؤثر و انعطاف‌پذیر از سلسله مراتب کلاس‌ها و مدل‌های مرتبط با هم استفاده کنید. این کار به خصوص زمانی مفید است که شما با داده‌های پیچیده و سلسله مراتبی سروکار دارید.

      روش‌های پرس‌وجو از مدل‌های مشتق شده

      1. پرس‌وجو از کلاس پایه:

        • ساده‌ترین روش است. با پرس‌وجو از کلاس پایه، می‌توانید به تمامی نمونه‌های کلاس‌های فرزند نیز دسترسی پیدا کنید.
        • مثال:
          Python
          session.query(User).all()  # تمام کاربران (کارمندان و مشتریان) را برمی‌گرداند
          
      2. پرس‌وجو با استفاده از ستون تبعیض‌گر:

        • با استفاده از ستون تبعیض‌گر (discriminator column) می‌توانید به طور خاص نمونه‌های یک کلاس فرزند را انتخاب کنید.
        • مثال:
          Python
          session.query(User).filter_by(type='employee').all()  # تمام کارمندان را برمی‌گرداند
          
      3. پرس‌وجو مستقیم از کلاس فرزند:

        • برای پرس‌وجو از ویژگی‌های خاص یک کلاس فرزند، می‌توانید مستقیماً از آن کلاس پرس‌وجو کنید.
        • مثال:
          Python
          session.query(Employee).all()  # تمام کارمندان را برمی‌گرداند
          

      مثال عملی

      Python
      from sqlalchemy import Column, Integer, String
      from sqlalchemy.ext.declarative import declarative_base
      from sqlalchemy.orm import sessionmaker
      
      Base = declarative_base()
      
      class User(Base):
          __tablename__ = 'users'
          id = Column(Integer, primary_key=True)
          name = Column(String(۵۰))
          type = Column(String(۲۰), nullable=False)  # Discriminator column
      
      class Employee(User):
          __tablename__ = 'employees'
          id = Column(Integer, ForeignKey('users.id'), primary_key=True)
          department = Column(String(۵۰))
      
      class Customer(User):
          __tablename__ = 'customers'
          id = Column(Integer, ForeignKey('users.id'), primary_key=True)
          address = Column(String(۱۰۰))
      
      # ایجاد یک Session
      Session = sessionmaker(bind=engine)
      session = Session()
      
      # پرس‌وجوهای مختلف
      all_users = session.query(User).all()
      employees = session.query(User).filter_by(type='employee').all()
      customers_in_department = session.query(Employee).filter_by(department='sales').all()
      

      نکات مهم

      • ستون تبعیض‌گر: این ستون برای تشخیص نوع هر رکورد ضروری است.
      • polymorphic_identity: با استفاده از این آرگومان، مقدار ستون تبعیض‌گر برای هر کلاس مشخص می‌شود.
      • پرس‌وجوهای چند شکل: با استفاده از وراثت، می‌توانید پرس‌وجوهایی را بنویسید که همزمان چندین نوع از یک کلاس را برگردانند.
      • عملکرد: انتخاب نوع پرس‌وجو به ساختار پایگاه داده و نیازهای عملکردی شما بستگی دارد.

      مثال‌های پیشرفته‌تر

      • استفاده از join: برای ترکیب نتایج پرس‌وجو از چندین جدول.
      • استفاده از subqueries: برای ساخت پرس‌وجوهای پیچیده.
      • استفاده از ORM expressions: برای ساخت عبارات پیچیده در پرس‌وجوها.

      جمع‌بندی

      پرس‌وجو از مدل‌های مشتق شده در SQLAlchemy به شما امکان می‌دهد تا به طور انعطاف‌پذیر و کارآمد با داده‌های پیچیده و سلسله مراتبی کار کنید. با درک اصول اولیه وراثت و پرس‌وجو در SQLAlchemy، می‌توانید مدل‌های داده‌ای قدرتمندی ایجاد کنید.

       

  • Hybrid Attributes:

    • ویژگی‌های محاسباتی در SQLAlchemy: یک نگاه عمیق

      ویژگی‌های محاسباتی در SQLAlchemy به ما اجازه می‌دهند تا مقادیر را به صورت دینامیک محاسبه کنیم و آن‌ها را به عنوان ویژگی‌های یک مدل در نظر بگیریم. این ویژگی‌ها برخلاف ستون‌های پایگاه داده که به صورت مستقیم ذخیره می‌شوند، در زمان اجرا محاسبه می‌شوند.

      چرا از ویژگی‌های محاسباتی استفاده می‌کنیم؟

      • سادگی پرس‌وجو: بسیاری از محاسبات پیچیده را می‌توان به عنوان ویژگی‌های ساده تعریف کرد و از آن‌ها در پرس‌وجوها استفاده کرد.
      • کاهش تکراری شدن کد: عملیات محاسباتی پیچیده را می‌توان یک بار تعریف کرد و در چندین مکان استفاده کرد.
      • افزایش خوانایی کد: کد شما خواناتر و قابل درک‌تر می‌شود.
      • انعطاف‌پذیری: می‌توانید محاسبات پیچیده را به راحتی به مدل‌های خود اضافه کنید.

      انواع ویژگی‌های محاسباتی

      SQLAlchemy دو نوع اصلی ویژگی محاسباتی را پشتیبانی می‌کند:

      1. hybrid_property:

        • برای تعریف ویژگی‌هایی استفاده می‌شود که هم در هنگام دسترسی به شی و هم هنگام تعیین مقدار آن محاسبه می‌شوند.
        • مثال:
          Python
          from sqlalchemy import Column, Integer
          from sqlalchemy.ext.hybrid import hybrid_property
          
          class User(Base):
              id = Column(Integer, primary_key=True)
              first_name = Column(String(۵۰))
              last_name = Column(String(۵۰))
          
              @hybrid_property
              def full_name(self):
                  return f"{self.first_name} {self.last_name}"
          
      2. column_property:

        • برای تعریف ویژگی‌هایی استفاده می‌شود که بر اساس عبارت‌های SQL محاسبه می‌شوند.
        • مثال:
          Python
          from sqlalchemy import Column, Integer, func
          from sqlalchemy.orm import column_property
          
          class Order(Base):
              id = Column(Integer, primary_key=True)
              total_price = column_property(func.sum(order_items.c.price))
          

      مزایای استفاده از ویژگی‌های محاسباتی

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

      نکات مهم

      • عملکرد: برای محاسبات پیچیده، باید به عملکرد پرس‌وجوها توجه کنید.
      • به‌روزرسانی پایگاه داده: ویژگی‌های محاسباتی معمولاً در پایگاه داده ذخیره نمی‌شوند و در زمان اجرا محاسبه می‌شوند.
      • تغییرات در مدل: اگر ساختار مدل تغییر کند، ممکن است نیاز به تغییر تعریف ویژگی‌های محاسباتی باشد.

      مثال‌های پیشرفته‌تر

      • محاسبه سن بر اساس تاریخ تولد:
        Python
        from datetime import date
        
        @hybrid_property
        def age(self):
            return date.today().year - self.birth_date.year - ((date.today().month, date.today().day) < (self.birth_date.month, self.birth_date.day))
        
      • محاسبه میانگین قیمت محصولات:
        Python
        from sqlalchemy import func
        
        average_price = column_property(func.avg(products.c.price))
        

      جمع‌بندی

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

       

  • Events:

    • گوش دادن به رویدادهای SQLAlchemy: یک رویکرد جامع

      SQLAlchemy ابزاری قدرتمند برای تعامل با پایگاه داده‌ها است و امکانات متنوعی را برای مدیریت داده‌ها فراهم می‌کند. یکی از این امکانات، قابلیت گوش دادن به رویدادهای مختلفی است که در طول چرخه عمر یک شیء یا جلسه (session) رخ می‌دهد. این قابلیت به شما اجازه می‌دهد تا اقدامات سفارشی را در زمان وقوع رویدادها انجام دهید.

      چرا به رویدادهای SQLAlchemy گوش دهیم؟

      • ثبت تغییرات: می‌توانید تغییرات ایجاد شده در اشیاء را ثبت کنید.
      • اعمال منطق سفارشی: قبل یا بعد از انجام عملیات خاصی، منطق سفارشی خود را اجرا کنید.
      • ایجاد تریگرها: مشابه تریگرهای پایگاه داده، می‌توانید رویدادهایی را برای انجام عملیات خاص در زمان‌های مشخص تعریف کنید.
      • انجام عملیات پاکسازی: پس از حذف یک شیء، می‌توانید عملیات پاکسازی مرتبط را انجام دهید.

      انواع رویدادهای SQLAlchemy

      SQLAlchemy انواع مختلفی از رویدادها را پشتیبانی می‌کند که برخی از مهم‌ترین آن‌ها عبارتند از:

      • قبل از درج (before_insert): قبل از اینکه یک شیء جدید به پایگاه داده اضافه شود.
      • بعد از درج (after_insert): بعد از اینکه یک شیء جدید به پایگاه داده اضافه شد.
      • قبل از به روزرسانی (before_update): قبل از اینکه یک شیء موجود به روز شود.
      • بعد از به روزرسانی (after_update): بعد از اینکه یک شیء موجود به روز شود.
      • قبل از حذف (before_delete): قبل از اینکه یک شیء حذف شود.
      • بعد از حذف (after_delete): بعد از اینکه یک شیء حذف شد.

      نحوه گوش دادن به رویدادها

      برای گوش دادن به رویدادها، از متد event.listen استفاده می‌کنیم. این متد به شما اجازه می‌دهد تا یک تابع را به یک رویداد خاص متصل کنید.

      Python
      from sqlalchemy import event
      from sqlalchemy.orm import sessionmaker
      
      # ... تعریف مدل‌ها ...
      
      @event.listens_for(User, 'before_insert')
      def receive_before_insert(mapper, connection, target):
          print(f"کاربر جدید با نام {target.name} در حال افزوده شدن است.")
      
      # ایجاد یک Session
      Session = sessionmaker(bind=engine)
      session = Session()
      
      # ... عملیات ایجاد، به‌روزرسانی یا حذف اشیاء ...
      

      مثال عملی: ثبت تغییرات در یک مدل

      Python
      from sqlalchemy import Column, Integer, String, event
      from sqlalchemy.ext.declarative import declarative_base
      
      Base = declarative_base()
      
      class User(Base):
          __tablename__ = 'users'
          id = Column(Integer, primary_key=True)
          name = Column(String(۵۰))
      
          def __init__(self, name):
              self.name = name
      
      @event.listens_for(User, 'before_update')
      def receive_before_update(mapper, connection, target):
          print(f"کاربر {target.name} در حال ویرایش است.")
      
      @event.listens_for(User, 'after_delete')
      def receive_after_delete(mapper, connection, target):
          print(f"کاربر {target.name} حذف شد.")
      

      نکات مهم

      • مکان قرارگیری کد گوش دادن: معمولاً کد گوش دادن به رویدادها در یک فایل جداگانه یا در آغاز فایل تعریف مدل‌ها قرار می‌گیرد.
      • تابع گوش دادن: تابعی که به یک رویداد متصل می‌شود، معمولاً سه آرگومان دریافت می‌کند: mapper (نمایانگر کلاس مدل)، connection (اتصال به پایگاه داده) و target (شیء‌ای که رویداد برای آن رخ داده است).
      • چندین تابع گوش دادن: می‌توانید چندین تابع را به یک رویداد متصل کنید.
      • رویدادهای سطح Session: علاوه بر رویدادهای سطح شیء، می‌توانید به رویدادهای سطح Session نیز گوش دهید.

      کاربردهای پیشرفته

      • ایجاد تاریخچه تغییرات: با ثبت تغییرات در یک جدول جداگانه، می‌توانید تاریخچه تغییرات ایجاد شده در اشیاء را پیگیری کنید.
      • اعمال اعتبارسنجی: قبل از ذخیره تغییرات، می‌توانید اعتبارسنجی‌های سفارشی را انجام دهید.
      • انجام عملیات پس‌زمینه: می‌توانید عملیات زمان‌بر را به صورت پس‌زمینه اجرا کنید.

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

       

  • Custom Types:

    • انواع داده‌های سفارشی در SQLAlchemy

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

      چرا به انواع داده‌های سفارشی نیاز داریم؟

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

      انواع اصلی داده‌های سفارشی در SQLAlchemy

      1. انواع داده‌های کاربری (User-Defined Types):

        • این نوع داده‌ها با استفاده از کلاس‌های پایتون تعریف می‌شوند و می‌توانند رفتارهای سفارشی داشته باشند.
        • مثال:
          Python
          from sqlalchemy import TypeDecorator, Integer
          
          class PositiveInteger(TypeDecorator):
              impl = Integer
          
              def process_bind_param(self, value, dialect):
                  if value < ۰:
                      raise ValueError("مقدار باید مثبت باشد")
                  return value
          
      2. انواع داده‌های انحصاری (Immutable Types):

        • این نوع داده‌ها معمولاً برای نمایش مقادیر ثابت و غیرقابل تغییر استفاده می‌شوند.
        • مثال:
          Python
          from sqlalchemy import TypeDecorator
          
          class Gender(TypeDecorator):
              impl = String
          
              def __init__(self, *args, **kwargs):
                  super().__init__(*args, **kwargs)
                  self._map = {'M': 'مرد', 'F': 'زن'}
          
              def process_bind_param(self, value, dialect):
                  if value not in self._map:
                      raise ValueError("مقدار جنسیت نامعتبر است")
                  return self._map[value]
          
      3. انواع داده‌های ترکیبی (Composite Types):

        • برای نمایش داده‌هایی که از چندین ستون تشکیل شده‌اند استفاده می‌شود.
        • مثال:
          Python
          from sqlalchemy import Column, Integer, String, Composite
          from sqlalchemy.orm import mapper
          
          class Address(Composite):
              __table__ = Table('addresses', metadata,
                                Column('street', String(۵۰)),
                                Column('city', String(۵۰)),
                                Column('zipcode', Integer))
          
              def __init__(self, street, city, zipcode):
                  self.street = street
                  self.city = city
                  self.zipcode = zipcode
          

      مزایای استفاده از انواع داده‌های سفارشی

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

      نکات مهم

      • انتخاب نوع مناسب: انتخاب نوع داده‌ی سفارشی مناسب به نوع داده‌ای که می‌خواهید مدل کنید بستگی دارد.
      • پیاده‌سازی روش‌های خاص: برای هر نوع داده‌ی سفارشی، باید روش‌هایی مانند process_bind_param, process_result_value و غیره را پیاده‌سازی کنید تا مشخص کنید چگونه داده‌ها بین پایتون و پایگاه داده تبدیل شوند.
      • بهینه سازی عملکرد: اگر از انواع داده‌های سفارشی پیچیده استفاده می‌کنید، ممکن است بر عملکرد پایگاه داده تأثیر بگذارد.

      کاربردهای عملی

      • مدل‌سازی داده‌های جغرافیایی: تعریف انواع داده‌های سفارشی برای نمایش مختصات جغرافیایی، مناطق و …
      • مدل‌سازی داده‌های پیچیده: تعریف انواع داده‌های سفارشی برای نمایش ساختارهای داده‌ای پیچیده مانند ماتریس‌ها، نمودارها و …
      • اعمال قوانین کسب‌وکار: تعریف انواع داده‌های سفارشی برای اعمال قوانین کسب‌وکار بر روی داده‌ها.

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

       

  • Performance Optimization:

    • بهینه‌سازی پرس‌وجوها در SQLAlchemy: راهنمایی جامع

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

      درک مفاهیم پایه

      • Session: یک جلسه SQLAlchemy برای مدیریت تعاملات با پایگاه داده استفاده می‌شود.
      • Query: یک شیء Query برای ساخت و اجرای پرس‌وجوها به کار می‌رود.
      • eager loading: بارگذاری همه روابط مرتبط با یک شیء در یک پرس‌وجو.
      • lazy loading: بارگذاری روابط تنها زمانی که به آن‌ها نیاز باشد.

      تکنیک‌های بهینه‌سازی

      1. استفاده بهینه از eager loading و lazy loading:

        • بهینه‌سازی پرس‌وجوها با استفاده از Eager Loading و Lazy Loading در SQLAlchemy

          Eager Loading و Lazy Loading دو تکنیک کلیدی در SQLAlchemy برای کنترل نحوه بارگذاری اشیاء مرتبط در پرس‌وجوها هستند. انتخاب مناسب بین این دو تکنیک به شدت بر عملکرد برنامه شما تأثیر می‌گذارد.

          Eager Loading
          • تعریف: در Eager Loading، تمامی اشیاء مرتبط با یک شیء اصلی به صورت همزمان و در یک پرس‌وجو بارگذاری می‌شوند. این روش زمانی مفید است که نیاز دارید به تمامی داده‌های مرتبط دسترسی داشته باشید و از اجرای چندین پرس‌وجو جداگانه جلوگیری کنید.
          • مزایا:
            • کاهش تعداد پرس‌وجوها به پایگاه داده
            • بهبود عملکرد در مواردی که به همه داده‌های مرتبط نیاز است
          • معایب:
            • ممکن است باعث بارگذاری داده‌های اضافی و کاهش کارایی شود، خصوصاً در صورتی که به همه داده‌های مرتبط نیاز نداشته باشید.
          • مثال:
            Python
            from sqlalchemy.orm import joinedload
            
            users = session.query(User).options(joinedload(User.addresses)).all()
            

            در این مثال، تمامی آدرس‌های مرتبط با هر کاربر به صورت همزمان بارگذاری می‌شوند.

          Lazy Loading
          • تعریف: در Lazy Loading، اشیاء مرتبط تنها زمانی بارگذاری می‌شوند که به آن‌ها دسترسی پیدا شود. این روش زمانی مفید است که به همه داده‌های مرتبط نیاز نداشته باشید و بخواهید از بارگذاری داده‌های اضافی جلوگیری کنید.
          • مزایا:
            • کاهش بارگذاری داده‌ها و بهبود عملکرد در مواردی که به همه داده‌های مرتبط نیاز ندارید.
          • معایب:
            • ممکن است باعث اجرای چندین پرس‌وجو جداگانه شود و عملکرد را کاهش دهد، خصوصاً در صورتی که به تعداد زیادی از اشیاء مرتبط دسترسی داشته باشید.
          • مثال:
            Python
            user = session.query(User).first()
            addresses = user.addresses  # این باعث اجرای یک پرس‌وجوی جداگانه می‌شود
            

            در این مثال، آدرس‌های کاربر تنها زمانی بارگذاری می‌شوند که به صفت addresses دسترسی پیدا کنیم.

          چه زمانی از کدام روش استفاده کنیم؟
          • Eager Loading:
            • زمانی که به همه داده‌های مرتبط نیاز دارید و از اجرای چندین پرس‌وجو جداگانه جلوگیری می‌خواهید.
            • زمانی که روابط بین اشیاء قوی است و اغلب با هم استفاده می‌شوند.
          • Lazy Loading:
            • زمانی که به همه داده‌های مرتبط نیاز ندارید و می‌خواهید از بارگذاری داده‌های اضافی جلوگیری کنید.
            • زمانی که روابط بین اشیاء ضعیف است و به ندرت با هم استفاده می‌شوند.
          نکات مهم
          • تعادل: انتخاب بین Eager Loading و Lazy Loading به ساختار داده‌ها و نیازهای برنامه شما بستگی دارد.
          • بهینه‌سازی: برای بهینه‌سازی عملکرد، باید تعادلی بین Eager Loading و Lazy Loading پیدا کنید.
          • ابزارهای پروفایلینگ: از ابزارهای پروفایلینگ برای شناسایی گلوگاه‌های عملکرد و بهینه سازی پرس‌وجوها استفاده کنید.
          • سایر تکنیک‌ها: علاوه بر Eager Loading و Lazy Loading، تکنیک‌های دیگری مانند subqueries، indexes و … نیز برای بهینه‌سازی پرس‌وجوها وجود دارد.
          مثال پیشرفته: ترکیب Eager Loading و Lazy Loading
          Python
          from sqlalchemy.orm import joinedload, selectinload
          
          users = session.query(User).options(
              joinedload(User.addresses),
              selectinload(User.orders).options(joinedload(Order.items))
          ).all()
          

          در این مثال، آدرس‌های کاربران به صورت Eager Loading بارگذاری می‌شوند، در حالی که آیتم‌های سفارشات به صورت Lazy Loading بارگذاری می‌شوند.

          جمع‌بندی: درک تفاوت بین Eager Loading و Lazy Loading و انتخاب مناسب بین آن‌ها برای نوشتن پرس‌وجوهای کارآمد در SQLAlchemy بسیار مهم است. با استفاده از این تکنیک‌ها می‌توانید عملکرد برنامه‌های خود را بهبود بخشیده و از منابع سیستم بهینه استفاده کنید.

           

      2. استفاده بهینه از Subqueries در SQLAlchemy برای بهبود کارایی پرس‌وجوها

        Subqueries یا زیر پرس‌جوها ابزاری قدرتمند در SQLAlchemy هستند که به شما اجازه می‌دهند یک پرس‌جو را درون پرس‌جوی دیگری قرار دهید. این امر به شما امکان می‌دهد تا پرس‌وجوهای پیچیده‌تری را بنویسید و تعداد join‌ها را کاهش دهید، که در نتیجه به بهبود کارایی پرس‌جو منجر می‌شود.

        چه زمانی از Subqueries استفاده کنیم؟
        • محاسبات پیچیده: زمانی که نیاز به محاسبات پیچیده‌ای مانند محاسبه مجموع، میانگین، حداکثر یا حداقل مقادیر دارید.
        • شرط‌های پیچیده: زمانی که شرط‌های where شما بسیار پیچیده هستند و شامل چندین جدول می‌شوند.
        • جایگزینی برای join‌ها: زمانی که join‌های زیادی دارید و می‌خواهید تعداد آن‌ها را کاهش دهید.
        • استفاده از توابع aggregate در شرط‌ها: زمانی که می‌خواهید از توابع aggregate مانند COUNT، SUM، AVG و … در شرط‌های where یا having استفاده کنید.
        مثال عملی

        فرض کنید می‌خواهیم لیستی از کاربران را به همراه تعداد سفارشات هر کاربر به دست آوریم. بدون استفاده از subquery، ما ممکن است از یک join استفاده کنیم:

        Python
        from sqlalchemy import func
        
        users_with_order_count = session.query(User, func.count(Order.id)).join(Order).group_by(User.id).all()
        

        با استفاده از subquery، می‌توانیم این پرس‌جو را به صورت زیر بازنویسی کنیم:

        Python
        subquery = session.query(func.count(Order.id)).filter(Order.user_id == User.id).subquery()
        users_with_order_count = session.query(User, subquery.c.count).join(subquery, User.id == subquery.c.user_id).all()
        

        در این مثال، ابتدا یک subquery ایجاد می‌کنیم که تعداد سفارشات هر کاربر را محاسبه می‌کند. سپس این subquery را به پرس‌جوی اصلی اضافه می‌کنیم.

        مزایای استفاده از Subqueries
        • افزایش خوانایی: Subqueries می‌توانند پرس‌وجوهای پیچیده را قابل فهم‌تر کنند.
        • کاهش تعداد join‌ها: در برخی موارد، استفاده از subquery می‌تواند تعداد join‌ها را کاهش دهد و به بهبود کارایی کمک کند.
        • انعطاف‌پذیری بیشتر: Subqueries به شما امکان می‌دهند تا پرس‌وجوهای پیچیده‌تری را بنویسید.
        نکات مهم
        • استفاده صحیح از پرانتز: هنگام استفاده از subqueries، باید از پرانتز به درستی استفاده کنید تا ساختار پرس‌جو مشخص باشد.
        • بهینه سازی subqueries: Subqueries نیز نیاز به بهینه‌سازی دارند. سعی کنید subqueries را ساده و مختصر نگه دارید.
        • استفاده از alias: برای افزایش خوانایی، به subqueries alias بدهید.
        مثال‌های پیشرفته‌تر
        • یافتن کاربران با بیشترین تعداد سفارش:
          Python
          subquery = session.query(func.count(Order.id), User.id).group_by(User.id).subquery()
          max_orders = session.query(func.max(subquery.c.count)).scalar()
          users_with_max_orders = session.query(User).join(subquery, User.id == subquery.c.user_id).filter(subquery.c.count == max_orders).all()
          
        • یافتن محصولاتی که در هیچ سفارشی وجود ندارند:
          Python
          subquery = session.query(Order.product_id).subquery()
          products_not_in_orders = session.query(Product).filter(~Product.id.in_(subquery)).all()
          

        جمع‌بندی: Subqueries یک ابزار قدرتمند در SQLAlchemy هستند که به شما امکان می‌دهند پرس‌وجوهای پیچیده‌تری را بنویسید و کارایی آن‌ها را بهبود بخشید. با استفاده صحیح از subqueries، می‌توانید پرس‌وجوهای خود را بهینه کرده و از پایگاه داده خود به بهترین شکل ممکن استفاده کنید.

         

      3. استفاده از indexes:

        • اهمیت شاخص‌ها در بهینه‌سازی پرس‌وجوها

          شاخص‌ها (Indexes) ساختارهای داده‌ای هستند که پایگاه داده برای سرعت بخشیدن به جستجو و بازیابی داده‌ها از روی یک یا چند ستون از یک جدول استفاده می‌کند. هنگامی که یک پرس‌جو شامل یک شرط where بر روی یک ستون است که شاخص روی آن تعریف شده باشد، پایگاه داده می‌تواند به‌جای اسکن کامل جدول، از شاخص برای یافتن سریع‌تر سطرهای مورد نظر استفاده کند.

          چرا شاخص‌ها مهم هستند؟
          • سرعت بخشیدن به پرس‌جوها: شاخص‌ها به پایگاه داده اجازه می‌دهند تا به‌طور مؤثرتری داده‌های مورد نظر را پیدا کند و در نتیجه زمان اجرای پرس‌جو کاهش یابد.
          • بهبود عملکرد در عملیات مرتب‌سازی و گروه‌بندی: شاخص‌ها می‌توانند به پایگاه داده کمک کنند تا عملیات مرتب‌سازی و گروه‌بندی را سریع‌تر انجام دهد.
          • کاهش I/O: با استفاده از شاخص‌ها، پایگاه داده می‌تواند تعداد خواندن‌های دیسک را کاهش دهد و در نتیجه عملکرد کلی را بهبود بخشد.
          چه زمانی باید شاخص ایجاد کنیم؟
          • ستون‌هایی که در شرط‌های where به‌طور مکرر استفاده می‌شوند: شاخص‌هایی که روی ستون‌هایی که اغلب در شرط‌های where استفاده می‌شوند، ایجاد می‌شوند، بیشترین تأثیر را در بهبود عملکرد پرس‌جوها دارند.
          • ستون‌هایی که برای مرتب‌سازی یا گروه‌بندی استفاده می‌شوند: اگر ستونی به‌طور مکرر برای مرتب‌سازی یا گروه‌بندی داده‌ها استفاده می‌شود، ایجاد شاخص روی آن می‌تواند مفید باشد.
          • ستون‌هایی که در کلیدهای خارجی استفاده می‌شوند: ایجاد شاخص روی ستون‌های کلید خارجی می‌تواند به بهبود عملکرد join‌ها کمک کند.
          انواع شاخص‌ها
          • شاخص‌های B-tree: رایج‌ترین نوع شاخص هستند و برای جستجوهای براساس مقدار دقیق یا بازه استفاده می‌شوند.
          • شاخص‌های hash: برای جستجوهای براساس مقدار دقیق بسیار سریع هستند، اما برای جستجوهای بازه مناسب نیستند.
          • شاخص‌های bitmap: برای ستون‌هایی با تعداد مقادیر منحصر به فرد کم بسیار مناسب هستند.
          چه زمانی نباید شاخص ایجاد کنیم؟
          • جدول‌های کوچک: برای جدول‌های کوچک، هزینه نگهداری شاخص ممکن است بیشتر از مزایای آن باشد.
          • ستون‌هایی با مقادیر تکراری زیاد: شاخص روی ستون‌هایی با مقادیر تکراری زیاد، کارایی چندانی ندارد.
          • ستون‌هایی که به‌طور مکرر به‌روزرسانی می‌شوند: ایجاد شاخص روی ستون‌هایی که به‌طور مکرر به‌روزرسانی می‌شوند، می‌تواند بر عملکرد عملیات نوشتاری تأثیر منفی بگذارد.
          نکات مهم در ایجاد شاخص
          • انتخاب ستون‌های مناسب: شاخص‌ها باید روی ستون‌هایی ایجاد شوند که به‌طور مکرر در شرط‌های where استفاده می‌شوند.
          • تعداد شاخص‌ها: ایجاد تعداد زیادی شاخص می‌تواند بر عملکرد نوشتاری پایگاه داده تأثیر منفی بگذارد، بنابراین باید تعادل بین تعداد شاخص‌ها و عملکرد را برقرار کرد.
          • نوع شاخص: انتخاب نوع شاخص مناسب به نوع داده‌ها و نحوه استفاده از ستون بستگی دارد.
          • اندازه شاخص: شاخص‌ها فضای دیسک را اشغال می‌کنند، بنابراین باید اندازه آن‌ها را مدیریت کرد.
          مثال در SQLAlchemy
          Python
          from sqlalchemy import Index
          
          # ایجاد شاخص روی ستون name در جدول users
          Index('ix_users_name', User.name, unique=True)
          
          در نهایت

          ایجاد شاخص‌های مناسب می‌تواند به طور قابل توجهی عملکرد پرس‌جوهای شما را بهبود بخشد. با این حال، ایجاد شاخص‌های زیاد و بی‌رویه می‌تواند عملکرد پایگاه داده شما را کاهش دهد. بنابراین، قبل از ایجاد هر شاخصی، باید به دقت نیازهای خود را ارزیابی کنید و با استفاده از ابزارهای تحلیل عملکرد، تأثیر شاخص‌ها را بر روی عملکرد پایگاه داده خود ارزیابی کنید.

           

      4. بهینه‌سازی پرس‌وجوها در SQLAlchemy با تمرکز بر توابع جمعی و انتخاب ستون‌ها

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

        استفاده از توابع جمعی در SQLAlchemy

        SQLAlchemy از تمامی توابع جمعی استاندارد SQL پشتیبانی می‌کند. برای استفاده از این توابع، آن‌ها را به عنوان متد به شیء Query اضافه می‌کنیم.

        Python
        from sqlalchemy import func
        
        # شمارش کل کاربران
        total_users = session.query(func.count(User.id)).scalar()
        
        # محاسبه متوسط سن کاربران
        average_age = session.query(func.avg(User.age)).scalar()
        
        # گروه‌بندی کاربران بر اساس کشور و شمارش تعداد کاربران هر کشور
        users_by_country = session.query(User.country, func.count(User.id)).group_by(User.country).all()
        
        انتخاب دقیق ستون‌ها در SQLAlchemy

        برای انتخاب ستون‌های خاص، از متد add_column() استفاده می‌کنیم. این کار باعث می‌شود که تنها داده‌های مورد نیاز از پایگاه داده بارگذاری شوند و در نتیجه عملکرد پرس‌جو بهبود یابد.

        Python
        # انتخاب نام و ایمیل کاربران
        users = session.query(User.name, User.email).all()
        
        ترکیب توابع جمعی و انتخاب ستون‌ها
        Python
        # محاسبه مجموع قیمت سفارشات هر محصول
        total_sales_by_product = session.query(Product.name, func.sum(Order.total)).join(Order).group_by(Product.name).all()
        
        نکات مهم
        • استفاده از label() برای نام‌گذاری ستون‌های محاسباتی:
          Python
          total_sales = session.query(func.sum(Order.total).label("total_sales")).scalar()
          
        • استفاده از distinct() برای حذف مقادیر تکراری:
          Python
          unique_countries = session.query(func.count(distinct(User.country))).scalar()
          
        • بهینه‌سازی با استفاده از having:
          Python
          # یافتن محصولات با فروش بیش از ۱۰۰۰ دلار
          products_with_high_sales = session.query(Product.name, func.sum(Order.total)).join(Order).group_by(Product.name).having(func.sum(Order.total) > ۱۰۰۰).all()
          
        مثال عملی: محاسبه میانگین قیمت محصولات در هر دسته
        Python
        from sqlalchemy import Column, Integer, String, ForeignKey
        from sqlalchemy.orm import relationship
        
        class Product(Base):
            __tablename__ = 'products'
            id = Column(Integer, primary_key=True)
            name = Column(String)
            category_id = Column(Integer, ForeignKey('categories.id'))
            category = relationship("Category", back_populates="products")
        
        class Category(Base):
            __tablename__ = 'categories'
            id = Column(Integer, primary_key=True)
            name = Column(String)
            products = relationship("Product", back_populates="category")
        
        # محاسبه میانگین قیمت محصولات در هر دسته
        average_prices = session.query(Category.name, func.avg(Product.price)).join(Product).group_by(Category.name).all()
        
        مزایای استفاده از توابع جمعی و انتخاب دقیق ستون‌ها
        • کاهش حجم داده منتقل شده: با انتخاب تنها ستون‌های مورد نیاز، حجم داده‌هایی که از پایگاه داده به برنامه منتقل می‌شود کاهش می‌یابد.
        • افزایش سرعت پرس‌جو: کاهش حجم داده و استفاده از توابع بهینه باعث می‌شود پرس‌جو سریع‌تر اجرا شود.
        • بهبود خوانایی پرس‌جو: انتخاب صریح ستون‌ها و استفاده از نام‌های معنادار برای ستون‌های محاسباتی، خوانایی پرس‌جو را افزایش می‌دهد.

        در نتیجه، با استفاده صحیح از توابع جمعی و انتخاب دقیق ستون‌ها در SQLAlchemy، می‌توان پرس‌وجوهای کارآمدتری نوشت که به بهبود عملکرد برنامه کمک می‌کنند.

         

      5. بهینه‌سازی Session در SQLAlchemy: مدیریت صحیح چرخه عمر Session

        همانطور که می‌دانید، Session در SQLAlchemy نقش بسیار مهمی در مدیریت تعاملات با پایگاه داده ایفا می‌کند. بهینه‌سازی نحوه استفاده از Session می‌تواند به طور قابل توجهی بر عملکرد برنامه شما تاثیر بگذارد. در این بخش به دو نکته مهم در رابطه با بهینه‌سازی Session می‌پردازیم:

        ۱. استفاده مجدد از یک Session برای چندین پرس‌وجو
        • چرا مهم است؟ ایجاد یک Session هزینه‌ای دارد. با استفاده مجدد از یک Session برای چندین پرس‌وجو، می‌توانیم از ایجاد Session‌های متعدد و کاهش عملکرد جلوگیری کنیم.
        • نحوه انجام:
          • در یک درخواست: معمولاً در هر درخواست HTTP یک Session ایجاد می‌شود و در پایان درخواست بسته می‌شود. تمام پرس‌وجوهای مربوط به آن درخواست از این Session استفاده می‌کنند.
          • در یک واحد کاری: در برخی موارد، ممکن است بخواهیم چندین عملیات را در یک واحد کاری انجام دهیم. در این حالت، می‌توانیم یک Session ایجاد کرده و تا پایان واحد کاری از آن استفاده کنیم.
        Python
        from sqlalchemy.orm import sessionmaker
        
        # ایجاد یک SessionMaker
        Session = sessionmaker(bind=engine)
        
        # استفاده از Session در یک درخواست
        with Session() as session:
            # پرس‌جوی اول
            result1 = session.query(User).filter_by(name='Alice').first()
        
            # پرس‌جوی دوم
            result2 = session.query(Order).filter_by(user_id=result1.id).all()
        
        ۲. Commit و Close کردن به موقع Session
        • Commit: زمانی که تغییرات در پایگاه داده پایدار شوند، باید از متد commit() استفاده کنیم.
        • Close: پس از اتمام کار با Session، باید آن را با استفاده از متد close() ببندیم تا منابع سیستم آزاد شوند.
        • زمان مناسب برای Commit و Close:
          • Commit: معمولاً پس از انجام یک مجموعه از عملیات مرتبط، Commit انجام می‌شود.
          • Close: Session را در پایان درخواست یا واحد کاری ببندید.
        Python
        with Session() as session:
            # اضافه کردن یک کاربر جدید
            new_user = User(name='Bob')
            session.add(new_user)
        
            # ذخیره تغییرات در پایگاه داده
            session.commit()
        
        نکات مهم
        • مدیریت Transaction: Session‌ها با Transaction‌ها ارتباط نزدیکی دارند. اطمینان حاصل کنید که Transaction‌ها به درستی مدیریت می‌شوند تا از مشکلات مربوط به قفل شدن و از دست رفتن داده‌ها جلوگیری شود.
        • Session.expire_on_commit: این پارامتر تعیین می‌کند که آیا اشیاء پس از Commit از حافظه پنهان خارج شوند یا خیر. تنظیم صحیح این پارامتر می‌تواند به بهبود عملکرد کمک کند.
        • استفاده از Context Manager: استفاده از with برای مدیریت Session، باعث می‌شود که Session به طور خودکار بسته شود حتی اگر خطایی رخ دهد.
        • پیکربندی SessionMaker: با استفاده از sessionmaker می‌توان تنظیمات مختلفی مانند autocommit, expire_on_commit و … را برای Session‌ها پیکربندی کرد.
        چه زمانی باید Session جدید ایجاد کنیم؟
        • هر درخواست HTTP: معمولاً برای هر درخواست HTTP یک Session جدید ایجاد می‌شود.
        • هنگامی که نیاز به شروع یک Transaction جدید داریم: اگر می‌خواهیم یک مجموعه از عملیات را به صورت یک Transaction انجام دهیم، می‌توانیم یک Session جدید ایجاد کنیم.
        • هنگامی که Session قبلی به دلیل خطا بسته شده است: اگر Session قبلی به دلیل خطایی بسته شده باشد، باید یک Session جدید ایجاد کنیم.

        در نهایت، انتخاب بهترین روش برای مدیریت Session به عوامل مختلفی مانند حجم داده، تعداد پرس‌وجوها و پیچیدگی برنامه بستگی دارد. با در نظر گرفتن این عوامل و رعایت نکات ذکر شده، می‌توانید بهینه سازی قابل توجهی در استفاده از Session در SQLAlchemy ایجاد کنید.

         

      6. استفاده از حافظه پنهان (Cache) برای بهینه سازی پرس‌وجوهای مکرر در SQLAlchemy

        مفهوم حافظه پنهان:

        حافظه پنهان مکانی برای ذخیره موقت نتایج پرس‌وجوهایی است که به طور مکرر اجرا می‌شوند. به این ترتیب، هنگامی که مجدداً همان پرس‌جو انجام می‌شود، نتیجه از حافظه پنهان بازیابی شده و نیازی به اجرای مجدد پرس‌جو در پایگاه داده نیست. این امر باعث افزایش سرعت و کاهش بار روی پایگاه داده می‌شود.

        چرا از حافظه پنهان استفاده کنیم؟
        • افزایش سرعت: با ذخیره نتایج پرس‌وجوهای پرکاربرد در حافظه پنهان، زمان پاسخگویی به این پرس‌وجوها به طور قابل توجهی کاهش می‌یابد.
        • کاهش بار روی پایگاه داده: با کاهش تعداد پرس‌وجوهای ارسالی به پایگاه داده، عمر مفید پایگاه داده افزایش یافته و منابع آن بهینه استفاده می‌شود.
        • بهبود مقیاس‌پذیری: استفاده از حافظه پنهان به برنامه کمک می‌کند تا با افزایش حجم ترافیک بهتر کنار بیاید.
        چگونه از حافظه پنهان در SQLAlchemy استفاده کنیم؟

        SQLAlchemy به طور مستقیم از حافظه پنهان پشتیبانی نمی‌کند، اما می‌توان از کتابخانه‌های حافظه پنهان خارجی مانند Redis، Memcached یا ابزارهای داخلی پایتون مانند functools.lru_cache استفاده کرد.

        مثال با استفاده از functools.lru_cache:

        Python
        from functools import lru_cache
        
        @lru_cache(maxsize=۱۲۸)
        def get_user_by_id(user_id):
            user = session.query(User).filter_by(id=user_id).first()
            return user
        

        در مثال بالا، دکوراتور @lru_cache باعث می‌شود که نتایج تابع get_user_by_id برای ۱۲۸ بار فراخوانی اخیر در حافظه پنهان ذخیره شود. در فراخوانی‌های بعدی، اگر ورودی (user_id) تکراری باشد، نتیجه از حافظه پنهان بازیابی می‌شود.

        نکات مهم:
        • انتخاب نوع حافظه پنهان: انتخاب نوع حافظه پنهان به عوامل مختلفی مانند اندازه داده‌ها، سرعت دسترسی، توزیع شده بودن و … بستگی دارد.
        • مدیریت انقضای داده‌ها: داده‌های ذخیره شده در حافظه پنهان باید پس از مدت زمان مشخصی منقضی شوند تا از بروزرسانی نشدن اطلاعات جلوگیری شود.
        • حساسیت به داده‌ها: برای داده‌هایی که به سرعت تغییر می‌کنند، استفاده از حافظه پنهان مناسب نیست.
        • استفاده از کلیدهای مناسب: کلیدهای استفاده شده برای ذخیره داده‌ها در حافظه پنهان باید به گونه‌ای انتخاب شوند که به طور منحصر به فرد هر داده را شناسایی کنند.

        مثال پیشرفته با استفاده از Redis:

        Python
        import redis
        
        redis_client = redis.Redis(host='localhost', port=۶۳۷۹, db=۰)
        
        def get_user_from_cache(user_id):
            user_json = redis_client.get(f"user:{user_id}")
            if user_json:
                return json.loads(user_json)
        
            user = session.query(User).filter_by(id=user_id).first()
            redis_client.set(f"user:{user_id}", json.dumps(user.__dict__))
            return user
        

        در این مثال، از Redis به عنوان حافظه پنهان استفاده شده است. داده‌ها به صورت JSON در Redis ذخیره می‌شوند و با استفاده از کلید user:{user_id} بازیابی می‌شوند.

        جمع‌بندی:

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

         

      7. استفاده از Explain Plan در SQLAlchemy برای بهینه‌سازی پرس‌وجوها

        توضیح Plan چیست؟

        Explain Plan یا نقشه اجرایی، یک ابزار قدرتمند در پایگاه‌های داده است که به شما نشان می‌دهد پایگاه داده چگونه یک پرس‌جو را اجرا می‌کند. با استفاده از این ابزار می‌توانید نقاط ضعف پرس‌جو را شناسایی کرده و با اعمال تغییراتی، عملکرد آن را بهبود ببخشید.

        چرا از Explain Plan استفاده کنیم؟
        • شناسایی نقاط کند: با بررسی Explain Plan می‌توانید بخش‌هایی از پرس‌جو را که زمان زیادی می‌برد شناسایی کنید.
        • بهینه‌سازی ایندکس‌ها: Explain Plan به شما نشان می‌دهد که آیا ایندکس‌های موجود برای پرس‌جو بهینه هستند یا خیر.
        • انتخاب الگوریتم مناسب: Explain Plan می‌تواند به شما کمک کند تا الگوریتم‌های بهینه‌تری برای اجرای پرس‌جو انتخاب کنید.
        نحوه استفاده از Explain Plan در SQLAlchemy

        SQLAlchemy به طور مستقیم از Explain Plan پشتیبانی نمی‌کند. اما می‌توان با استفاده از زبان SQL خام و یا ابزارهای ORM دیگر که از Explain Plan پشتیبانی می‌کنند، این کار را انجام داد.

        روش اول: استفاده از زبان SQL خام

        1. ایجاد یک اتصال به پایگاه داده:

          Python
          from sqlalchemy import create_engine
          
          engine = create_engine('postgresql://user:password@host:port/database')
          
        2. نوشتن پرس‌جوی Explain:

          Python
          with engine.connect() as conn:
              result = conn.execute("EXPLAIN SELECT * FROM users WHERE age > 30")
              for row in result:
                  print(row)
          

          توجه: دستور EXPLAIN در هر پایگاه داده متفاوت است. برای PostgreSQL از EXPLAIN, برای MySQL از EXPLAIN EXTENDED و برای SQL Server از EXPLAIN EXECUTE استفاده می‌شود.

        روش دوم: استفاده از ORM های دیگر

        برخی از ORM ها مانند SQLAlchemy-Utils امکان استفاده مستقیم از Explain Plan را فراهم می‌کنند. اما این روش به ORM مورد استفاده شما بستگی دارد.

        تفسیر Explain Plan

        تفسیر Explain Plan به پایگاه داده و نوع خروجی آن بستگی دارد. اما به طور کلی، Explain Plan اطلاعاتی در مورد مراحل اجرای پرس‌جو، هزینه هر مرحله، نوع دسترسی به داده‌ها (مانند استفاده از ایندکس یا اسکن کامل جدول) و … را ارائه می‌دهد.

        بهینه‌سازی پرس‌جو بر اساس Explain Plan
        • ایجاد یا حذف ایندکس‌ها: اگر Explain Plan نشان دهد که پرس‌جو از ایندکس استفاده نمی‌کند یا ایندکس موجود بهینه نیست، می‌توانید ایندکس جدیدی ایجاد یا ایندکس موجود را اصلاح کنید.
        • تغییر شرط‌های WHERE: گاهی اوقات با تغییر جزئی در شرط‌های WHERE می‌توانید عملکرد پرس‌جو را بهبود بخشید.
        • تغییر نوع JOIN: اگر پرس‌جو از JOIN استفاده می‌کند، می‌توانید نوع JOIN را تغییر دهید (مثلاً از INNER JOIN به LEFT JOIN).
        • استفاده از زیر پرس‌جوها: در برخی موارد، استفاده از زیر پرس‌جوها می‌تواند عملکرد پرس‌جو را بهبود بخشد.

        مثال عملی

        فرض کنید پرس‌جوی زیر کند است:

        Python
        users = session.query(User).filter(User.age > ۳۰).all()
        

        برای بررسی علت کندی، می‌توانیم از Explain Plan استفاده کنیم:

        Python
        with engine.connect() as conn:
            result = conn.execute("EXPLAIN SELECT * FROM users WHERE age > 30")
            for row in result:
                print(row)
        

        اگر Explain Plan نشان دهد که پرس‌جو از اسکن کامل جدول استفاده می‌کند و ایندکسی روی ستون age وجود ندارد، می‌توانیم یک ایندکس روی این ستون ایجاد کنیم تا عملکرد پرس‌جو بهبود یابد.

        نکات مهم
        • Explain Plan یک ابزار قدرتمند است، اما تنها ابزار نیست. برای بهینه‌سازی کامل پرس‌جوها، باید از سایر ابزارها و تکنیک‌ها نیز استفاده کنید.
        • تفسیر Explain Plan نیاز به دانش کافی دارد. برای درک کامل خروجی Explain Plan، باید با مفاهیم پایگاه داده و بهینه‌سازی پرس‌جو آشنا باشید.
        • Explain Plan ممکن است همیشه دقیق نباشد. گاهی اوقات، Explain Plan تخمین‌هایی از عملکرد پرس‌جو ارائه می‌دهد که ممکن است با واقعیت متفاوت باشد.
        نتیجه‌گیری

        Explain Plan ابزاری بسیار مفید برای بهینه‌سازی پرس‌جوها در SQLAlchemy است. با استفاده از این ابزار می‌توانید نقاط ضعف پرس‌جو را شناسایی کرده و با اعمال تغییرات مناسب، عملکرد آن را بهبود بخشید.

         

      8. بهینه‌سازی پایگاه داده با تنظیم پارامترها در SQLAlchemy

        مقدمه

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

        SQLAlchemy و تنظیم پارامترهای پایگاه داده

        SQLAlchemy به عنوان یک ORM قدرتمند، امکان تعامل مستقیم با پایگاه داده را فراهم می‌کند. با این حال، برای تنظیم پارامترهای پایگاه داده به صورت دقیق، نیاز به شناخت عمیق از پایگاه داده مورد استفاده و همچنین تنظیمات SQLAlchemy داریم.

        پارامترهای مهم و نحوه تنظیم آن‌ها
        • Pool size: تعداد اتصالاتی که در یک Pool نگهداری می‌شوند. با تنظیم مناسب این پارامتر می‌توان از ایجاد اتصالات جدید به صورت مداوم جلوگیری کرد و عملکرد را بهبود بخشید.

          Python
          from sqlalchemy import create_engine
          
          engine = create_engine('postgresql://user:password@host:port/database', pool_size=۲۰, max_overflow=۱۰)
          
        • Max overflow: تعداد اتصالات اضافی که می‌توانند فراتر از اندازه Pool ایجاد شوند.

        • Timeout: مدت زمانی که یک اتصال می‌تواند بدون استفاده باقی بماند.

        • Echo: نمایش تمامی دستورهای SQL ارسال شده به پایگاه داده در کنسول.

        • Isolation level: سطح جداسازی تراکنش‌ها.

        • Connect_args: برای ارسال آرگومان‌های اضافی به درایور پایگاه داده.

        • پارامترهای خاص پایگاه داده: هر پایگاه داده‌ای پارامترهای خاص خود را دارد که می‌توان آن‌ها را از طریق connect_args به engine منتقل کرد. مثلاً برای PostgreSQL می‌توان پارامترهایی مانند application_name, search_path و … را تنظیم کرد.

        بهینه‌سازی کش و بافر
        • کش پرس‌جو: برخی از پایگاه داده‌ها از کش پرس‌جو برای ذخیره نتایج پرس‌وجوهای اخیر استفاده می‌کنند. این امر باعث می‌شود که پرس‌جوهای تکراری سریع‌تر اجرا شوند.
        • بافر حافظه: بافر حافظه برای نگهداری داده‌های خوانده شده از دیسک استفاده می‌شود. افزایش اندازه بافر حافظه می‌تواند باعث کاهش تعداد عملیات خواندن از دیسک شود و در نتیجه عملکرد را بهبود بخشد.

        تنظیم پارامترهای کش و بافر معمولاً در سطح پایگاه داده انجام می‌شود و مستقیماً از طریق SQLAlchemy قابل تنظیم نیست. برای تنظیم این پارامترها باید به مستندات پایگاه داده مورد استفاده خود مراجعه کنید.

        ابزارهای بهینه‌سازی
        • Explain Plan: همانطور که در سوال قبلی توضیح داده شد، Explain Plan ابزاری قدرتمند برای تحلیل عملکرد پرس‌وجوها است.
        • Profiler: ابزارهای پروفایلینگ به شما کمک می‌کنند تا نقاط کند برنامه خود را شناسایی کنید.
        • ابزارهای مانیتورینگ پایگاه داده: این ابزارها به شما اجازه می‌دهند تا عملکرد پایگاه داده را در طول زمان مانیتور کنید و مشکلات احتمالی را شناسایی کنید.
        نکات مهم
        • تست و ارزیابی: هر تغییری که در پارامترهای پایگاه داده ایجاد می‌کنید را باید به دقت تست و ارزیابی کنید تا تأثیر آن بر عملکرد سیستم را بسنجید.
        • مشاوره با متخصص: اگر با تنظیم پارامترهای پایگاه داده آشنایی ندارید، بهتر است از یک متخصص کمک بگیرید.
        • به روز نگه داشتن درایور پایگاه داده: استفاده از آخرین نسخه درایور پایگاه داده باعث می‌شود که از آخرین بهینه‌سازی‌ها و رفع باگ‌ها بهره‌مند شوید.
        مثال:
        Python
        from sqlalchemy import create_engine
        
        engine = create_engine('postgresql://user:password@host:port/database',
                               pool_size=۲۰,
                               max_overflow=۱۰,
                               connect_args={'application_name': 'my_application'})
        
        نتیجه‌گیری

        بهینه‌سازی پایگاه داده یک فرآیند پیچیده و وابسته به بسیاری از عوامل است. با در نظر گرفتن پارامترهای مختلف و استفاده از ابزارهای مناسب، می‌توان عملکرد پایگاه داده را به طور قابل توجهی بهبود بخشید.

         

      مثال عملی

      Python
      from sqlalchemy import Column, Integer, String, ForeignKey
      from sqlalchemy.orm import relationship
      
      # ... تعریف مدل‌ها ...
      
      # eager loading
      users = session.query(User).options(joinedload(User.addresses)).all()
      
      # lazy loading
      user = session.query(User).first()
      addresses = user.addresses  # این باعث اجرای یک پرس‌وجوی جداگانه می‌شود
      
      # subquery
      subquery = session.query(func.count(Order.id)).filter(Order.user_id == User.id).subquery()
      users_with_orders = session.query(User, subquery.c.count).join(subquery, User.id == subquery.c.user_id).all()
      

      نکات مهم

      • درک داده‌ها: شناخت ساختار داده و الگوهای دسترسی به داده‌ها برای بهینه‌سازی پرس‌وجوها بسیار مهم است.
      • ابزارهای پروفایلینگ: از ابزارهای پروفایلینگ برای شناسایی گلوگاه‌های عملکرد استفاده کنید.
      • آزمایش: تغییرات را آزمایش کنید تا مطمئن شوید که عملکرد بهبود یافته است.

       

      با رعایت این نکات، می‌توانید پرس‌وجوهای خود را بهینه کرده و عملکرد برنامه‌های پایگاه داده‌ای خود را به طور قابل توجهی بهبود بخشید.

       

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

    تست کردن مدل‌های SQLAlchemy به این معنی است که اطمینان حاصل کنیم مدل‌های ما به درستی به جداول پایگاه داده نگاشت شده‌اند و عملیات CRUD (ایجاد، خواندن، به‌روزرسانی و حذف) به صورت صحیح انجام می‌شوند. این کار به جلوگیری از بروز خطا در برنامه و تضمین صحت داده‌ها کمک می‌کند.

    چرا تست کردن مدل‌ها مهم است؟

    • اطمینان از صحت نگاشت‌ها: اطمینان حاصل می‌شود که مدل‌های پایتون به درستی به ساختار جداول پایگاه داده نگاشت شده‌اند.
    • کشف باگ‌ها در زودترین زمان ممکن: با تست کردن مدل‌ها، باگ‌ها و خطاهای احتمالی در مراحل اولیه توسعه شناسایی می‌شوند.
    • تسهیل تغییرات: با داشتن تست‌های کامل، می‌توان تغییرات را با اطمینان بیشتری اعمال کرد و از ایجاد مشکلات جدید جلوگیری کرد.

    انواع تست‌ها

    • تست‌های واحد: هر مدل را به صورت جداگانه تست کرده و اطمینان حاصل کنید که عملیات CRUD به درستی انجام می‌شود.
    • تست‌های انتگرال: چندین مدل را با هم تست کرده و اطمینان حاصل کنید که روابط بین آن‌ها به درستی کار می‌کند.
    • تست‌های عملکرد: عملکرد مدل‌ها را در شرایط مختلف بررسی کنید تا از کارایی آن‌ها اطمینان حاصل شود.

    ابزارهای تست

    • unittest: کتابخانه استاندارد پایتون برای نوشتن تست‌ها
    • pytest: یک چارچوب تست محبوب با ویژگی‌های پیشرفته
    • nose2: یک چارچوب تست که جایگزینی برای nose است
    • SQLAlchemy-Utils: یک مجموعه از ابزارهای کاربردی برای SQLAlchemy که شامل ابزارهایی برای تست هم هست

    مثال با استفاده از unittest

    Python
    import unittest
    from sqlalchemy import create_engine
    from sqlalchemy.orm import sessionmaker
    from your_app.models import User
    
    class TestUserModel(unittest.TestCase):
        def setUp(self):
            # ایجاد اتصال به پایگاه داده
            self.engine = create_engine('sqlite://')
            # ایجاد Session
            Session = sessionmaker(bind=self.engine)
            self.session = Session()
            # ایجاد جدول
            Base.metadata.create_all(self.engine)
    
        def tearDown(self):
            # حذف جدول
            Base.metadata.drop_all(self.engine)
            # بستن Session
            self.session.close()
    
        def test_create_user(self):
            user = User(name='Alice', email='alice@example.com')
            self.session.add(user)
            self.session.commit()
    
            # بررسی اینکه کاربر ایجاد شده است
            user_from_db = self.session.query(User).filter_by(name='Alice').first()
            self.assertEqual(user_from_db.name, 'Alice')
    

    نکات مهم

    • پوشش کامل: سعی کنید همه سناریوهای ممکن را در تست‌های خود پوشش دهید.
    • تست‌های مستقل: هر تست باید به صورت مستقل اجرا شود و به تست‌های دیگر وابسته نباشد.
    • استفاده از داده‌های تست: از داده‌های تست استفاده کنید تا از تأثیرگذاری بر روی داده‌های اصلی جلوگیری شود.
    • تست‌های عملکرد: برای تست‌های عملکرد، از ابزارهایی مانند timeit یا pytest-benchmark استفاده کنید.

    مثال با استفاده از pytest

    Python
    import pytest
    from sqlalchemy import create_engine
    from sqlalchemy.orm import sessionmaker
    from your_app.models import User
    
    @pytest.fixture(scope='function')
    def session():
        # ایجاد Session و جدول
        # ...
        yield session
        # حذف جدول و بستن Session
        # ...
    
    def test_create_user(session):
        user = User(name='Alice', email='alice@example.com')
        session.add(user)
        session.commit()
    
        # بررسی اینکه کاربر ایجاد شده است
        user_from_db = session.query(User).filter_by(name='Alice').first()
        assert user_from_db.name == 'Alice'
    

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

     

۵/۵ ( ۱ امتیاز )
نمایش بیشتر

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا