۱۰ نمونه کد کاربردی Flask برای شروع سریع

فهرست مطالب

۱۰ نمونه کد کاربردی Flask برای شروع سریع

Flask، میکرو فریم‌ورک قدرتمند پایتون، به دلیل سادگی، انعطاف‌پذیری و جامعه فعال توسعه‌دهندگانش، گزینه‌ای عالی برای ساخت برنامه‌های وب در اندازه‌های مختلف، از APIهای کوچک تا برنامه‌های کاربردی پیچیده، محسوب می‌شود. فلسفه “با باتری‌ها گنجانده شده، اما قابل تعویض” به توسعه‌دهندگان این امکان را می‌دهد که با حداقل وابستگی‌ها شروع کنند و در صورت نیاز، ابزارها و کتابخانه‌های مورد نظر خود را ادغام کنند. این پست برای توسعه‌دهندگانی طراحی شده است که با مفاهیم پایه پایتون آشنا هستند و به دنبال غواصی عمیق‌تر در قابلیت‌های Flask از طریق مثال‌های کاربردی و عملی هستند.

در این مقاله جامع، ما ۱۰ نمونه کد کاربردی Flask را بررسی خواهیم کرد که به شما کمک می‌کنند تا مهارت‌های خود را در این فریم‌ورک تقویت کرده و پروژه‌های خود را با سرعت و کارایی بیشتری پیش ببرید. هر نمونه با توضیحات کامل، نکات مهم سئو، بهترین شیوه‌ها و جزئیات فنی ارائه می‌شود تا درک عمیق‌تری از چگونگی عملکرد Flask در سناریوهای مختلف به دست آورید.

مقدمه: چرا Flask برای توسعه سریع؟

Flask یک میکرو فریم‌ورک وب برای زبان برنامه‌نویسی پایتون است که با هدف اصلی سادگی و انعطاف‌پذیری طراحی شده است. برخلاف فریم‌ورک‌های “Full-stack” مانند Django که همراه با ORM داخلی، سیستم احراز هویت و پنل ادمین از پیش تعریف شده ارائه می‌شوند، Flask تنها ابزارهای ضروری برای ساخت یک برنامه وب را فراهم می‌کند: یک هسته WSGI، مسیردهی (routing) و یک سیستم قالب‌بندی (templating) با Jinja2. این رویکرد حداقل‌گرایانه، Flask را به گزینه‌ای ایده‌آل برای موارد زیر تبدیل کرده است:

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

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

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

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

۱. نصب پایتون (Python)

اگر پایتون ۳ را ندارید، آن را از وب‌سایت رسمی python.org دانلود و نصب کنید.

۲. ایجاد محیط مجازی (Virtual Environment)

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

python3 -m venv venv

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

  • در Linux/macOS:
    source venv/bin/activate
  • در Windows (CMD):
    venv\Scripts\activate.bat
  • در Windows (PowerShell):
    venv\Scripts\Activate.ps1

۳. نصب Flask

پس از فعال‌سازی محیط مجازی، Flask را نصب کنید:

pip install Flask

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

۱. پروژه پایه Flask و اجرای آن: ساختار و آغاز به کار

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

کد: app.py

# app.py
from flask import Flask

# ایجاد یک نمونه از برنامه Flask
# __name__ به Flask می‌گوید که فایل اصلی ما کجاست.
# این برای پیدا کردن منابع مانند الگوها و فایل‌های استاتیک مهم است.
app = Flask(__name__)

# تعریف یک مسیر (route) برای آدرس ریشه (/)
# هنگامی که کاربر به این آدرس دسترسی پیدا می‌کند، تابع hello_world اجرا می‌شود.
@app.route('/')
def hello_world():
    # بازگرداندن یک رشته متنی به عنوان پاسخ HTTP
    return '

سلام Flask! به اولین برنامه شما خوش آمدید.

' # تعریف یک مسیر دیگر @app.route('/about') def about(): return '

این یک صفحه درباره ما در Flask است.

' # این شرط تضمین می‌کند که سرور توسعه فقط زمانی اجرا شود که فایل app.py مستقیماً اجرا شود. # نه زمانی که به عنوان یک ماژول وارد شود. if __name__ == '__main__': # اجرای برنامه Flask در حالت دیباگ. # حالت دیباگ برای توسعه بسیار مفید است، زیرا: # ۱. تغییرات کد را به طور خودکار بارگذاری مجدد می‌کند. # ۲. یک دیباگر تعاملی برای خطاهای برنامه فراهم می‌کند. # در محیط‌های Production هرگز از debug=True استفاده نکنید. app.run(debug=True)

توضیحات و نکات کلیدی:

این کد ساده‌ترین شکل یک برنامه Flask را نشان می‌دهد. ابتدا، کلاس Flask را از ماژول flask وارد می‌کنیم. سپس، یک نمونه از برنامه Flask با app = Flask(__name__) ایجاد می‌شود. __name__ به Flask کمک می‌کند تا ریشه برنامه را پیدا کرده و منابعی مانند فایل‌های استاتیک و الگوها را به درستی بارگذاری کند.

دکوراتور @app.route('/') تابع hello_world را به URL ریشه (/) متصل می‌کند. هر زمان که کاربر به این آدرس دسترسی پیدا کند، Flask تابع hello_world را فراخوانی کرده و نتیجه آن را به عنوان پاسخ HTTP به مرورگر کاربر بازمی‌گرداند.

بلاک if __name__ == '__main__': تضمین می‌کند که app.run(debug=True) فقط زمانی اجرا می‌شود که اسکریپت app.py مستقیماً اجرا شود. debug=True سرور توسعه Flask را در حالت دیباگ اجرا می‌کند که امکان بارگذاری مجدد خودکار کد در هنگام تغییر و دسترسی به یک دیباگر تعاملی در صورت بروز خطا را فراهم می‌آورد. این ویژگی برای توسعه‌دهندگان Flask بسیار حیاتی است اما تاکید می‌شود که هرگز نباید در محیط‌های Production از آن استفاده شود.

اجرا کردن برنامه:

فایل را به نام app.py ذخیره کنید. سپس در ترمینال، در حالی که محیط مجازی فعال است، دستور زیر را اجرا کنید:

python app.py

شما پیامی مشابه این خواهید دید:

 * Running on http://127.0.0.1:5000 (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: XXX-XXX-XXX

با مراجعه به http://127.0.0.1:5000 در مرورگر خود، “سلام Flask! به اولین برنامه شما خوش آمدید.” را مشاهده خواهید کرد و با مراجعه به http://127.0.0.1:5000/about، پیام صفحه “درباره ما” نمایش داده می‌شود. این پایه و اساس هر برنامه Flask است و درک آن برای شروع کار با Flask ضروری است.

۲. مسیردهی دینامیک (Dynamic Routing) با پارامترها

یکی از قابلیت‌های قدرتمند Flask، توانایی تعریف مسیرهای پویا (dynamic routes) است. این قابلیت به شما اجازه می‌دهد تا بخش‌هایی از URL را به عنوان پارامترهای ورودی به توابع view خود ارسال کنید. این برای ساخت URLهای کاربرپسند (SEO-friendly URLs) و دریافت داده‌ها از آدرس بسیار مفید است.

کد: app.py (ادامه از مثال قبل)

# app.py (اضافه کردن به کد قبلی)
from flask import Flask

