نقشه ها و نماها(Blueprints and Views)¶
تابع view کدی است که شما برای پاسخ به درخواست های برنامه خود می نویسید. فلاسک از الگوهایی برای تطبیق URL درخواست ورودی با نمای مورد نظر استفاده می کند. View دادههایی را برمیگرداند که فلاسک به یک پاسخ خروجی تبدیل میکند. فلاسک همچنین می تواند جهت دیگری را برود و یک URL برای یک view بر اساس نام و آرگومان های آن ایجاد کند.
ساخت یک طرح اولیه¶
Blueprint
راهی برای سازماندهی گروهی از نماهای مرتبط و کدهای دیگر است. به جای ثبت نماها و سایر کدها به طور مستقیم با یک برنامه، آنها با یک نقشه ثبت می شوند. سپس طرح اولیه هنگامی که در عملکرد کارخانه در دسترس باشد با برنامه ثبت می شود.
Flaskr دو طرح دارد، یکی برای توابع احراز هویت و دیگری برای توابع پست های وبلاگ. کد هر طرح در یک ماژول جداگانه خواهد بود. از آنجایی که وبلاگ باید در مورد احراز هویت اطلاعات داشته باشد، ابتدا یک احراز هویت را بنویسید.
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()
طرح اولیه را از کارخانه وارد و ثبت کنید. قبل از بازگرداندن برنامه، کد جدید را در انتهای عملکرد کارخانه قرار دهید.
def create_app():
app = ...
# existing code omitted
from . import auth
app.register_blueprint(auth.bp)
return app
طرح احراز هویت دارای نماهایی برای ثبت نام کاربران جدید و ورود و خروج از سیستم است.
نمای اول: ثبت نام¶
هنگامی که کاربر از نشانی اینترنتی /auth/register
بازدید می کند، نمای HTML ، register
را همراه با فرمی برای پر کردن آن برمی گرداند. هنگامی که آنها فرم را ارسال می کنند، ورودی آنها را تأیید می کند و یا دوباره فرم را با پیام خطا نشان می دهد یا کاربر جدید را ایجاد می کند و به صفحه ورود می رود.
در حال حاضر شما فقط کد نما را می نویسید. در صفحه بعد، الگوهایی را برای تولید فرم HTML خواهید نوشت.
@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
بررسی می شود :
@bp.route
شانی اینترنتی/register
را با عملکرد نمایregister
مرتبط می کند. هنگامی که فلاسک درخواستی برای/auth/register
دریافت می کند، نمایregister
را فراخوانی می کند و از مقدار بازگشتی به عنوان پاسخ استفاده می کند.اگر کاربر فرم را ارسال کرده باشد،
request.method
خواهد بود. در این حالت، اعتبار ورودی را شروع کنید.request.form
نوع خاصی ازdict
نگاشت کلیدها و مقادیر فرم ارسال شده است. کاربرusername
وpassword
خود را وارد می کند.تأیید کنید که
username
وpassword
خالی نیستند.در صورت موفقیت آمیز بودن اعتبارسنجی، داده های کاربر جدید را در پایگاه داده وارد کنید.
db.execute
برای هر ورودی کاربر یک کوئری SQL با متغیرهای?
می دهد و چندین مقدار را جایگزین می کند. کتابخانه پایگاه داده از تزریق کد SQL فرار می کند تا شما در برابر حمله SQL injection آسیب پذیر نباشید.برای امنیت، رمزهای عبور هرگز نباید مستقیماً در پایگاه داده ذخیره شوند. در عوض،
generate_password_hash()
برای هش ایمن رمز عبور استفاده می شود و آن هش ذخیره می شود. از آنجایی که این کوئری داده ها را تغییر می دهد،db.commit()
باید پس از آن برای ذخیره تغییرات فراخوانی شود.اگر نام کاربری قبلاً وجود داشته باشد، یک
sqlite3.IntegrityError
رخ خواهد داد که باید به عنوان یک خطای اعتبارسنجی دیگر به کاربر نشان داده شود.
پس از ذخیره سازی کاربر، آنها به صفحه ورود هدایت می شوند.
url_for()
، URL را برای نمای ورود بر اساس نام آن ایجاد می کند. این برای نوشتن مستقیم URL ترجیح داده می شود زیرا به شما امکان می دهد URL را بعداً بدون تغییر همه کدهایی که به آن پیوند می دهند تغییر دهید.redirect()
یک پاسخ تغییر مسیر به URL تولید شده ایجاد می کند.اگر اعتبارسنجی ناموفق باشد، خطا به کاربر نشان داده می شود.
flash()
پیامهایی را ذخیره میکند که میتوان آنها را هنگام رندر کردن الگو بازیابی کرد.هنگامی که کاربر در ابتدا به
auth/register
میرود، یا یک خطای اعتبارسنجی وجود دارد، یک صفحه HTML با فرم ثبت نام باید نشان داده شود.render_template()
یک قالب حاوی HTML را ارائه می دهد که در مرحله بعدی آموزش آن را می نویسید.
ورود به حساب کاربری¶
این نمای از همان الگوی نمای``register`` در بالا پیروی می کند.
@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
وجود دارد:
کاربر ابتدا و در یک متغیر برای استفاده بعدی ذخیره می شود.
fetchone()
یک سطر ازکوئری را برمی گرداند. اگر کوئری هیچ نتیجه ای نداشت،None
را برمی گرداند. بعداً ازfetchall()
استفاده می شود که فهرستی از همه نتایج را برمی گرداند.check_password_hash()
رمز عبور ارسالی را همانند هش ذخیره شده هش می کند و به طور ایمن آنها را با هم مقایسه می کند. اگر مطابقت داشته باشند، رمز عبور صحیح است.session
یکdict است که داده ها را در بین درخواست ها ذخیره می کند. هنگامی که اعتبارسنجی موفقیت آمیز بود،``id`
کاربر در یک جلسه جدید ذخیره می شود. دادهها در یک کوکی ذخیره میشوند که به مرورگر ارسال میشود و مرورگر آن را با درخواستهای بعدی باز میفرستد. فلاسک به طور ایمن داده ها را امضا می کند تا نتوان آنها را دستکاری کرد.
اکنون که id
کاربر در session
ذخیره شده است، در درخواستهای بعدی در دسترس خواهد بود. در ابتدای هر درخواست، در صورتی که کاربری وارد شده باشد، باید اطلاعات وی بارگذاری شده و در اختیار سایر نماها قرار گیرد.
@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
در درخواستهای بعدی کاربر را بارگیری نمیکند.
@bp.route('/logout')
def logout():
session.clear()
return redirect(url_for('index'))
نیاز احراز هویت در سایر نماها¶
ایجاد، ویرایش و حذف پستهای وبلاگ نیاز به ورود کاربر دارد. میتوان از یک دکوراتور برای بررسی این موضوع برای هر نما که روی آن اعمال میشود استفاده کرد.
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'
اضافه کردید.
با قالب ها ادامه دهید.