اتصال Flask به دیتابیس PostgreSQL: یک راهنمای جامع

فهرست مطالب

اتصال Flask به دیتابیس PostgreSQL: یک راهنمای جامع

در دنیای توسعه وب مدرن، انتخاب ترکیبی قدرتمند و انعطاف‌پذیر از فریمورک وب و سیستم مدیریت پایگاه داده، از اهمیت بالایی برخوردار است. Flask به عنوان یک میکروفریمورک پایتون، به دلیل سادگی، انعطاف‌پذیری و قابلیت توسعه بالا، یکی از محبوب‌ترین انتخاب‌ها برای ساخت برنامه‌های وب سبک، APIها و میکروسرویس‌ها محسوب می‌شود. در سوی دیگر، PostgreSQL نه تنها به عنوان یکی از پیشرفته‌ترین و قدرتمندترین سیستم‌های مدیریت پایگاه داده رابطه‌ای (RDBMS) شناخته می‌شود، بلکه با رعایت کامل استانداردها، پشتیبانی از ویژگی‌های پیشرفته و قابلیت اطمینان بالا، گزینه‌ای ایده‌آل برای ذخیره‌سازی داده‌های حساس و حجیم است.

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

چرا Flask و PostgreSQL؟ مزایا و کاربردها

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

Flask: انعطاف‌پذیری و کنترل کامل

Flask یک میکروفریمورک است، به این معنی که هسته آن بسیار سبک و حداقل‌گرا طراحی شده است. این ویژگی به توسعه‌دهنده آزادی عمل بی‌سابقه‌ای می‌دهد. برخلاف فریمورک‌های “باتری‌های همراه” مانند Django که با خود مجموعه وسیعی از ابزارها و تصمیمات از پیش گرفته شده را به همراه دارند، Flask به شما اجازه می‌دهد تا هر جزء را به دلخواه خود انتخاب و پیکربندی کنید. این رویکرد “انتخاب آزادانه” در Flask مزایای بسیاری دارد:

  • سبک‌وزنی و عملکرد بالا: حجم کد کمتری برای بارگذاری و اجرا نیاز است که منجر به شروع سریع‌تر و مصرف منابع کمتر می‌شود.
  • انعطاف‌پذیری: شما می‌توانید از هر کتابخانه پایگاه داده، ORM، سیستم قالب، یا ابزار احراز هویت که ترجیح می‌دهید استفاده کنید. این امکان، Flask را برای طیف وسیعی از پروژه‌ها، از APIهای RESTful گرفته تا وب‌سایت‌های پیچیده، مناسب می‌سازد.
  • مناسب برای میکروسرویس‌ها: طبیعت سبک و ماژولار Flask، آن را به گزینه‌ای ایده‌آل برای معماری میکروسرویس‌ها تبدیل کرده است، جایی که هر سرویس کوچک و مستقل، وظیفه‌ای خاص را بر عهده دارد.
  • یادگیری آسان: با توجه به حداقل‌گرا بودن، منحنی یادگیری Flask نسبتاً هموار است و توسعه‌دهندگان جدید می‌توانند به سرعت با آن آشنا شوند.

PostgreSQL: قدرت، قابلیت اطمینان و امکانات پیشرفته

PostgreSQL اغلب به عنوان “پیشرفته‌ترین پایگاه داده منبع باز جهان” شناخته می‌شود و این عنوان بی‌دلیل نیست. این سیستم مدیریت پایگاه داده رابطه‌ای (RDBMS) ویژگی‌هایی را ارائه می‌دهد که معمولاً فقط در محصولات تجاری گران‌قیمت یافت می‌شوند:

  • قابلیت اطمینان و پایداری: PostgreSQL به دلیل رعایت دقیق استانداردهای ACID (Atomicity, Consistency, Isolation, Durability) برای تراکنش‌ها، تضمین می‌کند که داده‌های شما همیشه صحیح و قابل اعتماد باقی می‌مانند.
  • پشتیبانی از انواع داده‌های پیشرفته: علاوه بر انواع داده‌های استاندارد، PostgreSQL از انواع داده‌های پیچیده‌تری مانند JSON/JSONB، آرایه‌ها، XML، و انواع هندسی پشتیبانی می‌کند که آن را برای ذخیره‌سازی ساختارهای داده متنوع بسیار منعطف می‌سازد.
  • ویژگی‌های Enterprise-Grade: شامل Replication، Point-in-Time Recovery، Table Partitioning، Full-Text Search، و سیستم توسعه‌پذیری قوی از طریق Functionها و Stored Procedureها.
  • امنیت قوی: مکانیزم‌های احراز هویت و مجوزدهی قدرتمند، امنیت داده‌ها را در برابر دسترسی‌های غیرمجاز تضمین می‌کند.
  • جامعه بزرگ و پشتیبانی فعال: PostgreSQL دارای یک جامعه توسعه‌دهنده فعال و بزرگ است که به طور مداوم در حال بهبود و توسعه آن هستند و منابع فراوانی برای یادگیری و رفع مشکلات ارائه می‌دهند.

هم‌افزایی Flask و PostgreSQL

هنگامی که این دو تکنولوژی در کنار یکدیگر قرار می‌گیرند، یک ترکیب فوق‌العاده قوی و کارآمد را ایجاد می‌کنند:

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

کاربردها

ترکیب Flask و PostgreSQL برای طیف وسیعی از کاربردها مناسب است، از جمله:

  • APIهای RESTful: ساخت بک‌اند برای اپلیکیشن‌های موبایل یا Single Page Application (SPA) با استفاده از Flask و ذخیره‌سازی داده‌ها در PostgreSQL.
  • میکروسرویس‌ها: توسعه سرویس‌های کوچک و مستقل که هر یک وظیفه‌ای خاص را بر عهده دارند و از یک پایگاه داده PostgreSQL مشترک یا اختصاصی استفاده می‌کنند.
  • برنامه‌های وب مبتنی بر داده: وب‌سایت‌هایی که نیاز به مدیریت و پردازش حجم زیادی از داده‌ها دارند، مانند پلتفرم‌های تحلیل داده، سیستم‌های مدیریت محتوا یا برنامه‌های تجارت الکترونیک.
  • ابزارهای داخلی: ساخت داشبوردهای مدیریتی یا ابزارهای داخلی برای کسب و کار که نیاز به اتصال به پایگاه داده‌ای قدرتمند دارند.

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

پیش‌نیازها و راه‌اندازی اولیه محیط

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

۱. نصب پایتون

اطمینان حاصل کنید که پایتون ۳ (ترجیحاً نسخه ۳.۸ یا بالاتر) روی سیستم شما نصب است. می‌توانید با اجرای دستور زیر در ترمینال، نسخه پایتون خود را بررسی کنید:


python3 --version

اگر پایتون نصب نیست، می‌توانید آن را از وب‌سایت رسمی پایتون (python.org) دانلود و نصب کنید یا از طریق مدیریت بسته‌های سیستم عامل خود اقدام نمایید.

۲. ایجاد و فعال‌سازی محیط مجازی

استفاده از محیط مجازی (virtual environment) یک رویه ضروری در توسعه پایتون است. محیط مجازی به شما اجازه می‌دهد تا وابستگی‌های پروژه‌های مختلف را ایزوله کنید و از تداخل نسخه‌های کتابخانه‌ها جلوگیری نمایید.

  1. به پوشه پروژه خود بروید یا یک پوشه جدید ایجاد کنید:
    
            mkdir my_flask_app
            cd my_flask_app
            
  2. یک محیط مجازی ایجاد کنید:
    
            python3 -m venv venv
            

    این دستور یک پوشه به نام `venv` ایجاد می‌کند که حاوی یک کپی ایزوله از پایتون و ابزار `pip` است.

  3. محیط مجازی را فعال کنید:
    • در لینوکس/macOS:
      
                      source venv/bin/activate
                      
    • در ویندوز (Command Prompt):
      
                      venv\Scripts\activate.bat
                      
    • در ویندوز (PowerShell):
      
                      venv\Scripts\Activate.ps1
                      

    پس از فعال‌سازی، نام محیط مجازی (معمولاً `(venv)`) در ابتدای خط فرمان شما نمایش داده می‌شود که نشان‌دهنده فعال بودن آن است.