app = Flask(__name__)

# ... (مسیرهای قبلی)

# مسیردهی دینامیک با یک پارامتر رشته‌ای (string)
# <name> یک بخش متغیر از URL است که به عنوان آرگومان به تابع welcome_user ارسال می‌شود.
# str: به صورت پیش‌فرض، نوع پارامتر رشته‌ای است و نیازی به مشخص کردن آن نیست، اما برای وضوح می‌توان آن را اضافه کرد.
@app.route('/user/<string:name>')
def welcome_user(name):
    return f'

سلام، {name}! خوش آمدید.

' # مسیردهی دینامیک با یک پارامتر عددی (integer) # int: نوع پارامتر را به عدد صحیح محدود می‌کند. اگر مقدار غیر عددی وارد شود، Flask خطای 404 برمی‌گرداند. @app.route('/post/<int:post_id>') def show_post(post_id): # فرض کنید post_id برای واکشی پستی از پایگاه داده استفاده می‌شود. return f'

نمایش پست با شناسه: {post_id}

' # مسیردهی با استفاده از converter 'path' # 'path' شبیه 'string' است، اما می‌تواند شامل اسلش (/) نیز باشد، که برای URLهای دارای زیرمسیر مفید است. @app.route('/path/<path:subpath>') def show_subpath(subpath): return f'

زیرمسیر درخواستی: {subpath}

' # ... (if __name__ == '__main__': و app.run(debug=True) )

توضیحات و نکات کلیدی:

در Flask، می‌توانید با استفاده از علامت <> در تعریف مسیر، پارامترهای پویا ایجاد کنید. مثلاً، <string:name> یک پارامتر به نام name از نوع رشته‌ای تعریف می‌کند. Flask به طور خودکار مقدار این بخش از URL را استخراج کرده و به عنوان آرگومان به تابع view مربوطه ارسال می‌کند.

Flask چندین converter پیش‌فرض برای انواع داده‌ها دارد:

  • string (پیش‌فرض): هر متنی بدون اسلش.
  • int: اعداد صحیح.
  • float: اعداد اعشاری.
  • path: شبیه string، اما می‌تواند شامل اسلش نیز باشد (مفید برای مسیرهای فایل).
  • uuid: برای شناسه منحصر به فرد جهانی.

استفاده صحیح از این تبدیل‌کننده‌ها نه تنها باعث می‌شود کد شما خواناتر باشد، بلکه به Flask کمک می‌کند تا درخواست‌های نامعتبر را با خطای 404 (Not Found) به درستی مدیریت کند و امنیت برنامه شما را افزایش دهد. این قابلیت Flask به شما امکان می‌دهد تا URLهایی معنی‌دار و سازمان‌یافته ایجاد کنید که برای موتورهای جستجو نیز بهینه‌تر هستند.

مثال کاربرد:

  • http://127.0.0.1:5000/user/علی -> “سلام، علی! خوش آمدید.”
  • http://127.0.0.1:5000/post/123 -> “نمایش پست با شناسه: 123”
  • http://127.0.0.1:5000/path/files/document.pdf -> “زیرمسیر درخواستی: files/document.pdf”

۳. مدیریت متدهای HTTP و ارسال فرم: تعامل با کاربر

برنامه‌های وب معمولاً نیاز به تعامل با کاربر از طریق فرم‌ها دارند. Flask به شما اجازه می‌دهد تا متدهای HTTP مختلف مانند GET و POST را برای یک مسیر خاص مدیریت کنید. متد GET برای درخواست داده‌ها و متد POST برای ارسال داده‌ها به سرور استفاده می‌شود.

کد: app.py و form.html

ابتدا، فایل app.py را به‌روزرسانی می‌کنیم:

# app.py (اضافه کردن به کد قبلی)
from flask import Flask, request, render_template, redirect, url_for

app = Flask(__name__)

# ... (مسیرهای قبلی)

# مسیر برای نمایش فرم و پردازش داده‌های ارسالی
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        # دریافت داده‌ها از فرم (با استفاده از name attribute در HTML)
        username = request.form['username']
        password = request.form['password']

        # منطق ساده احراز هویت (در یک برنامه واقعی، باید از پایگاه داده و هش کردن استفاده کنید)
        if username == 'admin' and password == 'secret':
            # در صورت موفقیت‌آمیز بودن احراز هویت، کاربر را به صفحه پروفایل هدایت کنید.
            # url_for برای ساخت URLها بر اساس نام تابع استفاده می‌شود، که نگهداری کد را آسان‌تر می‌کند.
            return redirect(url_for('profile', username=username))
        else:
            # در صورت ناموفق بودن، پیام خطا را نمایش دهید.
            return render_template('login.html', error='نام کاربری یا رمز عبور اشتباه است.')
    
    # اگر متد درخواست GET باشد (یعنی کاربر برای اولین بار به صفحه مراجعه کرده است)، فرم را نمایش دهید.
    return render_template('login.html')

@app.route('/profile/<string:username>')
def profile(username):
    return f'

به پروفایل {username} خوش آمدید!

' # ... (if __name__ == '__main__': و app.run(debug=True) )

سپس، یک پوشه به نام templates در کنار فایل app.py ایجاد کنید و فایل login.html را در آن قرار دهید:

<!-- templates/login.html -->
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ورود به سیستم</title>
</head>
<body>
    <h1>ورود به سیستم</h1>
    {% if error %}
        <p>{{ error }}</p>
    {% endif %}
    <form method="POST" action="{{ url_for('login') }}">
        <label for="username">نام کاربری:</label><br>
        <input type="text" id="username" name="username" required><br><br>
        
        <label for="password">رمز عبور:</label><br>
        <input type="password" id="password" name="password" required><br><br>
        
        <input type="submit" value="ورود">
    </form>
</body>
</html>

توضیحات و نکات کلیدی:

این مثال نشان می‌دهد که چگونه می‌توان یک فرم ورود ساده را در Flask پیاده‌سازی کرد. مسیر /login هر دو متد GET و POST را پشتیبانی می‌کند که با methods=['GET', 'POST'] مشخص می‌شود.

  • هنگامی که کاربر برای اولین بار به /login مراجعه می‌کند (درخواست GET)، Flask فایل login.html را با render_template('login.html') نمایش می‌دهد.
  • هنگامی که کاربر فرم را پر کرده و دکمه “ورود” را کلیک می‌کند (ارسال درخواست POST)، کد داخل بلاک if request.method == 'POST': اجرا می‌شود.

شیء request در Flask اطلاعات مربوط به درخواست HTTP فعلی را در خود نگه می‌دارد. request.form یک دیکشنری شامل داده‌های ارسال شده از طریق فرم (با متد POST) است. شما می‌توانید با استفاده از request.form['نام_فیلد'] به مقادیر فیلدهای فرم دسترسی پیدا کنید.

تابع redirect(url_for('نام_تابع')) برای هدایت کاربر به یک URL دیگر پس از انجام عملیات (مانند ورود موفق) استفاده می‌شود. استفاده از url_for به جای کدنویسی مستقیم URLها، کد شما را انعطاف‌پذیرتر می‌کند، زیرا اگر مسیرها تغییر کنند، نیازی به به‌روزرسانی دستی URLها در کد نیست.

