نقشه ها و نماها(Blueprints and Views)

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

ساخت یک طرح اولیه

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

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

flaskr/auth.py
import functools

from flask import (
    Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash

from flaskr.db import get_db

bp = Blueprint('auth', __name__, url_prefix='/auth')

این یک Blueprint با نام auth ایجاد می کند. مانند شی برنامه، طرح اولیه باید بداند کجا تعریف شده است، بنابراین __name__ به عنوان آرگومان دوم ارسال می شود. url_prefix به تمام URL های مرتبط با طرح اولیه اضافه می شود.

با استفاده از app.register_blueprint() طرح اولیه را از کارخانه وارد و ثبت کنید. قبل از بازگرداندن برنامه، کد جدید را در انتهای عملکرد کارخانه قرار دهید.

flaskr/__init__.py
def create_app():
    app = ...
    # existing code omitted

    from . import auth
    app.register_blueprint(auth.bp)

    return app

طرح احراز هویت دارای نماهایی برای ثبت نام کاربران جدید و ورود و خروج از سیستم است.

نمای اول: ثبت نام

هنگامی که کاربر از نشانی اینترنتی /auth/register بازدید می کند، نمای HTML ، register را همراه با فرمی برای پر کردن آن برمی گرداند. هنگامی که آنها فرم را ارسال می کنند، ورودی آنها را تأیید می کند و یا دوباره فرم را با پیام خطا نشان می دهد یا کاربر جدید را ایجاد می کند و به صفحه ورود می رود.

در حال حاضر شما فقط کد نما را می نویسید. در صفحه بعد، الگوهایی را برای تولید فرم HTML خواهید نوشت.

flaskr/auth.py
@bp.route('/register', methods=('GET', 'POST'))
def register():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        db = get_db()
        error = None

        if not username:
            error = 'Username is required.'
        elif not password:
            error = 'Password is required.'

        if error is None:
            try:
                db.execute(
                    "INSERT INTO user (username, password) VALUES (?, ?)",
                    (username, generate_password_hash(password)),
                )
                db.commit()
            except db.IntegrityError:
                error = f"User {username} is already registered."
            else:
                return redirect(url_for("auth.login"))

        flash(error)

    return render_template('auth/register.html')

در اینجا عملکرد تابع نمای register بررسی می شود :

  1. @bp.route شانی اینترنتی /register را با عملکرد نمای register مرتبط می کند. هنگامی که فلاسک درخواستی برای /auth/register دریافت می کند، نمای register را فراخوانی می کند و از مقدار بازگشتی به عنوان پاسخ استفاده می کند.

  2. اگر کاربر فرم را ارسال کرده باشد، request.method خواهد بود. در این حالت، اعتبار ورودی را شروع کنید.

  3. request.form نوع خاصی از dict نگاشت کلیدها و مقادیر فرم ارسال شده است. کاربر username و password خود را وارد می کند.

  4. تأیید کنید که username و password خالی نیستند.

  5. در صورت موفقیت آمیز بودن اعتبارسنجی، داده های کاربر جدید را در پایگاه داده وارد کنید.

    • db.execute برای هر ورودی کاربر یک کوئری SQL با متغیرهای ? می دهد و چندین مقدار را جایگزین می کند. کتابخانه پایگاه داده از تزریق کد SQL فرار می کند تا شما در برابر حمله SQL injection آسیب پذیر نباشید.

    • برای امنیت، رمزهای عبور هرگز نباید مستقیماً در پایگاه داده ذخیره شوند. در عوض، generate_password_hash() برای هش ایمن رمز عبور استفاده می شود و آن هش ذخیره می شود. از آنجایی که این کوئری داده ها را تغییر می دهد، db.commit() باید پس از آن برای ذخیره تغییرات فراخوانی شود.

    • اگر نام کاربری قبلاً وجود داشته باشد، یک sqlite3.IntegrityError رخ خواهد داد که باید به عنوان یک خطای اعتبارسنجی دیگر به کاربر نشان داده شود.

  6. پس از ذخیره سازی کاربر، آنها به صفحه ورود هدایت می شوند. url_for() ، URL را برای نمای ورود بر اساس نام آن ایجاد می کند. این برای نوشتن مستقیم URL ترجیح داده می شود زیرا به شما امکان می دهد URL را بعداً بدون تغییر همه کدهایی که به آن پیوند می دهند تغییر دهید. redirect() یک پاسخ تغییر مسیر به URL تولید شده ایجاد می کند.

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

  8. هنگامی که کاربر در ابتدا به auth/register می‌رود، یا یک خطای اعتبارسنجی وجود دارد، یک صفحه HTML با فرم ثبت نام باید نشان داده شود. render_template() یک قالب حاوی HTML را ارائه می دهد که در مرحله بعدی آموزش آن را می نویسید.

ورود به حساب کاربری

این نمای از همان الگوی نمای``register`` در بالا پیروی می کند.

flaskr/auth.py
@bp.route('/login', methods=('GET', 'POST'))
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        db = get_db()
        error = None
        user = db.execute(
            'SELECT * FROM user WHERE username = ?', (username,)
        ).fetchone()

        if user is None:
            error = 'Incorrect username.'
        elif not check_password_hash(user['password'], password):
            error = 'Incorrect password.'

        if error is None:
            session.clear()
            session['user_id'] = user['id']
            return redirect(url_for('index'))

        flash(error)

    return render_template('auth/login.html')

چند تفاوت با نمای register وجود دارد:

  1. کاربر ابتدا و در یک متغیر برای استفاده بعدی ذخیره می شود.

    fetchone() یک سطر ازکوئری را برمی گرداند. اگر کوئری هیچ نتیجه ای نداشت،None را برمی گرداند. بعداً از fetchall() استفاده می شود که فهرستی از همه نتایج را برمی گرداند.

  2. check_password_hash() رمز عبور ارسالی را همانند هش ذخیره شده هش می کند و به طور ایمن آنها را با هم مقایسه می کند. اگر مطابقت داشته باشند، رمز عبور صحیح است.

  3. session یک dict است که داده ها را در بین درخواست ها ذخیره می کند. هنگامی که اعتبارسنجی موفقیت آمیز بود،``id` کاربر در یک جلسه جدید ذخیره می شود. داده‌ها در یک کوکی ذخیره می‌شوند که به مرورگر ارسال می‌شود و مرورگر آن را با درخواست‌های بعدی باز می‌فرستد. فلاسک به طور ایمن داده ها را امضا می کند تا نتوان آنها را دستکاری کرد.

اکنون که id کاربر در session ذخیره شده است، در درخواست‌های بعدی در دسترس خواهد بود. در ابتدای هر درخواست، در صورتی که کاربری وارد شده باشد، باید اطلاعات وی بارگذاری شده و در اختیار سایر نماها قرار گیرد.

flaskr/auth.py
@bp.before_app_request
def load_logged_in_user():
    user_id = session.get('user_id')

    if user_id is None:
        g.user = None
    else:
        g.user = get_db().execute(
            'SELECT * FROM user WHERE id = ?', (user_id,)
        ).fetchone()

bp.before_app_request() تابعی را ثبت می کند که قبل از تابع view اجرا می شود، مهم نیست چه URL درخواست شده است. load_logged_in_user بررسی می کند که آیا شناسه کاربری در session ذخیره شده است یا خیر و داده های آن کاربر را از پایگاه داده دریافت می کند و آن را در g.user ذخیره می کند، که طول آن به مدت درخواست. اگر شناسه کاربری وجود نداشته باشد، یا اگر شناسه وجود نداشته باشد، g.user برابر با None خواهد بود.

خروج از حساب کاربری

برای خروج از سیستم، نیاز دارید که شناسه کاربری را از session حذف کنید. سپس load_logged_in_user در درخواست‌های بعدی کاربر را بارگیری نمی‌کند.

flaskr/auth.py
@bp.route('/logout')
def logout():
    session.clear()
    return redirect(url_for('index'))

نیاز احراز هویت در سایر نماها

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

flaskr/auth.py
def login_required(view):
    @functools.wraps(view)
    def wrapped_view(**kwargs):
        if g.user is None:
            return redirect(url_for('auth.login'))

        return view(**kwargs)

    return wrapped_view

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

نقاط پایانی و URL ها

تابع url_for() یک نما را بر اساس نام و آرگومان ها تولید می کند. نام مرتبط با یک ، endpoint نیز نامیده می شود و به طور پیش فرض با نام تابع view یکی است.

برای مثال، نمای hello() که قبلاً در آموزش به کارخانه برنامه افزوده شده است، نام 'hello' را دارد و می‌توان آن را با url_for('hello') پیوند داد. اگر به آرگومان نیاز داشت، که بعداً خواهید دید، به استفاده از url_for('hello', who='World'). پیوند داده می‌شود.

هنگام استفاده از یک طرح اولیه، نام طرح اولیه به نام تابع اضافه می شود، بنابراین نقطه پایانی برای تابع login که در بالا نوشتید، 'auth.login' است زیرا آن را به طرح 'auth' اضافه کردید.

با قالب ها ادامه دهید.