۳. نصب Flask و Psycopg2

در حالی که محیط مجازی فعال است، Flask و کتابخانه `psycopg2-binary` (برای اتصال به PostgreSQL) را نصب کنید:


pip install Flask psycopg2-binary

`psycopg2-binary` یک نسخه از `psycopg2` است که شامل فایل‌های باینری از پیش کامپایل شده است و نصب آن را در اکثر سیستم‌عامل‌ها ساده‌تر می‌کند. اگر با مشکل مواجه شدید، ممکن است نیاز به نصب ابزارهای کامپایلر (مانند `build-essential` در اوبونتو یا Xcode Command Line Tools در macOS) و سپس نصب `psycopg2` (بدون `-binary`) داشته باشید.

۴. نصب و راه‌اندازی سرور PostgreSQL

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

در لینوکس (بر پایه دبیان/اوبونتو):

  1. نصب سرور PostgreSQL:
    
            sudo apt update
            sudo apt install postgresql postgresql-contrib
            
  2. پس از نصب، سرویس PostgreSQL به طور خودکار شروع به کار می‌کند. می‌توانید وضعیت آن را بررسی کنید:
    
            sudo systemctl status postgresql
            

در macOS (با استفاده از Homebrew):

  1. نصب Homebrew (اگر قبلاً نصب نکرده‌اید):
    
            /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
            
  2. نصب PostgreSQL:
    
            brew install postgresql
            
  3. شروع سرویس PostgreSQL:
    
            brew services start postgresql
            

در ویندوز:

بهترین راه برای نصب PostgreSQL در ویندوز، استفاده از نصب‌کننده (installer) رسمی است که از وب‌سایت PostgreSQL (www.postgresql.org/download/windows/) قابل دانلود است. این نصب‌کننده شامل سرور، ابزارهای خط فرمان و pgAdmin (یک ابزار GUI برای مدیریت پایگاه داده) است.

استفاده از Docker (روش توصیه شده برای توسعه):

استفاده از Docker برای راه‌اندازی PostgreSQL مزایای زیادی دارد، از جمله ایزوله‌سازی، قابلیت تکرارپذیری و سادگی در مدیریت. اگر Docker را نصب کرده‌اید، می‌توانید با یک دستور ساده یک کانتینر PostgreSQL را اجرا کنید:


docker run --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword -p 5432:5432 -d postgres
  • `–name some-postgres`: نامی برای کانتینر شما.
  • `-e POSTGRES_PASSWORD=mysecretpassword`: پسورد کاربر `postgres`. حتماً این پسورد را به یک مقدار قوی تغییر دهید.
  • `-p 5432:5432`: پورت داخلی کانتینر (5432) را به پورت 5432 روی هاست شما نگاشت می‌کند.
  • `-d postgres`: کانتینر را در پس‌زمینه اجرا می‌کند و از ایمیج `postgres` استفاده می‌کند.

۵. ایجاد کاربر و پایگاه داده در PostgreSQL

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

به ترمینال بروید و با کاربر `postgres` که کاربر پیش‌فرض ادمین در PostgreSQL است، به `psql` (کلاینت خط فرمان PostgreSQL) متصل شوید:

  • در لینوکس/macOS:
    
            sudo -u postgres psql
            
  • اگر از Docker استفاده می‌کنید:
    
            docker exec -it some-postgres psql -U postgres
            

    سپس پسورد `mysecretpassword` را وارد کنید.

  • در ویندوز یا اگر از کاربر `postgres` خارج شده‌اید:
    
            psql -U postgres
            

    و پسورد را وارد کنید.

پس از ورود به محیط `psql`، دستورات زیر را برای ایجاد کاربر و پایگاه داده اجرا کنید (مقادیر را به دلخواه خود تغییر دهید):


-- ۱. ایجاد یک کاربر جدید (مثلاً 'flask_user') با پسورد
CREATE USER flask_user WITH PASSWORD 'your_secure_password';

-- ۲. ایجاد یک پایگاه داده جدید (مثلاً 'flask_db')
CREATE DATABASE flask_db;

-- ۳. اعطای تمام دسترسی‌ها به کاربر جدید روی پایگاه داده جدید
GRANT ALL PRIVILEGES ON DATABASE flask_db TO flask_user;

-- ۴. خروج از psql
\q

حالا محیط توسعه شما آماده است و می‌توانید به سراغ کدنویسی و اتصال Flask به PostgreSQL بروید.

اتصال مستقیم با Psycopg2

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

مبانی اتصال با Psycopg2

برای برقراری اتصال با Psycopg2، ابتدا باید کتابخانه را وارد کنید و سپس از تابع `connect()` برای ایجاد یک شیء اتصال استفاده نمایید. این تابع پارامترهای مختلفی از جمله `dbname`, `user`, `password`, `host`, و `port` را می‌پذیرد.


import psycopg2
from psycopg2 import Error

def get_db_connection():
    conn = None
    try:
        conn = psycopg2.connect(
            dbname="flask_db",
            user="flask_user",
            password="your_secure_password",
            host="localhost",
            port="5432"
        )
        print("اتصال به دیتابیس با موفقیت انجام شد.")
        return conn
    except Error as e:
        print(f"خطا در اتصال به دیتابیس: {e}")
        return None

if __name__ == '__main__':
    conn = get_db_connection()
    if conn:
        # انجام عملیات با دیتابیس
        cursor = conn.cursor()
        try:
            cursor.execute("SELECT version();")
            db_version = cursor.fetchone()
            print(f"نسخه دیتابیس PostgreSQL: {db_version}")
        except Error as e:
            print(f"خطا در اجرای کوئری: {e}")
        finally:
            if cursor:
                cursor.close()
            conn.close()
            print("اتصال به دیتابیس بسته شد.")

در کد بالا:

  • `psycopg2.connect()`: شیء اتصال به پایگاه داده را برمی‌گرداند.
  • `conn.cursor()`: یک شیء Cursor ایجاد می‌کند که برای اجرای دستورات SQL استفاده می‌شود.
  • `cursor.execute()`: دستور SQL را اجرا می‌کند.
  • `cursor.fetchone()` / `cursor.fetchall()`: نتایج کوئری را از Cursor دریافت می‌کند.
  • `conn.commit()`: تغییرات ایجاد شده در تراکنش را به پایگاه داده اعمال می‌کند.
  • `conn.close()`: اتصال به پایگاه داده را می‌بندد و منابع را آزاد می‌کند. این کار بسیار مهم است تا از نشت منابع جلوگیری شود.

عملیات CRUD (Create, Read, Update, Delete)

بیایید نحوه انجام عملیات CRUD با استفاده از Psycopg2 را در یک برنامه Flask ببینیم.

۱. ایجاد جدول (Create Table)

ابتدا، یک تابع برای ایجاد یک جدول نمونه (مثلاً `users`) تعریف می‌کنیم:


def create_users_table(conn):
    try:
        cursor = conn.cursor()
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS users (
                id SERIAL PRIMARY KEY,
                username VARCHAR(50) UNIQUE NOT NULL,
                email VARCHAR(100) UNIQUE NOT NULL
            );
        """)
        conn.commit()
        print("جدول users با موفقیت ایجاد شد یا از قبل وجود داشت.")
    except Error as e:
        print(f"خطا در ایجاد جدول: {e}")
    finally:
        if cursor:
            cursor.close()

۲. درج داده (Insert Data)

برای جلوگیری از حملات SQL Injection، همیشه از کوئری‌های پارامترایز شده استفاده کنید. Psycopg2 از پارامترهای `%s` در کوئری SQL و تاپل داده‌ها در آرگومان دوم `execute` پشتیبانی می‌کند.


def insert_user(conn, username, email):
    try:
        cursor = conn.cursor()
        cursor.execute(
            "INSERT INTO users (username, email) VALUES (%s, %s);",
            (username, email)
        )
        conn.commit()
        print(f"کاربر {username} با موفقیت اضافه شد.")
    except Error as e:
        print(f"خطا در درج کاربر: {e}")
        conn.rollback() # برگشت به حالت قبل در صورت خطا
    finally:
        if cursor:
            cursor.close()

۳. خواندن داده (Read Data)


def get_all_users(conn):
    try:
        cursor = conn.cursor()
        cursor.execute("SELECT id, username, email FROM users;")
        users = cursor.fetchall()
        print("لیست کاربران:")
        for user in users:
            print(f"ID: {user[0]}, نام کاربری: {user[1]}, ایمیل: {user[2]}")
        return users
    except Error as e:
        print(f"خطا در دریافت کاربران: {e}")
        return []
    finally:
        if cursor:
            cursor.close()

def get_user_by_username(conn, username):
    try:
        cursor = conn.cursor()
        cursor.execute(
            "SELECT id, username, email FROM users WHERE username = %s;",
            (username,) # تاپل تک عنصری
        )
        user = cursor.fetchone()
        if user:
            print(f"کاربر یافت شد: ID: {user[0]}, نام کاربری: {user[1]}, ایمیل: {user[2]}")
        else:
            print(f"کاربری با نام کاربری {username} یافت نشد.")
        return user
    except Error as e:
        print(f"خطا در دریافت کاربر: {e}")
        return None
    finally:
        if cursor:
            cursor.close()

۴. به‌روزرسانی داده (Update Data)


def update_user_email(conn, username, new_email):
    try:
        cursor = conn.cursor()
        cursor.execute(
            "UPDATE users SET email = %s WHERE username = %s;",
            (new_email, username)
        )
        conn.commit()
        if cursor.rowcount > 0:
            print(f"ایمیل کاربر {username} به {new_email} به‌روزرسانی شد.")
        else:
            print(f"کاربری با نام کاربری {username} برای به‌روزرسانی یافت نشد.")
    except Error as e:
        print(f"خطا در به‌روزرسانی کاربر: {e}")
        conn.rollback()
    finally:
        if cursor:
            cursor.close()

۵. حذف داده (Delete Data)


def delete_user(conn, username):
    try:
        cursor = conn.cursor()
        cursor.execute(
            "DELETE FROM users WHERE username = %s;",
            (username,)
        )
        conn.commit()
        if cursor.rowcount > 0:
            print(f"کاربر {username} با موفقیت حذف شد.")
        else:
            print(f"کاربری با نام کاربری {username} برای حذف یافت نشد.")
    except Error as e:
        print(f"خطا در حذف کاربر: {e}")
        conn.rollback()
    finally:
        if cursor:
            cursor.close()

یکپارچه‌سازی با Flask

در یک برنامه Flask، مدیریت اتصالات پایگاه داده بسیار مهم است. هر درخواست HTTP معمولاً نیاز به یک اتصال به پایگاه داده دارد. بهترین رویکرد این است که اتصال را در ابتدای درخواست ایجاد کرده و در پایان درخواست آن را ببندید یا از یک Connection Pool استفاده کنید.


from flask import Flask, g
import psycopg2
from psycopg2 import Error

app = Flask(__name__)

# اطلاعات اتصال به دیتابیس
DATABASE_CONFIG = {
    "dbname": "flask_db",
    "user": "flask_user",
    "password": "your_secure_password",
    "host": "localhost",
    "port": "5432"
}

def get_db():
    """
    اتصال به دیتابیس را برمی‌گرداند و آن را در g (گلوکال Flask) ذخیره می‌کند
    تا در طول یک درخواست، اتصال مجدداً ایجاد نشود.
    """
    if 'db_conn' not in g:
        try:
            g.db_conn = psycopg2.connect(**DATABASE_CONFIG)
            print("اتصال به دیتابیس برای درخواست جاری ایجاد شد.")
        except Error as e:
            print(f"خطا در اتصال به دیتابیس: {e}")
            g.db_conn = None # در صورت خطا، اتصال را None قرار می‌دهیم
    return g.db_conn

@app.teardown_appcontext
def close_db_connection(exception):
    """
    اتصال به دیتابیس را پس از اتمام هر درخواست می‌بندد.
    """
    db_conn = g.pop('db_conn', None)
    if db_conn is not None:
        db_conn.close()
        print("اتصال به دیتابیس بسته شد.")

@app.route('/')
def index():
    conn = get_db()
    if conn is None:
        return "خطا: قادر به اتصال به دیتابیس نیستیم.", 500

    try:
        cursor = conn.cursor()
        create_users_table(conn) # اطمینان از وجود جدول
        insert_user(conn, "ali", "ali@example.com") # تست درج
        insert_user(conn, "reza", "reza@example.com") # تست درج
        
        users = get_all_users(conn) # تست خواندن
        
        output = ["

لیست کاربران:

    "] for user in users: output.append(f"
  • ID: {user[0]}, نام کاربری: {user[1]}, ایمیل: {user[2]}
  • ") output.append("
") update_user_email(conn, "ali", "ali_new@example.com") # تست به‌روزرسانی delete_user(conn, "reza") # تست حذف return "".join(output) except Exception as e: print(f"خطا در مسیر /: {e}") return f"خطا در پردازش درخواست: {e}", 500 if __name__ == '__main__': # در محیط واقعی، این عملیات‌ها نباید در مسیر روت باشند. # این فقط برای نمایش عملکرد است. # توصیه می‌شود عملیات اولیه دیتابیس را در اسکریپت‌های مجزا یا هنگام راه‌اندازی اپلیکیشن انجام دهید. app.run(debug=True)

مدیریت Pool اتصال (Connection Pool)

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

Psycopg2 ماژول `psycopg2.pool` را برای مدیریت Connection Pool ارائه می‌دهد. استفاده از Connection Pool به ویژه در محیط‌های تولیدی با ترافیک بالا توصیه می‌شود.


from flask import Flask, g
from psycopg2 import Error
from psycopg2.pool import SimpleConnectionPool

app = Flask(__name__)

DATABASE_CONFIG = {
    "dbname": "flask_db",
    "user": "flask_user",
    "password": "your_secure_password",
    "host": "localhost",
    "port": "5432"
}

# حداقل و حداکثر تعداد اتصالات در Pool
MIN_CONN = 1
MAX_CONN = 10

# ایجاد یک Connection Pool گلوبال
db_pool = None

def init_db_pool():
    global db_pool
    try:
        db_pool = SimpleConnectionPool(MIN_CONN, MAX_CONN, **DATABASE_CONFIG)
        print(f"Connection Pool با حداقل {MIN_CONN} و حداکثر {MAX_CONN} اتصال ایجاد شد.")
    except Error as e:
        print(f"خطا در ایجاد Connection Pool: {e}")
        db_pool = None

def get_pooled_connection():
    """
    یک اتصال از Pool دریافت می‌کند.
    """
    if db_pool is None:
        init_db_pool() # اگر Pool هنوز ایجاد نشده، آن را ایجاد می‌کند
        if db_pool is None: # اگر ایجاد Pool ناموفق بود
            return None
    try:
        conn = db_pool.getconn()
        return conn
    except Error as e:
        print(f"خطا در دریافت اتصال از Pool: {e}")
        return None

def put_pooled_connection(conn):
    """
    یک اتصال را به Pool برمی‌گرداند.
    """
    if db_pool and conn:
        db_pool.putconn(conn)

@app.before_request
def before_request_hook():
    g.db_conn = get_pooled_connection()
    if g.db_conn is None:
        # اگر اتصال از Pool دریافت نشد، یک خطای مناسب برگردانید.
        # در این مثال برای سادگی فقط پرینت می‌کنیم.
        print("خطا: قادر به دریافت اتصال از Connection Pool نیستیم.")

@app.teardown_appcontext
def close_pooled_connection(exception):
    conn = g.pop('db_conn', None)
    if conn is not None:
        put_pooled_connection(conn)
        print("اتصال به Connection Pool برگشت داده شد.")

@app.route('/')
def pool_index():
    conn = g.db_conn
    if conn is None:
        return "خطا: قادر به اتصال به دیتابیس نیستیم (از Pool).", 500
    
    # مثال استفاده از اتصال Pool شده
    cursor = None
    try:
        cursor = conn.cursor()
        cursor.execute("SELECT now();")
        current_time = cursor.fetchone()[0]
        return f"زمان فعلی دیتابیس (از Connection Pool): {current_time}"
    except Error as e:
        print(f"خطا در اجرای کوئری با Pool: {e}")
        conn.rollback() # در صورت خطا، تراکنش را برگردانید
        return "خطا در اجرای کوئری با Connection Pool.", 500
    finally:
        if cursor:
            cursor.close()

if __name__ == '__main__':
    init_db_pool() # Pool را هنگام راه‌اندازی اپلیکیشن ایجاد کنید
    app.run(debug=True)
    # هنگام بستن اپلیکیشن (مثلا با Ctrl+C)، Pool را هم ببندید
    # در محیط تولیدی، این راه‌اندازی و بستن باید توسط سرور WSGI مدیریت شود.
    # if db_pool:
    #     db_pool.closeall()

استفاده مستقیم از Psycopg2 به توسعه‌دهنده کنترل حداکثری می‌دهد و برای پروژه‌هایی که نیاز به بهینه‌سازی دقیق کوئری‌های SQL و عملکرد بالا دارند، گزینه مناسبی است. با این حال، حجم کد بیشتری برای مدیریت مدل‌ها و منطق پایگاه داده نیاز دارد.

استفاده از Flask-SQLAlchemy برای ORM

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

چرا ORM و Flask-SQLAlchemy؟

  • انتزاع: ORM جزئیات پایگاه داده را پنهان می‌کند. شما با اشیاء پایتون کار می‌کنید، نه با جداول و ستون‌های SQL.
  • کاهش کد boilerplate: بسیاری از عملیات CRUD به طور خودکار توسط ORM مدیریت می‌شوند.
  • امنیت: ORMها به طور خودکار از کوئری‌های پارامترایز شده استفاده می‌کنند و از حملات SQL Injection جلوگیری می‌کنند.
  • قابلیت نگهداری: کد پایگاه داده خواناتر و نگهداری آن آسان‌تر می‌شود.
  • انتقال‌پذیری: تغییر بین سیستم‌های مختلف پایگاه داده (مثلاً از PostgreSQL به MySQL) آسان‌تر می‌شود، هرچند که همچنان تفاوت‌های خاص پایگاه داده وجود خواهد داشت.
  • یکپارچگی با Flask: Flask-SQLAlchemy به طور خودکار شیء `db` را به برنامه Flask شما متصل می‌کند و مدیریت Session را تسهیل می‌بخشد.

۱. نصب Flask-SQLAlchemy

ابتدا Flask-SQLAlchemy و Psycopg2 را نصب کنید (اگر قبلاً نصب نکرده‌اید):


pip install Flask-SQLAlchemy psycopg2-binary

۲. پیکربندی و راه‌اندازی اولیه

برای استفاده از Flask-SQLAlchemy، باید URI اتصال به پایگاه داده را در تنظیمات Flask خود پیکربندی کنید. URI برای PostgreSQL معمولاً به شکل `postgresql://user:password@host:port/dbname` است.


from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# پیکربندی URI اتصال به PostgreSQL
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://flask_user:your_secure_password@localhost:5432/flask_db'
# گزینه زیر برای جلوگیری از اخطارهای مربوط به مصرف حافظه توسط SQLAlchemy است
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# ایجاد شیء SQLAlchemy
db = SQLAlchemy(app)

۳. تعریف مدل‌ها (Models)

در Flask-SQLAlchemy، مدل‌ها کلاس‌های پایتون هستند که جداول پایگاه داده شما را نشان می‌دهند. هر ویژگی (attribute) از کلاس، یک ستون در جدول را نشان می‌دهد.


# تعریف مدل User
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)

    def __repr__(self):
        return f''
  • `db.Model`: کلاس پایه برای همه مدل‌های SQLAlchemy.
  • `db.Column`: برای تعریف ستون‌ها در جدول.
  • `db.Integer`, `db.String`, `db.Boolean`, `db.DateTime` و غیره: انواع داده‌های SQLAlchemy.
  • `primary_key=True`: این ستون کلید اصلی جدول است.
  • `unique=True`: مقادیر در این ستون باید منحصربه‌فرد باشند.
  • `nullable=False`: این ستون نمی‌تواند خالی (NULL) باشد.
  • `__repr__`: یک متد کمکی برای نمایش بهتر شیء در کنسول.

۴. ایجاد جداول

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


# در یک اسکریپت یا هنگام راه‌اندازی برنامه Flask
with app.app_context():
    db.create_all()
    print("جداول دیتابیس ایجاد شدند (در صورت عدم وجود).")

`db.create_all()` تمام جداول را بر اساس مدل‌هایی که تعریف کرده‌اید، ایجاد می‌کند. توجه داشته باشید که این دستور جداول موجود را به‌روزرسانی نمی‌کند، فقط جداولی را که وجود ندارند، ایجاد می‌کند. برای مدیریت تغییرات شمای پایگاه داده در طول زمان، به بخش Flask-Migrate مراجعه خواهیم کرد.

۵. عملیات CRUD با Flask-SQLAlchemy

۱. درج داده (Create)


from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://flask_user:your_secure_password@localhost:5432/flask_db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)

    def __repr__(self):
        return f''

@app.route('/add_user//')
def add_user(username, email):
    with app.app_context():
        new_user = User(username=username, email=email)
        try:
            db.session.add(new_user)
            db.session.commit()
            return f'کاربر {username} با موفقیت اضافه شد! شناسه: {new_user.id}'
        except Exception as e:
            db.session.rollback() # در صورت خطا، تراکنش را برگردانید
            return f'خطا در اضافه کردن کاربر: {e}'
  • `db.session.add(obj)`: شیء را به جلسه (session) اضافه می‌کند، که نشان‌دهنده عملیات pending برای پایگاه داده است.
  • `db.session.commit()`: تغییرات در جلسه را به پایگاه داده اعمال می‌کند.
  • `db.session.rollback()`: در صورت خطا، تمام تغییرات در جلسه جاری را لغو می‌کند.

۲. خواندن داده (Read)


# ... (مدل User و پیکربندی Flask-SQLAlchemy مانند بالا) ...

@app.route('/users')
def list_users():
    with app.app_context():
        users = User.query.all() # دریافت همه کاربران
        # users = User.query.filter_by(username='ali').first() # فیلتر بر اساس نام کاربری
        # users = User.query.get(1) # دریافت کاربر با شناسه
        
        output = ['

لیست کاربران:

    '] for user in users: output.append(f'
  • ID: {user.id}, نام کاربری: {user.username}, ایمیل: {user.email}
  • ') output.append('
') return ''.join(output) @app.route('/user/') def get_user(user_id): with app.app_context(): user = User.query.get(user_id) # دریافت کاربر با شناسه if user: return f'

جزئیات کاربر:

ID: {user.id}, نام کاربری: {user.username}, ایمیل: {user.email}

' return 'کاربر یافت نشد.', 404
  • `User.query`: نقطه شروع برای ساخت کوئری‌ها.
  • `all()`: تمام نتایج را به صورت لیستی از اشیاء مدل برمی‌گرداند.
  • `first()`: اولین نتیجه را برمی‌گرداند یا `None` اگر یافت نشد.
  • `get(id)`: یک شیء مدل را بر اساس کلید اصلی آن برمی‌گرداند.
  • `filter_by()`: برای فیلتر کردن بر اساس شرایط کلید-مقدار.
  • `filter()`: برای فیلتر کردن با استفاده از عبارات پیچیده‌تر SQLAlchemy.

۳. به‌روزرسانی داده (Update)


# ... (مدل User و پیکربندی Flask-SQLAlchemy مانند بالا) ...

@app.route('/update_user_email//')
def update_user_email(user_id, new_email):
    with app.app_context():
        user = User.query.get(user_id)
        if user:
            user.email = new_email # تغییر مقدار ویژگی شیء
            try:
                db.session.commit() # اعمال تغییر به دیتابیس
                return f'ایمیل کاربر {user.username} به {new_email} به‌روزرسانی شد.'
            except Exception as e:
                db.session.rollback()
                return f'خطا در به‌روزرسانی ایمیل: {e}'
        return 'کاربر یافت نشد.', 404

به‌روزرسانی داده‌ها در SQLAlchemy بسیار ساده است. کافی است شیء مورد نظر را از پایگاه داده بازیابی کنید، ویژگی‌های آن را تغییر دهید و سپس `db.session.commit()` را فراخوانی کنید.

۴. حذف داده (Delete)


# ... (مدل User و پیکربندی Flask-SQLAlchemy مانند بالا) ...

@app.route('/delete_user/')
def delete_user(user_id):
    with app.app_context():
        user = User.query.get(user_id)
        if user:
            try:
                db.session.delete(user) # حذف شیء از جلسه
                db.session.commit() # اعمال حذف به دیتابیس
                return f'کاربر {user.username} با موفقیت حذف شد.'
            except Exception as e:
                db.session.rollback()
                return f'خطا در حذف کاربر: {e}'
        return 'کاربر یافت نشد.', 404

if __name__ == '__main__':
    with app.app_context():
        db.create_all() # ایجاد جداول در زمان راه‌اندازی (فقط برای توسعه)
    app.run(debug=True)

برای حذف یک شیء، آن را از پایگاه داده بازیابی کرده، با `db.session.delete()` به جلسه اضافه کرده و سپس با `db.session.commit()` تغییرات را اعمال کنید.

مدیریت Session

Flask-SQLAlchemy به طور خودکار یک `db.session` را برای هر درخواست HTTP مدیریت می‌کند. این جلسه در ابتدای درخواست ایجاد شده و در پایان درخواست (یا هنگام وقوع خطا) به طور خودکار بسته می‌شود یا بازگردانده می‌شود. این رفتار با استفاده از دکوراتور `@app.teardown_appcontext` انجام می‌شود تا اطمینان حاصل شود که منابع پایگاه داده به درستی آزاد می‌شوند. این یکی از مزایای اصلی استفاده از Flask-SQLAlchemy نسبت به استفاده مستقیم از SQLAlchemy است، زیرا نیاز به مدیریت دستی `Session` را کاهش می‌دهد.

Flask-SQLAlchemy به شدت توصیه می‌شود مگر اینکه نیازهای خاصی برای کنترل سطح پایین SQL یا عملکردی داشته باشید که ORM نمی‌تواند آن را به خوبی مدیریت کند.

مدیریت Schema و Migrations با Flask-Migrate

در طول چرخه حیات یک برنامه وب، شمای پایگاه داده به ندرت ثابت می‌ماند. با رشد برنامه، ممکن است نیاز به اضافه کردن ستون‌های جدید به جداول موجود، ایجاد جداول جدید، تغییر انواع داده یا حذف ستون‌ها باشد. مدیریت دستی این تغییرات (Schema Migrations) در محیط‌های تولیدی و تیم‌های توسعه، می‌تواند بسیار دشوار و پرخطا باشد. Flask-Migrate یک اکستنشن برای Flask است که این فرآیند را با استفاده از کتابخانه قدرتمند Alembic به صورت خودکار و نسخه‌بندی شده (version-controlled) مدیریت می‌کند.

مشکل Migrations

تصور کنید یک برنامه Flask با پایگاه داده PostgreSQL دارید و آن را در حال تولید (production) مستقر کرده‌اید. پس از مدتی، تصمیم می‌گیرید یک فیلد جدید (مثلاً `age`) به مدل `User` اضافه کنید. اگر شما صرفاً کد مدل را تغییر دهید و `db.create_all()` را دوباره اجرا کنید، هیچ اتفاقی نمی‌افتد زیرا جدول `users` از قبل وجود دارد. راه‌حل این است که به صورت دستی یک دستور `ALTER TABLE` در پایگاه داده تولید اجرا کنید. این رویکرد مشکل‌ساز است زیرا:

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

راه‌حل: Flask-Migrate (بر پایه Alembic)

Flask-Migrate ابزارهایی را برای تولید و اعمال (apply) اسکریپت‌های تغییر شمای پایگاه داده (migrations) ارائه می‌دهد. این اسکریپت‌ها نسخه‌بندی شده‌اند و می‌توانند به جلو (upgrade) یا عقب (downgrade) برگردانده شوند.

۱. نصب Flask-Migrate


pip install Flask-Migrate

۲. پیکربندی و راه‌اندازی اولیه

شما نیاز دارید Flask-Migrate را به برنامه Flask و شیء `db` (از Flask-SQLAlchemy) خود متصل کنید.


from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://flask_user:your_secure_password@localhost:5432/flask_db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    # فرض کنید می‌خواهیم یک ستون جدید 'age' اضافه کنیم
    age = db.Column(db.Integer, nullable=True) # اضافه کردن این ستون جدید

    def __repr__(self):
        return f''

# اتصال Flask-Migrate به برنامه و دیتابیس
migrate = Migrate(app, db)

# برای اجرای دستورات Flask-Migrate از خط فرمان،
# باید اپلیکیشن Flask شما در دسترس باشد.
# این معمولاً با تعریف متغیر محیطی FLASK_APP انجام می‌شود:
# export FLASK_APP=app.py (در لینوکس/macOS)
# $env:FLASK_APP = "app.py" (در ویندوز PowerShell)

# در عمل، برنامه Flask شما معمولاً با یک فایل 'wsgi.py' یا 'app.py' شروع می‌شود.
# اگر این کد را در یک فایل به نام 'app.py' ذخیره کرده‌اید:
if __name__ == '__main__':
    app.run(debug=True)

۳. مراحل Workflow با Flask-Migrate

الف) مقداردهی اولیه Migrations (یک بار)

اولین گام، مقداردهی اولیه دایرکتوری migrations است. این کار یک پوشه `migrations` با فایل‌های پیکربندی Alembic و یک پوشه `versions` برای اسکریپت‌های migration ایجاد می‌کند.


(venv) flask db init

این دستور یک فایل `alembic.ini` و یک پوشه `migrations` در ریشه پروژه شما ایجاد می‌کند.

ب) ایجاد اولین Migration (برای شمای اولیه)

حال که `migrations` را مقداردهی اولیه کرده‌اید، می‌توانید اولین اسکریپت migration را برای ایجاد جداول فعلی برنامه (بر اساس مدل‌های SQLAlchemy) ایجاد کنید.


(venv) flask db migrate -m "Initial migration"

این دستور فایل پایتون جدیدی در پوشه `migrations/versions` ایجاد می‌کند. این فایل حاوی تابع `upgrade()` برای اعمال تغییرات و `downgrade()` برای برگرداندن آن‌ها است. Flask-Migrate به طور خودکار تفاوت بین مدل‌های SQLAlchemy شما و شمای پایگاه داده فعلی را تشخیص داده و اسکریپت migration را تولید می‌کند. در اولین migration، این اسکریپت دستورات `CREATE TABLE` را برای تمام مدل‌های شما خواهد داشت.

حتماً فایل migration تولید شده را باز کرده و آن را بررسی کنید تا مطمئن شوید تغییرات مورد انتظار شما در آن قرار دارد.

ج) اعمال Migration به پایگاه داده

برای اعمال تغییرات موجود در اسکریپت migration به پایگاه داده، از دستور `upgrade` استفاده کنید:


(venv) flask db upgrade

این دستور `upgrade()` را در اسکریپت migration شما اجرا می‌کند و جداول را در پایگاه داده ایجاد یا به‌روزرسانی می‌کند. Alembic یک جدول `alembic_version` در پایگاه داده شما ایجاد می‌کند تا نسخه فعلی شمای پایگاه داده را ردیابی کند.

د) ایجاد Migrations بعدی (هنگام تغییر مدل‌ها)

فرض کنید پس از مدتی تصمیم می‌گیرید یک ستون `age` به مدل `User` اضافه کنید، همانطور که در مثال کد بالا نشان داده شد. پس از اعمال تغییر در مدل پایتون:


class User(db.Model):
    # ...
    age = db.Column(db.Integer, nullable=True) # ستون جدید

برای تولید یک migration جدید که این تغییر را منعکس کند، دوباره دستور `migrate` را اجرا کنید:


(venv) flask db migrate -m "Add age column to user"

Flask-Migrate یک فایل migration جدید ایجاد می‌کند که حاوی دستور `ALTER TABLE ADD COLUMN age …` است. دوباره، فایل را بررسی کنید.

سپس، این تغییر را به پایگاه داده اعمال کنید:


(venv) flask db upgrade

ه) بازگشت به Migration قبلی (Rollback)

اگر بعد از اعمال یک migration متوجه مشکلی شدید، می‌توانید با استفاده از دستور `downgrade` به نسخه قبلی شمای پایگاه داده بازگردید:


(venv) flask db downgrade

این دستور تابع `downgrade()` را در آخرین اسکریپت migration اجرا می‌کند. می‌توانید با `flask db downgrade ` به یک نسخه خاص بازگردید یا با `flask db downgrade -1` یک گام به عقب برگردید.

دستورات مفید دیگر Flask-Migrate

  • `flask db current`: نشان می‌دهد که کدام migration در حال حاضر روی پایگاه داده اعمال شده است.
  • `flask db history`: تاریخچه کامل تمام migrations را نمایش می‌دهد.
  • `flask db stamp head`: نسخه فعلی پایگاه داده را به آخرین migration (بدون اجرای آن) تنظیم می‌کند. (مفید برای زمانی که پایگاه داده از قبل در آخرین نسخه است و نمی‌خواهید `upgrade` کنید.)

اهمیت Flask-Migrate

Flask-Migrate ابزاری ضروری برای هر پروژه Flask با پایگاه داده است که در حال تکامل است. با استفاده از آن، شما:

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

ادغام Flask-Migrate از همان ابتدا در پروژه، به طور قابل توجهی فرآیند توسعه و نگهداری برنامه شما را بهبود می‌بخشد.

بهینه‌سازی عملکرد و بهترین شیوه‌ها

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

۱. مدیریت Connection Pooling

همانطور که قبلاً در بخش Psycopg2 اشاره شد، باز و بسته کردن مکرر اتصالات پایگاه داده در هر درخواست HTTP، سربار قابل توجهی دارد. Connection Pooling راه‌حل این مشکل است. یک Connection Pool مجموعه‌ای از اتصالات دیتابیس از پیش ایجاد شده و آماده استفاده را نگهداری می‌کند.

  • برای Psycopg2: از `psycopg2.pool.SimpleConnectionPool` یا `psycopg2.pool.ThreadedConnectionPool` (برای برنامه‌های چند رشته‌ای) استفاده کنید. این Pool باید در هنگام راه‌اندازی برنامه (یک بار) ایجاد شود و اتصالات از آن در هر درخواست گرفته و در پایان درخواست به آن برگردانده شوند.
  • برای Flask-SQLAlchemy: SQLAlchemy دارای یک Connection Pool داخلی است که به طور پیش‌فرض فعال است. می‌توانید با تنظیم پارامترهایی مانند `pool_size` (تعداد اتصالات در Pool) و `max_overflow` (تعداد اتصالات اضافی که می‌توانند ایجاد شوند) در `SQLALCHEMY_ENGINE_OPTIONS` آن را پیکربندی کنید.
    
            app.config['SQLALCHEMY_DATABASE_URI'] = '...'
            app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
            app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
                'pool_size': 10,
                'max_overflow': 20,
                'pool_recycle': 3600 # هر یک ساعت اتصالات را بازیافت می‌کند
            }
            db = SQLAlchemy(app)
            

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

۲. بهینه‌سازی کوئری‌ها و ORM

  • شناسایی کوئری‌های ناکارآمد (N+1 Problem): این مشکل زمانی رخ می‌دهد که برای بازیابی یک لیست از اشیاء، ابتدا لیستی از اشیاء اصلی را کوئری می‌کنید و سپس برای هر شیء اصلی، یک کوئری جداگانه برای بازیابی روابط مرتبط آن انجام می‌دهید. این می‌تواند منجر به N+1 کوئری شود.
    
            # مثال مشکل N+1
            users = User.query.all()
            for user in users:
                print(user.posts.all()) # هر بار یک کوئری جدید برای posts
            

    برای حل این مشکل در SQLAlchemy، از `joinedload` یا `subqueryload` برای بارگذاری eagerly (به صورت مشتاقانه) روابط استفاده کنید:

    
            from sqlalchemy.orm import joinedload
            # حل مشکل N+1 با joinedload
            users = User.query.options(joinedload(User.posts)).all()
            for user in users:
                print(user.posts) # posts از قبل بارگذاری شده‌اند
            
  • استفاده از Indexes: برای ستون‌هایی که اغلب در بندهای `WHERE`، `JOIN` یا `ORDER BY` استفاده می‌شوند، Index ایجاد کنید. Indexes به پایگاه داده کمک می‌کنند تا داده‌ها را سریع‌تر پیدا کند. Flask-Migrate (Alembic) به شما اجازه می‌دهد تا Indexes را در migrations خود اضافه کنید.
  • پرهیز از `SELECT *`: فقط ستون‌هایی را انتخاب کنید که واقعاً به آن‌ها نیاز دارید. این کار حجم داده‌های منتقل شده و بار روی پایگاه داده را کاهش می‌دهد. در ORM، این به معنی استفاده از `query.with_entities()` است.
  • Batch Operations: به جای اجرای تعداد زیادی عملیات INSERT/UPDATE/DELETE به صورت جداگانه، آن‌ها را در یک تراکنش یا به صورت Batch (عملیات دسته‌ای) انجام دهید. ORMها معمولاً متدهایی برای این منظور دارند.

۳. مدیریت تراکنش‌ها (Transaction Management)

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

  • با Psycopg2: `conn.commit()` برای اعمال تغییرات و `conn.rollback()` برای لغو آن‌ها. همیشه این عملیات را در بلوک‌های `try-except-finally` انجام دهید.
  • با Flask-SQLAlchemy: `db.session.commit()` و `db.session.rollback()`. Flask-SQLAlchemy به طور خودکار `db.session.remove()` را پس از هر درخواست فراخوانی می‌کند که اطمینان از بسته شدن صحیح Session را می‌دهد.

۴. امنیت

  • جلوگیری از SQL Injection:
    • با Psycopg2: همیشه از کوئری‌های پارامترایز شده (با `%s`) استفاده کنید. هرگز متغیرهای ورودی کاربر را مستقیماً در رشته SQL قرار ندهید.
    • با Flask-SQLAlchemy: ORM به طور خودکار از پارامترایز کردن کوئری‌ها مراقبت می‌کند، بنابراین شما به طور پیش‌فرض در برابر SQL Injection محافظت می‌شوید، مگر اینکه عمداً از روش‌های ناامن مانند `db.session.execute(text(…))` بدون پارامترایز کردن استفاده کنید.
  • متغیرهای محیطی برای اطلاعات حساس: اطلاعات حساس مانند نام کاربری، رمز عبور و URI پایگاه داده را هرگز مستقیماً در کد یا کنترل نسخه (Git) قرار ندهید. از متغیرهای محیطی (environment variables) یا ابزارهای مدیریت secret (مانند HashiCorp Vault یا AWS Secrets Manager) استفاده کنید. برای توسعه محلی، می‌توانید از کتابخانه `python-dotenv` استفاده کنید.
  • مجوزهای پایگاه داده (Database Permissions): کاربری را که برنامه Flask شما برای اتصال به پایگاه داده استفاده می‌کند، فقط حداقل مجوزهای لازم را به آن بدهید. از اعطای `ALL PRIVILEGES` به کاربر برنامه در محیط تولید خودداری کنید. فقط مجوزهای `SELECT`, `INSERT`, `UPDATE`, `DELETE` و `REFERENCES` (در صورت نیاز به کلیدهای خارجی) روی جداول مربوطه را بدهید.

۵. Logging و Error Handling

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


import logging
from flask import Flask

app = Flask(__name__)
app.logger.setLevel(logging.ERROR) # فقط خطاهای مهم را لاگ می‌کند

@app.errorhandler(500)
def handle_internal_server_error(e):
    app.logger.error(f"Internal Server Error: {e}")
    # همچنین می‌توانید جزئیات بیشتری از خطا را لاگ کنید
    return "یک خطای داخلی سرور رخ داد.", 500

# در یک مسیر یا تابع عملیاتی
try:
    # عملیات دیتابیس
    pass
except Exception as e:
    app.logger.error(f"خطای دیتابیس در مسیر X: {e}", exc_info=True)
    db.session.rollback() # در صورت استفاده از SQLAlchemy
    # مدیریت خطا و بازگرداندن پاسخ مناسب

۶. تستینگ (Testing)

تست‌های واحد (Unit Tests) و تست‌های یکپارچه‌سازی (Integration Tests) برای لایه پایگاه داده بسیار مهم هستند. برای تست، می‌توانید از یک پایگاه داده جداگانه و موقت (مثلاً یک دیتابیس PostgreSQL با نام `flask_test_db`) یا یک پایگاه داده درون حافظه (مانند SQLite، البته با احتیاط به دلیل تفاوت‌های SQL) استفاده کنید. Flask-SQLAlchemy تست را تسهیل می‌کند.

۷. استراتژی‌های استقرار (Deployment)

  • مقیاس‌بندی پایگاه داده: در محیط‌های تولیدی با ترافیک بالا، پایگاه داده ممکن است نیاز به مقیاس‌بندی عمودی (افزایش قدرت سرور) یا افقی (استفاده از Replication Master-Slave یا Sharding) داشته باشد.
  • پشتیبان‌گیری و بازیابی (Backup & Recovery): یک استراتژی قوی برای پشتیبان‌گیری منظم از پایگاه داده و توانایی بازیابی سریع آن در صورت بروز فاجعه، ضروری است.
  • مانیتورینگ: پایگاه داده خود را به طور فعال مانیتور کنید تا مشکلات عملکردی (مانند کوئری‌های کند، Deadlockها) را به سرعت شناسایی و رفع کنید.
  • استفاده از PgBouncer: برای برنامه‌هایی با تعداد زیادی اتصال همزمان، PgBouncer یک Poolر اتصال خارجی و قدرتمند برای PostgreSQL است که می‌تواند سربار مدیریت اتصالات را به طور قابل توجهی کاهش دهد.

با رعایت این بهترین شیوه‌ها، می‌توانید اطمینان حاصل کنید که برنامه Flask شما با PostgreSQL نه تنها کار می‌کند، بلکه به طور موثر، امن و قابل اعتماد عمل می‌کند.

جایگزین‌ها و ابزارهای پیشرفته

در حالی که Psycopg2 برای اتصال مستقیم و Flask-SQLAlchemy برای ORM محبوب‌ترین گزینه‌ها هستند، اکوسیستم پایتون و PostgreSQL ابزارهای جایگزین و پیشرفته‌تری را نیز برای سناریوهای خاص ارائه می‌دهد. شناخت این جایگزین‌ها می‌تواند به شما در انتخاب بهترین ابزار برای نیازهای خاص پروژه کمک کند.

۱. SQLAlchemy Core (برای کنترل دقیق‌تر بدون ORM)

SQLAlchemy در واقع از دو بخش اصلی تشکیل شده است: Core و ORM. Flask-SQLAlchemy بر پایه بخش ORM استوار است. اگر می‌خواهید از مزایای ساختار کوئری‌های بیلدینگ (SQL Expression Language) SQLAlchemy بهره‌مند شوید، اما نمی‌خواهید سربار و انتزاع ORM را داشته باشید، SQLAlchemy Core گزینه مناسبی است.

SQLAlchemy Core به شما اجازه می‌دهد تا کوئری‌های SQL را به صورت برنامه‌نویسی‌شده (programmatically) با استفاده از اشیاء پایتون (مثل `Table`, `Column`, `select`, `insert`) بسازید و اجرا کنید. این رویکرد انعطاف‌پذیری و کنترل نزدیک‌تری به SQL خام را فراهم می‌کند، در حالی که همچنان از پارامترایز کردن کوئری‌ها برای جلوگیری از SQL Injection محافظت می‌کند.


from sqlalchemy import create_engine, text, Table, Column, Integer, String, MetaData

# اتصال به دیتابیس
engine = create_engine('postgresql://flask_user:your_secure_password@localhost:5432/flask_db')
metadata = MetaData()

# تعریف جدول به صورت Core
users_table = Table(
    'users', metadata,
    Column('id', Integer, primary_key=True),
    Column('username', String(50), unique=True, nullable=False),
    Column('email', String(100), unique=True, nullable=False)
)

# ایجاد جداول (اگر وجود ندارند)
metadata.create_all(engine)

# درج داده
with engine.connect() as connection:
    connection.execute(users_table.insert().values(username='core_user', email='core@example.com'))
    connection.commit()

# انتخاب داده
with engine.connect() as connection:
    result = connection.execute(users_table.select())
    for row in result:
        print(row)

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

۲. AsyncPG (برای برنامه‌های ناهمگام – Asynchronous)

اگر برنامه Flask شما با استفاده از `asyncio` (مانند فریمورک Quart که رابط Flask-like دارد) به صورت ناهمگام طراحی شده است، `psycopg2` که یک کتابخانه بلاک‌کننده (blocking) است، انتخاب مناسبی نخواهد بود. در این موارد، `asyncpg` یک درایور PostgreSQL ناهمگام و با عملکرد بالا برای پایتون است.

`asyncpg` برای کارهای I/O غیربلاک‌کننده بهینه شده است و برای برنامه‌هایی که نیاز به مقیاس‌پذیری بالا و مدیریت هزاران اتصال همزمان دارند، ایده‌آل است. برای استفاده از آن، شما نیاز به یک فریمورک وب ناهمگام مانند Quart یا FastAPI دارید.


import asyncpg
import asyncio

async def run_query():
    conn = None
    try:
        conn = await asyncpg.connect(user='flask_user', password='your_secure_password',
                                     database='flask_db', host='localhost')
        rows = await conn.fetch('SELECT $1::text, $2::text;', 'hello', 'world')
        print(rows)
    except Exception as e:
        print(f"Error: {e}")
    finally:
        if conn:
            await conn.close()

# این کد باید در یک حلقه رویداد asyncio اجرا شود
# asyncio.run(run_query())

توجه داشته باشید که Flask به صورت پیش‌فرض یک فریمورک همگام است. اگر به عملکرد ناهمگام نیاز دارید، باید به فریمورک‌هایی مانند Quart یا Fastapi فکر کنید که از `async` و `await` به طور کامل پشتیبانی می‌کنند.

۳. PgBouncer (Pooling Connection خارجی)

برای برنامه‌هایی با ترافیک بسیار بالا و تعداد زیاد worker process که هر کدام به Connection Pool خود نیاز دارند، مدیریت Connection Pooling در سطح برنامه ممکن است کافی نباشد. `PgBouncer` یک Connection Poolر خارجی و مستقل برای PostgreSQL است که در جلوی پایگاه داده قرار می‌گیرد. این ابزار برای مدیریت تعداد زیادی اتصال مشتری و نگهداری تعداد محدودی اتصال به سرور PostgreSQL طراحی شده است.

مزایای PgBouncer:

  • کاهش سربار سرور دیتابیس: PostgreSQL به ازای هر اتصال جدید سرباری ایجاد می‌کند. PgBouncer این سربار را با مدیریت اتصالات مشتری و نگهداری اتصالات فعال کمتری به PostgreSQL کاهش می‌دهد.
  • افزایش مقیاس‌پذیری: به برنامه شما اجازه می‌دهد تا اتصالات بیشتری را نسبت به آنچه PostgreSQL می‌تواند به طور موثر مدیریت کند، برقرار کند.
  • سهولت مدیریت: می‌توانید تنظیمات Pooling را بدون نیاز به تغییر کد برنامه، پیکربندی کنید.

راه‌اندازی PgBouncer نیاز به نصب و پیکربندی جداگانه دارد و به عنوان یک سرویس مستقل اجرا می‌شود. برنامه Flask شما به جای اتصال مستقیم به PostgreSQL، به PgBouncer متصل می‌شود و PgBouncer اتصالات را به PostgreSQL مدیریت می‌کند.

۴. Containerization با Docker

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

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

دستور `docker run` برای راه‌اندازی یک کانتینر PostgreSQL قبلاً در بخش “پیش‌نیازها” ارائه شد. برای مدیریت پیچیده‌تر، می‌توانید از `docker-compose` برای تعریف سرویس‌های برنامه و پایگاه داده در یک فایل استفاده کنید.

۵. پایگاه‌های داده ابری (Cloud Databases)

برای استقرار در محیط تولید، به جای مدیریت سرور PostgreSQL خودتان، استفاده از سرویس‌های پایگاه داده مدیریت شده توسط ارائه‌دهندگان ابری (مانند AWS RDS for PostgreSQL, Google Cloud SQL for PostgreSQL, Azure Database for PostgreSQL) می‌تواند مزایای زیادی داشته باشد:

  • مدیریت آسان: ارائه‌دهنده ابری مسئولیت نگهداری، پشتیبان‌گیری، پچ کردن (patching) و مقیاس‌بندی پایگاه داده را بر عهده می‌گیرد.
  • قابلیت اطمینان بالا: این سرویس‌ها معمولاً دارای ویژگی‌های High Availability و Replication هستند.
  • مقیاس‌پذیری: به راحتی می‌توانید منابع پایگاه داده را (CPU, RAM, Storage) بر اساس نیازهای برنامه خود مقیاس‌بندی کنید.
  • امنیت: ارائه دهندگان ابری ابزارهای امنیتی پیشرفته‌ای مانند رمزنگاری داده‌ها در حال انتقال و در حالت سکون، و فایروال‌های شبکه را ارائه می‌دهند.

اگرچه هزینه استفاده از پایگاه داده‌های ابری ممکن است بیشتر باشد، اما صرفه‌جویی در زمان و منابع مدیریتی، به ویژه برای تیم‌های کوچک، می‌تواند بسیار ارزشمند باشد. URI اتصال به این پایگاه داده‌ها را معمولاً می‌توانید از کنسول مدیریتی سرویس ابری دریافت کنید و آن را به عنوان متغیر محیطی در برنامه Flask خود تنظیم نمایید.

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

نتیجه‌گیری

در این راهنمای جامع، ما به طور عمیق به فرآیند اتصال Flask به دیتابیس PostgreSQL پرداختیم. از راه‌اندازی اولیه محیط توسعه و نصب پیش‌نیازها گرفته تا بررسی دو رویکرد اصلی برای تعامل با پایگاه داده: اتصال مستقیم و سطح پایین با `Psycopg2` و استفاده از ORM قدرتمند `Flask-SQLAlchemy`.

آموزش دیدیم که چگونه با `Psycopg2`، اتصالات را به صورت دستی مدیریت کنیم، کوئری‌های SQL خام را اجرا کرده و عملیات CRUD را پیاده‌سازی کنیم. تاکید شد که با این روش، مسئولیت مدیریت اتصالات و جلوگیری از SQL Injection به عهده توسعه‌دهنده است و استفاده از Connection Pool برای بهینه‌سازی عملکرد ضروری است.

سپس، با `Flask-SQLAlchemy` آشنا شدیم، که با انتزاع لایه پایگاه داده، توسعه را به شکل شیءگرا ساده‌تر می‌کند. با تعریف مدل‌ها و استفاده از `db.session`، دیدیم که چگونه می‌توانیم عملیات CRUD را با کد کمتر و خوانایی بیشتر انجام دهیم، در حالی که از مزایای داخلی ORM مانند محافظت در برابر SQL Injection بهره‌مند می‌شویم.

یکی از جنبه‌های حیاتی در توسعه برنامه‌های وب، مدیریت تکامل شمای پایگاه داده است. `Flask-Migrate` به عنوان یک لایه روی `Alembic`، ابزار قدرتمندی را برای ایجاد، اعمال و بازگرداندن تغییرات شمای پایگاه داده به صورت نسخه‌بندی شده ارائه داد که خطاهای انسانی را کاهش داده و همکاری تیمی را تسهیل می‌کند.

در ادامه، بهینه‌سازی عملکرد و بهترین شیوه‌ها را مورد بحث قرار دادیم، از جمله اهمیت Connection Pooling، بهینه‌سازی کوئری‌ها برای جلوگیری از مشکل N+1، استفاده از Indexes، مدیریت صحیح تراکنش‌ها، و نکات امنیتی مانند استفاده از متغیرهای محیطی برای اطلاعات حساس و اعطای حداقل مجوزها به کاربر دیتابیس. همچنین، اهمیت Logging و Testing برای نگهداری و پایداری برنامه مورد تاکید قرار گرفت.

در نهایت، به بررسی جایگزین‌ها و ابزارهای پیشرفته‌ای مانند `SQLAlchemy Core` برای کنترل دقیق‌تر، `AsyncPG` برای برنامه‌های ناهمگام، `PgBouncer` به عنوان یک Connection Poolر خارجی و قدرتمند، استفاده از `Docker` برای Containerization و مزایای استفاده از `Cloud Databases` در محیط تولید پرداختیم.

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

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

“تسلط به برنامه‌نویسی پایتون با هوش مصنوعی: آموزش کدنویسی هوشمند با ChatGPT”

قیمت اصلی 2.290.000 ریال بود.قیمت فعلی 1.590.000 ریال است.

"تسلط به برنامه‌نویسی پایتون با هوش مصنوعی: آموزش کدنویسی هوشمند با ChatGPT"

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

ویژگی‌های کلیدی:

بدون نیاز به تجربه قبلی برنامه‌نویسی

زیرنویس فارسی با ترجمه حرفه‌ای

۳۰ ٪ تخفیف ویژه برای دانشجویان و دانش آموزان