امنیت فرم‌ها: در یک برنامه واقعی، باید اقدامات امنیتی بیشتری مانند محافظت در برابر حملات CSRF (Cross-Site Request Forgery) را اعمال کنید. Flask-WTF یک اکستنشن محبوب برای Flask است که این قابلیت‌ها و بسیاری دیگر را برای کار با فرم‌ها فراهم می‌کند.

۴. استفاده از Jinja2 برای رندرینگ الگوها: نمایش داده‌ها

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

کد: app.py و index.html

ابتدا، فایل app.py را به‌روزرسانی می‌کنیم:

# app.py (اضافه کردن به کد قبلی)
from flask import Flask, render_template

app = Flask(__name__)

# ... (مسیرهای قبلی)

@app.route('/dashboard')
def dashboard():
    user_name = "مهمان"
    is_admin = False
    items = [
        {'id': 1, 'name': 'لپ‌تاپ', 'price': 1200},
        {'id': 2, 'name': 'موس', 'price': 25},
        {'id': 3, 'name': 'کیبورد', 'price': 75}
    ]
    # ارسال متغیرها به الگو با استفاده از render_template
    return render_template('dashboard.html', user=user_name, admin=is_admin, products=items)

@app.route('/dashboard/admin')
def admin_dashboard():
    user_name = "مدیر"
    is_admin = True
    items = [
        {'id': 1, 'name': 'لپ‌تاپ', 'price': 1200},
        {'id': 2, 'name': 'موس', 'price': 25},
        {'id': 3, 'name': 'کیبورد', 'price': 75},
        {'id': 4, 'name': 'مانیتور', 'price': 300}
    ]
    return render_template('dashboard.html', user=user_name, admin=is_admin, products=items, welcome_message="به داشبورد ادمین خوش آمدید!")

# ... (if __name__ == '__main__': و app.run(debug=True) )

سپس، فایل dashboard.html را در پوشه templates ایجاد کنید:

<!-- templates/dashboard.html -->
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>داشبورد کاربر</title>
</head>
<body>
    <div>
        <h1>خوش آمدید، {{ user }}!</h1>

        {% if welcome_message %}
            <p>{{ welcome_message }}</p>
        {% endif %}

        {% if admin %}
            <div>
                <h2>پنل مدیریت</h2>
                <p>شما به عنوان مدیر وارد شده‌اید. به امکانات اضافی دسترسی دارید.</p>
            </div>
        {% else %}
            <p>این داشبورد شماست.</p>
        {% endif %}

        <h2>لیست محصولات</h2>
        {% if products %}
            <div>
                {% for product in products %}
                    <div>
                        <h3>{{ product.name }}</h3>
                        <p>شناسه: {{ product.id }}</p>
                        <p>قیمت: {{ product.price }} دلار</p>
                    </div>
                {% endfor %}
            </div>
        {% else %}
            <p>هیچ محصولی برای نمایش وجود ندارد.</p>
        {% endif %}

        <hr>
        <p>&copy; 2023 Flask Application</p>
    </div>
</body>
</html>

توضیحات و نکات کلیدی:

تابع render_template('نام_فایل.html', متغیر=مقدار) مسئول بارگذاری الگو از پوشه templates و ارسال متغیرها به آن است. Flask به طور خودکار به دنبال پوشه templates در کنار فایل app.py می‌گردد.

در Jinja2، از سه نوع سینتکس اصلی استفاده می‌شود:

  • {{ expression }}: برای نمایش مقادیر متغیرها (مقادیر فرستاده شده از Flask).
  • {% statement %}: برای کنترل منطق برنامه مانند حلقه‌ها (for) و شرط‌ها (if/else).
  • {# comment #}: برای نوشتن نظرات که در خروجی HTML نمایش داده نمی‌شوند.

در فایل dashboard.html:

  • {{ user }} مقدار متغیر user_name را نمایش می‌دهد.
  • بلاک {% if admin %} ... {% else %} ... {% endif %} محتوای شرطی را بر اساس مقدار متغیر admin نمایش می‌دهد.
  • حلقه {% for product in products %} ... {% endfor %} بر روی لیست products تکرار شده و جزئیات هر محصول را نمایش می‌دهد.

وراثت الگو (Template Inheritance): Jinja2 از مفهوم وراثت الگو پشتیبانی می‌کند که امکان تعریف یک الگو پایه (base template) با ساختار مشترک (مانند هدر، فوتر و نوار کناری) و سپس گسترش آن در الگوهای فرزند (child templates) را فراهم می‌کند. این کار با استفاده از تگ‌های {% extends %} و {% block %} انجام می‌شود و به کاهش تکرار کد و حفظ یکپارچگی طراحی کمک شایانی می‌کند. به عنوان مثال، می‌توانید یک base.html ایجاد کنید و سایر الگوها آن را گسترش دهند تا از بخش‌های مشترک استفاده کنند.

استفاده از Jinja2 قدرت زیادی برای ایجاد رابط‌های کاربری پویا و تعاملی به Flask می‌دهد و با توجه به قابلیت‌های SEO-friendly بودن، به بهینه‌سازی وب‌سایت شما در موتورهای جستجو کمک می‌کند.

۵. مدیریت نشست‌ها (Sessions) و پیام‌های Flash: حفظ وضعیت کاربر

در توسعه وب، HTTP یک پروتکل بدون وضعیت (stateless) است، به این معنی که سرور اطلاعاتی درباره درخواست‌های قبلی کاربر نگه نمی‌دارد. برای حفظ وضعیت کاربر بین درخواست‌ها، Flask از نشست‌ها (sessions) استفاده می‌کند. نشست‌ها به شما امکان می‌دهند تا داده‌های خاص کاربر را در یک دوره زمانی مشخص ذخیره کنید. همچنین، Flask ابزاری به نام “flash messages” را برای نمایش پیام‌های یک‌بار مصرف به کاربر فراهم می‌کند، که اغلب پس از عملیات مانند ثبت‌نام موفق یا خطاهای اعتبارسنجی استفاده می‌شوند.

کد: app.py و base.html

ابتدا، فایل app.py را به‌روزرسانی می‌کنیم. توجه داشته باشید که برای استفاده از sessions، باید یک SECRET_KEY تنظیم کنید که برای امضای کوکی‌های session استفاده می‌شود.

# app.py (اضافه کردن به کد قبلی)
from flask import Flask, request, render_template, redirect, url_for, session, flash

app = Flask(__name__)
# SECRET_KEY برای امن کردن کوکی‌های سشن استفاده می‌شود.
# باید یک رشته طولانی و پیچیده باشد و هرگز آن را در کد منبع عمومی قرار ندهید.
app.secret_key = 'super_secret_key_that_no_one_can_guess_12345' # در محیط واقعی از os.urandom استفاده کنید

# ... (مسیرهای قبلی)

@app.route('/set_session')
def set_session_data():
    session['username'] = 'testuser'
    session['logged_in'] = True
    flash('اطلاعات نشست شما با موفقیت تنظیم شد!', 'success') # 'success' دسته پیام است
    return redirect(url_for('show_session_data'))

@app.route('/get_session')
def show_session_data():
    username = session.get('username', 'مهمان') # .get برای جلوگیری از خطا در صورت عدم وجود کلید
    logged_in = session.get('logged_in', False)
    # get_flashed_messages() پیام‌های flash را بازیابی و از سشن پاک می‌کند.
    return render_template('session_display.html', username=username, logged_in=logged_in)

@app.route('/logout_session')
def logout_session():
    # حذف همه کلیدها از سشن
    session.pop('username', None)
    session.pop('logged_in', None)
    flash('شما با موفقیت از سیستم خارج شدید.', 'info')
    return redirect(url_for('show_session_data'))

# ... (if __name__ == '__main__': و app.run(debug=True) )

سپس، فایل session_display.html را در پوشه templates ایجاد کنید:

<!-- templates/session_display.html -->
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>نمایش اطلاعات نشست</title>
</head>
<body>
    <div>
        <h1>اطلاعات نشست و پیام‌های Flash</h1>

        {% with messages = get_flashed_messages(with_categories=true) %}
            {% if messages %}
                <ul>
                {% for category, message in messages %}
                    <li>{{ message }}</li>
                {% endfor %}
                </ul>
            {% endif %}
        {% endwith %}

        <p>نام کاربری در نشست: <strong>{{ username }}</strong></p>
        <p>وضعیت ورود: <strong>{{ 'ورود کرده' if logged_in else 'ورود نکرده' }}</strong></p>

        <p>
            <a href="{{ url_for('set_session_data') }}">تنظیم اطلاعات نشست</a>
            <a href="{{ url_for('logout_session') }}">خروج از سیستم (پاک کردن نشست)</a>
        </p>
    </div>
</body>
</html>

توضیحات و نکات کلیدی:

Sessions:

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

  • app.secret_key: بسیار مهم است که این کلید را در محیط Production به یک رشته تصادفی، طولانی و پیچیده تغییر دهید و آن را به صورت امن (مثلاً از طریق متغیرهای محیطی) مدیریت کنید. افشای این کلید می‌تواند منجر به حملات امنیتی جدی شود.
  • session['key'] = value: برای ذخیره داده در نشست استفاده می‌شود.
  • session.get('key', default_value): برای بازیابی داده از نشست. استفاده از .get() با یک مقدار پیش‌فرض امن‌تر است زیرا اگر کلید وجود نداشته باشد، خطا نمی‌دهد.
  • session.pop('key', None): برای حذف یک کلید خاص از نشست استفاده می‌شود.

Flash Messages:

پیام‌های فلش برای نمایش پیام‌های یک‌بار مصرف به کاربر طراحی شده‌اند. هنگامی که یک پیام فلش با flash('پیام شما', 'دسته') ایجاد می‌شود، آن پیام در نشست ذخیره می‌شود و در درخواست بعدی (یا هر درخواستی تا زمانی که خوانده شود) در دسترس خواهد بود. پس از خوانده شدن توسط get_flashed_messages()، پیام‌ها از نشست حذف می‌شوند.

  • flash('پیام', 'دسته'): پیام را در نشست ذخیره می‌کند. ‘دسته’ (category) اختیاری است و می‌توان از آن برای استایل‌دهی (مثلاً ‘success’, ‘error’, ‘info’) استفاده کرد.
  • get_flashed_messages(with_categories=true): در الگوی Jinja2، این تابع پیام‌های ذخیره شده را بازیابی می‌کند. with_categories=true به شما امکان می‌دهد تا دسته هر پیام را نیز دریافت کنید که برای استایل‌دهی CSS بسیار مفید است.

مدیریت نشست‌ها و پیام‌های Flash اجزای حیاتی برای ایجاد یک تجربه کاربری روان و اطلاع‌رسانی در برنامه‌های وب هستند و درک عمیق آن‌ها به شما کمک می‌کند تا برنامه‌های Flask قدرتمندتر و تعاملی‌تری بسازید.

۶. ساخت API RESTful با Flask (پاسخ JSON): ارائه داده‌ها

Flask یک انتخاب عالی برای ساخت APIهای RESTful است که داده‌ها را در قالب JSON به کلاینت‌ها (مانند برنامه‌های موبایل، برنامه‌های تک‌صفحه‌ای یا سایر سرویس‌ها) ارائه می‌دهند. سادگی Flask در مدیریت درخواست‌ها و تولید پاسخ‌های JSON آن را به ابزاری قدرتمند در این زمینه تبدیل کرده است.

کد: app.py

# app.py (اضافه کردن به کد قبلی)
from flask import Flask, request, jsonify

app = Flask(__name__)

# ... (مسیرهای قبلی)

# لیست فرضی از محصولات
products = [
    {'id': 1, 'name': 'Laptop', 'price': 1200, 'description': 'Powerful notebook for professionals'},
    {'id': 2, 'name': 'Mouse', 'price': 25, 'description': 'Ergonomic wireless mouse'},
    {'id': 3, 'name': 'Keyboard', 'price': 75, 'description': 'Mechanical keyboard with RGB lighting'}
]

# API برای دریافت همه محصولات
@app.route('/api/products', methods=['GET'])
def get_products():
    # jsonify یک دیکشنری یا لیست را به پاسخ JSON تبدیل می‌کند و هدر Content-Type را به application/json تنظیم می‌کند.
    return jsonify(products)

# API برای دریافت یک محصول خاص بر اساس شناسه
@app.route('/api/products/<int:product_id>', methods=['GET'])
def get_product(product_id):
    product = next((p for p in products if p['id'] == product_id), None)
    if product:
        return jsonify(product)
    # بازگرداندن پاسخ خطا با کد وضعیت HTTP 404 (Not Found)
    return jsonify({'message': 'Product not found'}), 404

# API برای افزودن یک محصول جدید
@app.route('/api/products', methods=['POST'])
def add_product():
    if not request.is_json:
        return jsonify({'message': 'Request must be JSON'}), 400
    
    new_product_data = request.get_json()
    if 'name' not in new_product_data or 'price' not in new_product_data:
        return jsonify({'message': 'Missing name or price in request'}), 400
    
    # تولید شناسه جدید
    new_id = max([p['id'] for p in products]) + 1 if products else 1
    new_product = {
        'id': new_id,
        'name': new_product_data['name'],
        'price': new_product_data['price'],
        'description': new_product_data.get('description', '')
    }
    products.append(new_product)
    # بازگرداندن محصول جدید با کد وضعیت HTTP 201 (Created)
    return jsonify(new_product), 201

# API برای به‌روزرسانی یک محصول
@app.route('/api/products/<int:product_id>', methods=['PUT'])
def update_product(product_id):
    if not request.is_json:
        return jsonify({'message': 'Request must be JSON'}), 400
    
    update_data = request.get_json()
    product = next((p for p in products if p['id'] == product_id), None)
    
    if product:
        product.update(update_data)
        return jsonify(product)
    return jsonify({'message': 'Product not found'}), 404

# API برای حذف یک محصول
@app.route('/api/products/<int:product_id>', methods=['DELETE'])
def delete_product(product_id):
    global products # برای تغییر لیست سراسری
    initial_len = len(products)
    products = [p for p in products if p['id'] != product_id]
    
    if len(products) < initial_len:
        return jsonify({'message': 'Product deleted'}), 200
    return jsonify({'message': 'Product not found'}), 404

# ... (if __name__ == '__main__': و app.run(debug=True) )

توضیحات و نکات کلیدی:

این مثال یک API CRUD (Create, Read, Update, Delete) ساده برای مدیریت محصولات را نشان می‌دهد. Flask با استفاده از jsonify() کار تبدیل دیکشنری‌ها و لیست‌های پایتون به پاسخ‌های JSON را بسیار آسان می‌کند. jsonify() همچنین به طور خودکار هدر Content-Type پاسخ را به application/json تنظیم می‌کند.

  • request.is_json: برای بررسی اینکه آیا هدر Content-Type درخواست application/json است یا خیر، استفاده می‌شود.
  • request.get_json(): برای پارس کردن داده‌های JSON از بدنه درخواست به یک دیکشنری پایتون استفاده می‌شود.
  • کدهای وضعیت HTTP: در APIها، بازگرداندن کدهای وضعیت HTTP مناسب (مانند 200 OK، 201 Created، 400 Bad Request، 404 Not Found، 500 Internal Server Error) برای اطلاع‌رسانی به کلاینت در مورد نتیجه درخواست، از اهمیت بالایی برخوردار است. می‌توانید با اضافه کردن عدد کد وضعیت به عنوان آرگومان دوم jsonify()، این کدها را تنظیم کنید.
  • مدیریت خطا: برای سناریوهایی که منبع مورد نظر یافت نمی‌شود یا درخواست نامعتبر است، بازگرداندن پیام‌های خطای واضح به همراه کد وضعیت مناسب، ضروری است.

برای تست این API، می‌توانید از ابزارهایی مانند Postman، Insomnia یا دستور curl در ترمینال استفاده کنید.

مثال‌ها:

  • GET /api/products: لیست همه محصولات را برمی‌گرداند.
  • GET /api/products/1: محصول با شناسه ۱ را برمی‌گرداند.
  • POST /api/products: با بدنه JSON مانند {"name": "Tablet", "price": 400} یک محصول جدید اضافه می‌کند.
  • PUT /api/products/1: با بدنه JSON مانند {"price": 1250} محصول با شناسه ۱ را به‌روزرسانی می‌کند.
  • DELETE /api/products/2: محصول با شناسه ۲ را حذف می‌کند.

ساخت APIهای RESTful با Flask پایه و اساس بسیاری از برنامه‌های مدرن وب و موبایل است و این مثال نمایانگر قدرت Flask در این حوزه است.

۷. آپلود فایل (File Uploads): مدیریت رسانه‌ها

بسیاری از برنامه‌های وب نیاز به قابلیت آپلود فایل (مانند تصاویر پروفایل، اسناد و غیره) توسط کاربران دارند. Flask ابزارهای لازم برای مدیریت آپلود فایل‌ها را به سادگی فراهم می‌کند. با استفاده از شیء request.files می‌توانید به فایل‌های آپلود شده دسترسی پیدا کرده و آن‌ها را ذخیره کنید.

کد: app.py و upload.html

ابتدا، فایل app.py را به‌روزرسانی می‌کنیم. نیاز به وارد کردن os برای کار با مسیرهای فایل و secure_filename از werkzeug.utils برای ایمن‌سازی نام فایل‌ها داریم.

# app.py (اضافه کردن به کد قبلی)
import os
from flask import Flask, request, render_template, flash, redirect, url_for
from werkzeug.utils import secure_filename

app = Flask(__name__)
app.secret_key = 'another_super_secret_key_for_uploads_456' # برای فلش مسیج
# مسیر پوشه‌ای که فایل‌های آپلود شده در آن ذخیره می‌شوند
UPLOAD_FOLDER = 'uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
# فرمت‌های مجاز برای آپلود
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}

# ایجاد پوشه آپلود اگر وجود ندارد
if not os.path.exists(UPLOAD_FOLDER):
    os.makedirs(UPLOAD_FOLDER)

# تابع کمکی برای بررسی پسوند فایل
def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        # بررسی وجود فایل در درخواست
        if 'file' not in request.files:
            flash('هیچ فایلی انتخاب نشده است.', 'error')
            return redirect(request.url)
        file = request.files['file']
        # اگر کاربر فایلی را انتخاب نکرده باشد، مرورگر یک فایل خالی بدون نام ارسال می‌کند.
        if file.filename == '':
            flash('فایلی انتخاب نشده است.', 'error')
            return redirect(request.url)
        
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename) # ایمن‌سازی نام فایل
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            flash(f'فایل "{filename}" با موفقیت آپلود شد!', 'success')
            return redirect(url_for('upload_file'))
        else:
            flash('نوع فایل مجاز نیست.', 'error')
            return redirect(request.url)
    
    return render_template('upload.html')

# ... (if __name__ == '__main__': و app.run(debug=True) )

سپس، فایل upload.html را در پوشه templates ایجاد کنید:

<!-- templates/upload.html -->
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>آپلود فایل</title>
</head>
<body>
    <div>
        <h1>آپلود فایل</h1>

        {% with messages = get_flashed_messages(with_categories=true) %}
            {% if messages %}
                <ul>
                {% for category, message in messages %}
                    <li>{{ message }}</li>
                {% endfor %}
                </ul>
            {% endif %}
        {% endwith %}

        <form method="POST" enctype="multipart/form-data" action="{{ url_for('upload_file') }}">
            <input type="file" name="file">
            <input type="submit" value="آپلود">
        </form>
    </div>
</body>
</html>

توضیحات و نکات کلیدی:

برای مدیریت آپلود فایل در Flask، مراحل زیر را دنبال می‌کنیم:

  1. تنظیمات: ابتدا یک پوشه برای ذخیره فایل‌های آپلود شده (مثلاً uploads) تعریف می‌کنیم و آن را در app.config ثبت می‌کنیم. همچنین، لیستی از پسوندهای فایل مجاز را برای جلوگیری از آپلود فایل‌های مخرب یا ناخواسته تعیین می‌کنیم.
  2. فرم HTML: در سمت کلاینت، یک فرم HTML با method="POST" و enctype="multipart/form-data" برای آپلود فایل نیاز داریم. این enctype برای ارسال داده‌های باینری فایل ضروری است. فیلد ورودی فایل باید type="file" داشته باشد و یک name مشخص (مثلاً file) برای دسترسی به آن در Flask داشته باشد.
  3. پردازش در Flask:
    • در تابع view مربوطه، بررسی می‌کنیم که متد درخواست POST باشد.
    • request.files: یک دیکشنری شامل فایل‌های آپلود شده است. هر ورودی در این دیکشنری، یک شیء FileStorage (از Werkzeug) است. کلید این دیکشنری، همان name فیلد ورودی در فرم HTML است (مثلاً request.files['file']).
    • اعتبارسنجی: ضروری است که فایل آپلود شده را اعتبارسنجی کنید. این شامل بررسی وجود فایل ('file' not in request.files)، خالی نبودن نام فایل (file.filename == '') و بررسی پسوند فایل (با استفاده از تابع allowed_file) می‌شود.
    • ایمن‌سازی نام فایل: قبل از ذخیره فایل، حتماً از secure_filename(file.filename) استفاده کنید. این تابع نام فایل را از کاراکترهای مخرب و مسیرهای نسبی پاک‌سازی می‌کند تا از حملات امنیتی مانند Directory Traversal جلوگیری شود.
    • ذخیره فایل: متد file.save(مسیر_کامل_فایل) فایل را در مسیر مشخص شده ذخیره می‌کند.

ملاحظات امنیتی مهم:

  • اعتبارسنجی شدید: همیشه نوع فایل، اندازه و محتوای آن را در سمت سرور اعتبارسنجی کنید.
  • نام‌گذاری امن: همیشه از secure_filename استفاده کنید.
  • مکان ذخیره‌سازی: فایل‌های آپلود شده را در پوشه‌ای خارج از ریشه وب (در صورت امکان) یا در پوشه‌ای که به عنوان فایل‌های استاتیک سرو نمی‌شود، ذخیره کنید. این کار از اجرای ناخواسته کدهای مخرب آپلود شده جلوگیری می‌کند.
  • اندازه فایل: می‌توانید حداکثر اندازه فایل آپلود را با app.config['MAX_CONTENT_LENGTH'] محدود کنید (به بایت).

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

۸. مدیریت خطاها و صفحات سفارشی خطا: تجربه کاربری بهتر

هنگامی که خطایی در برنامه وب شما رخ می‌دهد، Flask به طور پیش‌فرض یک صفحه خطای عمومی را نمایش می‌دهد. با این حال، برای بهبود تجربه کاربری و حفظ زیبایی بصری برنامه، می‌توانید صفحات خطای سفارشی (مانند 404 Not Found یا 500 Internal Server Error) ایجاد کنید. Flask ابزارهایی را برای ثبت توابع handler خطا فراهم می‌کند.

کد: app.py و 404.html، 500.html

ابتدا، فایل app.py را به‌روزرسانی می‌کنیم:

# app.py (اضافه کردن به کد قبلی)
from flask import Flask, render_template, abort

app = Flask(__name__)

# ... (مسیرهای قبلی)

# مسیر برای شبیه‌سازی خطای 404
@app.route('/force-404')
def force_404():
    # تابع abort() یک خطای HTTP را ایجاد می‌کند.
    # Flask به دنبال handler ثبت شده برای این خطا می‌گردد.
    abort(404)

# مسیر برای شبیه‌سازی خطای 500 (خطای سرور داخلی)
@app.route('/force-500')
def force_500():
    # شبیه‌سازی خطای تقسیم بر صفر
    result = 1 / 0
    return f'Result: {result}'

# ثبت یک error handler برای خطای 404 (Not Found)
@app.errorhandler(404)
def page_not_found(e):
    # e شامل اطلاعات مربوط به خطا است، اما می‌توانیم آن را نادیده بگیریم.
    return render_template('404.html'), 404

# ثبت یک error handler برای خطای 500 (Internal Server Error)
@app.errorhandler(500)
def internal_server_error(e):
    # در محیط Production، می‌توانید جزئیات خطا را لاگ کنید.
    return render_template('500.html'), 500

# ... (if __name__ == '__main__': و app.run(debug=True) )

سپس، فایل 404.html را در پوشه templates ایجاد کنید:

<!-- templates/404.html -->
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>404 - صفحه یافت نشد</title>
</head>
<body>
    <div>
        <h1>404</h1>
        <p>متاسفیم، صفحه مورد نظر شما یافت نشد.</p>
        <p><a href="{{ url_for('hello_world') }}">بازگشت به صفحه اصلی</a></p>
    </div>
</body>
</html>

و فایل 500.html را در پوشه templates ایجاد کنید:

<!-- templates/500.html -->
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>500 - خطای سرور داخلی</title>
</head>
<body>
    <div>
        <h1>500</h1>
        <p>متاسفیم، خطای داخلی در سرور رخ داده است.</p>
        <p>لطفا بعداً دوباره امتحان کنید یا با پشتیبانی تماس بگیرید.</p>
        <p><a href="{{ url_for('hello_world') }}">بازگشت به صفحه اصلی</a></p>
    </div>
</body>
</html>

توضیحات و نکات کلیدی:

@app.errorhandler(کد_خطا):

این دکوراتور برای ثبت توابع به عنوان handler برای کدهای خطای HTTP خاص (مانند 404، 500، 403 و غیره) استفاده می‌شود. زمانی که Flask با خطایی از نوع مشخص شده مواجه می‌شود، به جای نمایش صفحه خطای پیش‌فرض، تابع handler ثبت شده را فراخوانی می‌کند.

  • تابع handler خطای شما باید یک آرگومان بپذیرد که شیء استثنای (exception) مربوط به خطا است.
  • این تابع باید یک پاسخ HTTP (مانند رشته، الگو رندر شده) و کد وضعیت HTTP مربوطه را بازگرداند. فراموش نکنید که کد وضعیت HTTP را به صورت صریح برگردانید (مثلاً return render_template('404.html'), 404).

abort(کد_خطا):

تابع abort() از Flask برای ایجاد یک خطای HTTP مشخص به صورت عمدی استفاده می‌شود. این تابع یک استثنا با کد وضعیت HTTP مشخص ایجاد می‌کند که سپس توسط handler خطای مربوطه (اگر ثبت شده باشد) مدیریت می‌شود.

چرا صفحات خطای سفارشی مهم هستند؟

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

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

۹. پیمانه‌بندی برنامه با Blueprints: ساختاردهی پروژه‌های بزرگ

همانطور که برنامه‌های Flask رشد می‌کنند و پیچیده‌تر می‌شوند، نگهداری همه مسیرها، ویوها و فایل‌های استاتیک در یک فایل app.py به سرعت دشوار می‌شود. Blueprints (نقشه‌های اولیه) مکانیزمی در Flask برای سازماندهی برنامه به اجزای کوچک‌تر و قابل استفاده مجدد فراهم می‌کنند. Blueprints به شما اجازه می‌دهند تا بخش‌های مختلف برنامه را به صورت جداگانه تعریف کرده و سپس آن‌ها را در برنامه اصلی (main app) ثبت کنید.

کد: ساختار فایل و app.py، auth.py، dashboard.py

فرض کنید این ساختار فایل را داریم:

myproject/
├── venv/
├── app.py
├── config.py
├── blueprints/
│   ├── __init__.py
│   ├── auth.py
│   ├── dashboard.py
├── templates/
│   ├── base.html
│   ├── auth/
│   │   └── login.html
│   ├── dashboard/
│   │   └── index.html
└── static/
    └── css/
        └── style.css

ابتدا، blueprints/__init__.py (می‌تواند خالی باشد یا شامل تنظیمات مشترک باشد).
سپس، blueprints/auth.py را ایجاد می‌کنیم:

# blueprints/auth.py
from flask import Blueprint, render_template, request, flash, redirect, url_for, session

# ایجاد یک Blueprint به نام 'auth'
auth_bp = Blueprint('auth', __name__, url_prefix='/auth')

@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')

        if username == 'admin' and password == 'password': # ساده‌شده برای مثال
            session['username'] = username
            flash('با موفقیت وارد شدید.', 'success')
            return redirect(url_for('dashboard_bp.index')) # ارجاع به Blueprint دیگر
        else:
            flash('نام کاربری یا رمز عبور اشتباه است.', 'error')
    
    return render_template('auth/login.html')

@auth_bp.route('/logout')
def logout():
    session.pop('username', None)
    flash('شما از سیستم خارج شدید.', 'info')
    return redirect(url_for('auth_bp.login'))

بعد، blueprints/dashboard.py را ایجاد می‌کنیم:

# blueprints/dashboard.py
from flask import Blueprint, render_template, session, redirect, url_for, flash

# ایجاد یک Blueprint به نام 'dashboard_bp'
dashboard_bp = Blueprint('dashboard_bp', __name__, url_prefix='/dashboard')

@dashboard_bp.route('/')
def index():
    if 'username' not in session:
        flash('برای دسترسی به داشبورد ابتدا وارد شوید.', 'warning')
        return redirect(url_for('auth_bp.login')) # ارجاع به Blueprint دیگر
    
    return render_template('dashboard/index.html', username=session['username'])

@dashboard_bp.route('/profile')
def profile():
    if 'username' not in session:
        flash('برای دسترسی به پروفایل ابتدا وارد شوید.', 'warning')
        return redirect(url_for('auth_bp.login'))
    
    return render_template('dashboard/profile.html', username=session['username'])

و در نهایت، app.py را برای ثبت Blueprints به‌روزرسانی می‌کنیم:

# app.py
from flask import Flask, render_template
from blueprints.auth import auth_bp
from blueprints.dashboard import dashboard_bp

def create_app():
    app = Flask(__name__)
    app.config.from_pyfile('config.py') # بارگذاری تنظیمات از فایل config.py
    
    # ثبت Blueprints
    app.register_blueprint(auth_bp)
    app.register_blueprint(dashboard_bp)

    @app.route('/')
    def index():
        return render_template('base.html')
    
    return app

if __name__ == '__main__':
    app = create_app()
    app.run(debug=True)

فایل config.py (در ریشه پروژه) را ایجاد کنید:

# config.py
SECRET_KEY = 'your_super_secret_key_for_production' # باید یک مقدار تصادفی و قوی باشد

و فایل‌های قالب (base.html، auth/login.html، dashboard/index.html) را با محتوای مناسب ایجاد کنید. مثلاً base.html می‌تواند شامل لینک‌هایی به مسیرهای Blueprint باشد.

templates/auth/login.html و templates/dashboard/index.html مشابه مثال‌های قبلی خواهند بود، اما با ارجاعات url_for که به صورت 'blueprint_name.route_function_name' خواهند بود.

توضیحات و نکات کلیدی:

ایجاد Blueprint:

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

my_blueprint = Blueprint('my_blueprint_name', __name__, url_prefix='/myprefix')
  • 'my_blueprint_name': نام Blueprint است و برای ارجاع به آن در توابع url_for() استفاده می‌شود (مثلاً url_for('my_blueprint_name.my_route_function')).
  • __name__: مشابه با Flask(__name__)، به Blueprint کمک می‌کند تا منابع (مانند الگوها و فایل‌های استاتیک) را در ماژول خود پیدا کند.
  • url_prefix='/myprefix': یک پیشوند URL برای تمام مسیرهای تعریف شده در این Blueprint اضافه می‌کند. به عنوان مثال، @auth_bp.route('/login') به /auth/login تبدیل می‌شود.

ثبت Blueprint:

پس از ایجاد Blueprintها، باید آن‌ها را در نمونه اصلی برنامه Flask خود با app.register_blueprint(my_blueprint) ثبت کنید. این کار معمولاً در یک تابع سازنده برنامه (مانند create_app()) انجام می‌شود که یک الگوی رایج برای برنامه‌های بزرگتر Flask است.

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

  • ماژولار بودن: برنامه را به اجزای مستقل تقسیم می‌کند.
  • قابلیت استفاده مجدد: Blueprints می‌توانند در چندین برنامه Flask استفاده شوند.
  • سازماندهی بهتر: هر Blueprint می‌تواند پوشه‌های templates و static خاص خود را داشته باشد، که به جلوگیری از تداخل نام‌ها کمک می‌کند.
  • توسعه‌پذیری: اضافه کردن قابلیت‌های جدید آسان‌تر می‌شود.

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

۱۰. احراز هویت پایه با Flask (مثال Login/Logout): امنیت کاربر

احراز هویت (Authentication) فرآیند تأیید هویت یک کاربر است. در برنامه‌های وب، این معمولاً شامل ورود کاربران با نام کاربری و رمز عبور است. Flask خود سیستم احراز هویت داخلی ندارد، اما به دلیل انعطاف‌پذیری‌اش، می‌توان آن را با استفاده از قابلیت‌هایی مانند Sessions و اکستنشن‌های شخص ثالث پیاده‌سازی کرد. در این مثال، یک سیستم احراز هویت بسیار ساده را با استفاده از sessions Flask نشان می‌دهیم، اما تاکید می‌کنیم که برای برنامه‌های Production باید از روش‌های امن‌تر و اکستنشن‌های تخصصی استفاده شود.

کد: app.py و login.html، profile.html

فایل app.py (با فرض استفاده از app.secret_key از مثال Sessions):

# app.py (اضافه کردن به کد قبلی و ترکیب با مفاهیم سشن و تمپلیت)
from flask import Flask, render_template, request, redirect, url_for, session, flash

app = Flask(__name__)
app.secret_key = 'a_very_secret_key_for_authentication_demo_789' # کلید امنیتی برای سشن‌ها

# کاربران آزمایشی (در برنامه واقعی از پایگاه داده و هش کردن رمز عبور استفاده کنید)
users = {
    'john': 'pass123',
    'admin': 'adminpass'
}

# دکوراتور برای محافظت از مسیرها (فقط کاربران لاگین شده)
def login_required(f):
    from functools import wraps
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if 'logged_in' not in session or not session['logged_in']:
            flash('برای دسترسی به این صفحه ابتدا وارد شوید.', 'warning')
            return redirect(url_for('login_auth'))
        return f(*args, **kwargs)
    return decorated_function

@app.route('/login_auth', methods=['GET', 'POST'])
def login_auth():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')

        if username in users and users[username] == password:
            session['logged_in'] = True
            session['username'] = username
            flash(f'به عنوان {username} با موفقیت وارد شدید!', 'success')
            return redirect(url_for('profile_page'))
        else:
            flash('نام کاربری یا رمز عبور اشتباه است.', 'error')
    
    return render_template('auth_login.html')

@app.route('/logout_auth')
@login_required # این مسیر نیز می‌تواند محافظت شود تا فقط کاربران وارد شده بتوانند خارج شوند
def logout_auth():
    session.pop('logged_in', None)
    session.pop('username', None)
    flash('شما با موفقیت از سیستم خارج شدید.', 'info')
    return redirect(url_for('login_auth'))

@app.route('/profile_page')
@login_required # این صفحه نیاز به احراز هویت دارد
def profile_page():
    return render_template('auth_profile.html', username=session['username'])

@app.route('/secret_data')
@login_required # این مسیر نیز محافظت شده است
def secret_data():
    return f"

داده‌های محرمانه برای {session['username']}

فقط کاربران وارد شده می‌توانند این را ببینند!

" # ... (سایر مسیرها و if __name__ == '__main__': و app.run(debug=True) )

فایل templates/auth_login.html (مشابه login.html قبلی با تفاوت در action و flash message display):

<!-- templates/auth_login.html -->
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ورود به سیستم</title>
</head>
<body>
    <div>
        <h1>ورود به سیستم</h1>

        {% with messages = get_flashed_messages(with_categories=true) %}
            {% if messages %}
                <ul>
                {% for category, message in messages %}
                    <li>{{ message }}</li>
                {% endfor %}
                </ul>
            {% endif %}
        {% endwith %}

        <form method="POST" action="{{ url_for('login_auth') }}">
            <label for="username">نام کاربری:</label>
            <input type="text" id="username" name="username" required>
            
            <label for="password">رمز عبور:</label>
            <input type="password" id="password" name="password" required>
            
            <input type="submit" value="ورود">
        </form>
        <a href="{{ url_for('profile_page') }}">برو به پروفایل (نیاز به ورود)</a>
        <a href="{{ url_for('secret_data') }}">برو به داده‌های محرمانه (نیاز به ورود)</a>
    </div>
</body>
</html>

فایل templates/auth_profile.html:

<!-- templates/auth_profile.html -->
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>پروفایل کاربر</title>
</head>
<body>
    <div>
        <h1>پروفایل کاربر {{ username }}</h1>
        <p>به صفحه پروفایل خود خوش آمدید. شما با موفقیت وارد سیستم شده‌اید.</p>
        <div>
            <a href="{{ url_for('secret_data') }}">مشاهده داده‌های محرمانه</a>
            <a href="{{ url_for('logout_auth') }}">خروج از سیستم</a>
        </div>
    </div>
</body>
</html>

توضیحات و نکات کلیدی:

این مثال یک دمو از یک سیستم احراز هویت ساده را ارائه می‌دهد. مراحل کلیدی عبارتند از:

  1. دکوراتور login_required: این یک تابع دکوراتور سفارشی است که می‌توان آن را به هر مسیر (route) اضافه کرد تا اطمینان حاصل شود که فقط کاربران وارد شده می‌توانند به آن دسترسی داشته باشند. اگر کاربر وارد نشده باشد، به صفحه ورود هدایت می‌شود و یک پیام فلش نمایش داده می‌شود. از functools.wraps برای حفظ متادیتای تابع اصلی استفاده می‌کنیم.
  2. صفحه ورود (/login_auth):
    • درخواست GET: فرم ورود را نمایش می‌دهد.
    • درخواست POST: نام کاربری و رمز عبور را از request.form دریافت می‌کند.
    • اعتبارسنجی: در این مثال، ما فقط اعتبار را در یک دیکشنری ساده users بررسی می‌کنیم. در یک برنامه واقعی، شما باید رمز عبور را هش کنید (با استفاده از کتابخانه‌هایی مانند Werkzeug.security یا passlib) و آن را در پایگاه داده ذخیره کنید و هنگام ورود، رمز عبور ارسالی را با هش ذخیره شده مقایسه کنید.
    • مدیریت نشست: پس از ورود موفق، session['logged_in'] = True و session['username'] = username را تنظیم می‌کنیم. این اطلاعات در جلسات بعدی کاربر مورد استفاده قرار می‌گیرد.
    • پیام‌های Flash: برای اطلاع‌رسانی به کاربر در مورد وضعیت ورود (موفقیت‌آمیز، خطا، یا نیاز به ورود) از پیام‌های فلش استفاده می‌شود.
  3. صفحه پروفایل (/profile_page) و داده‌های محرمانه (/secret_data): این مسیرها توسط دکوراتور @login_required محافظت می‌شوند. فقط پس از ورود موفق، کاربر می‌تواند به آن‌ها دسترسی پیدا کند.
  4. خروج از سیستم (/logout_auth): این مسیر متغیرهای 'logged_in' و 'username' را از نشست کاربر حذف می‌کند و او را به صفحه ورود هدایت می‌کند.

ملاحظات امنیتی حیاتی برای Production:

  • هش کردن رمز عبور: هرگز رمز عبورها را به صورت متن ساده ذخیره نکنید. همیشه از توابع هشینگ یک‌طرفه (مانند bcrypt یا PBKDF2) استفاده کنید.
  • Flask-Login: برای مدیریت احراز هویت در Flask، اکستنشن Flask-Login به شدت توصیه می‌شود. این اکستنشن بسیاری از جنبه‌های پیچیده احراز هویت (مانند مدیریت کاربران، یادآوری ورود و غیره) را به صورت ایمن و استاندارد مدیریت می‌کند.
  • Flask-WTF: برای مدیریت فرم‌ها و محافظت در برابر حملات CSRF.
  • Session Fixation: Flask به طور خودکار به محافظت در برابر Session Fixation کمک می‌کند، اما آگاهی از آن مهم است.
  • HTTPS: همیشه از HTTPS برای رمزگذاری ارتباطات بین کلاینت و سرور استفاده کنید تا از شنود اطلاعات حساس جلوگیری شود.

در حالی که این مثال یک ایده اولیه از احراز هویت با Flask را ارائه می‌دهد، برای یک برنامه Production، باید به طور جدی به استفاده از اکستنشن‌های امنیتی و پیروی از بهترین شیوه‌های امنیتی توجه کنید.

نتیجه‌گیری و گام‌های بعدی

در این مقاله، ما ۱۰ نمونه کد کاربردی Flask را بررسی کردیم که از راه‌اندازی یک پروژه پایه گرفته تا موضوعات پیشرفته‌تری مانند مسیردهی پویا، مدیریت فرم‌ها، رندرینگ الگوها با Jinja2، استفاده از سشن‌ها و پیام‌های فلش، ساخت APIهای RESTful، مدیریت آپلود فایل‌ها، ایجاد صفحات خطای سفارشی، پیمانه‌بندی برنامه با Blueprints و حتی یک سیستم احراز هویت پایه را پوشش می‌دهند.

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

گام‌های بعدی برای پیشرفت در Flask:

  1. اکستنشن‌های Flask را کاوش کنید: جامعه Flask اکستنشن‌های غنی و باکیفیتی را توسعه داده است که قابلیت‌های Flask را به شدت گسترش می‌دهند. برخی از مهم‌ترین آن‌ها عبارتند از:
    • Flask-SQLAlchemy: برای ادغام ORM SQLAlchemy با Flask و مدیریت پایگاه داده.
    • Flask-WTF: برای ساده‌سازی کار با فرم‌ها و محافظت در برابر CSRF.
    • Flask-Login: برای مدیریت پیشرفته احراز هویت و کاربران.
    • Flask-Migrate: برای مدیریت مهاجرت‌های پایگاه داده.
    • Flask-RESTful / Flask-RESTX: برای ساخت APIهای RESTful با امکانات بیشتر.
  2. مفاهیم پایگاه داده: یادگیری نحوه اتصال Flask به پایگاه داده‌های مختلف (مانند PostgreSQL, MySQL, SQLite) و استفاده از ORMها برای تعامل با داده‌ها یک مهارت ضروری است.
  3. تست‌نویسی: یاد بگیرید چگونه تست‌های واحد (Unit Tests) و تست‌های یکپارچه‌سازی (Integration Tests) برای برنامه‌های Flask خود بنویسید تا از صحت عملکرد و پایداری کد خود اطمینان حاصل کنید.
  4. استقرار (Deployment): با نحوه استقرار برنامه‌های Flask در محیط‌های Production آشنا شوید. این شامل استفاده از سرورهای WSGI مانند Gunicorn یا uWSGI و وب‌سرورهایی مانند Nginx یا Apache است.
  5. امنیت: همیشه بهترین شیوه‌های امنیتی را در توسعه وب رعایت کنید. Flask ابزارهایی برای کمک به امنیت فراهم می‌کند، اما مسئولیت نهایی بر عهده توسعه‌دهنده است.
  6. میکروسرویس‌ها: درک اینکه چگونه Flask می‌تواند برای ساخت میکروسرویس‌های مستقل استفاده شود، یکی از مسیرهای پیشرفته‌تر است.

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

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

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

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

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

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

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

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

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