Lekce 5 - Přihlášení uživatelů ve Flask
V minulé lekci, Static files a upload souborů ve Flask, jsme si ukázali, jak používat static files a nahrávat soubory do galerie.
V dnešním Python tutoriálu si ukážeme, jak za použití Flask frameworku vytvořit přihlašovací formulář, používat session a aplikaci si rozdělíme na jednotlivé moduly pomocí Blueprintů, jelikož psát celou aplikaci do jediného souboru není efektivní.
Přihlašovací formulář
Ze všeho nejdříve potřebujeme formulář, který vytvoříme pomocí
WTForms. Stále pokračujeme s naší učební aplikací, do
main.py
přidáme další importy a třídu reprezentující
přihlašovací formulář:
from wtforms import StringField, PasswordField from wtforms.validators import InputRequired class LoginForm(FlaskForm): user = StringField("Uživatel", validators = [InputRequired()], render_kw = dict(class_ = "form-control")) #Přihlašovací jméno password = PasswordField("Heslo", validators = [InputRequired()], render_kw = dict(class_ = "form-control")) #Heslo submit = SubmitField("Odeslat", render_kw = dict(class_ = "btn btn-outline-primary btn-block"))
Vytváříme 2 vstupní pole na uživatelské jméno a heslo (+ submit
tlačítko). Aplikujeme validátor InputRequired()
, jelikož
chceme, by uživatel musel obě pole vyplnit.
Nyní vytvoříme logiku stránky, která se bude nacházet na adrese
http://127.0.0.1:5000/login/
. Opět pokračujeme v
main.py
:
from flask import session @app.route("/login/", methods = ["GET", "POST"]) def login(): form = LoginForm() if form.validate_on_submit(): user = form.user.data password = form.password.data if password == "letmein": session["user"] = user return render_template("login.html", form = form)
Nejdříve si importujeme session
. Česky se často používá
překlad "relace" a právě tento mechanismus nám umožňuje zapamatovat si,
že je daný uživatel přihlášený. Vnitřně sessions fungují pomocí
cookies v prohlížeči uživatele a dalšího mechanismu na serveru, který
tyto cookies ověřuje. Zadané údaje jsme si do session
uložili
pod klíč "user"
.
Jako vždy si vytváříme instanci formuláře, provedeme validaci a získáme data. Poté kontrolujeme, zda je heslo "letmein" a do session uložíme jméno uživatele. Prozatím uživatele neukládáme do databáze, ale máme jedno heslo pro všechny. Dále v kurzu aplikaci ještě vylepšíme.
Jelikož se pořádný web neobejde bez stránky pro odhlášení uživatele,
vytvoříme si také jednu. Stránka pro odhlášení se bude nacházet na
adrese http://127.0.0.1:5000/logout/
. Přidáme tedy další routu
a metodu do main.py
:
from flask import redirect, url_for @app.route("/logout/", methods = ["GET", "POST"]) def odhlasit(): session.pop("user", None) return redirect(url_for("login"))
Jelikož session
funguje stejně jako dictionary
neboli slovník, tak pomocí funkce session.pop(klíč, hodnota)
naší session
s klíčem "user"
odstraníme. Nakonec
uživatele přesměrujeme na stránku s loginem. Druhý parametr funkci
předáváme, aby nám v případě neexistujícího klíče nevyvolala
error.
Toto je error, který by na nás vyskočil, bez druhého parametru ve funkci
session.pop()
:
Vytvoříme si šablonu pro stránku s přihlašovacím formulářem
root/templates/login.html
:
{% extends "base.html" %} {% block header %} {{ super() }} <!-- V hlavní šabloně se bude vypisovat aktuální uživatel, proto vyrenderujeme její header --> {% endblock %} {% block content %} <form method="POST" class="row justify-content-center"> {{ form.hidden_tag() }} <table> <tr><td>{{ form.user.label }}:</td><td>{{ form.user }}</td></tr> <tr><td>{{ form.password.label }}: </td><td>{{ form.password }}</td></tr> <tr><td>{{ form.submit.label }}</td><td> {{ form.submit }}<td></tr> </table> </form> <div class="text-center"> {% for field, errors in form.errors.items() %} {% for error in errors %} <span style="color: red;">Chyba pole - {{ field }} hláška - {{ error }}</span><br> {% endfor %} {% endfor %} <hr> </div> {% endblock %}
Upravíme si hlavní šablonu base.html
, u které odstraníme
skript root/static/main.js
(ten můžete smazat) a v headeru budeme
vypisovat aktuálního uživatele:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Můj web ve Flasku</title> <style> <!-- Styl zůstává stejný --> </style> <link rel="stylesheet" href="{{ url_for('static', filename='bootstrap-4.1.3-dist/css/bootstrap.min.css')}}"> </head> <body> <header> {% block header %} {% set user = session["user"] %} Login - {% if user %} {{ user }} {% else %} Nejsi přihlášen {% endif %}<br> <a href = "{{ url_for('odhlasit') }}">Odhlásit</a> {% endblock %} </header> <div id="content"> {% block content %} {% endblock %} </div> <footer> {% block footer %} Prostý footer {% endblock %} </footer> </body> </html>
Také chceme, aby se vypisovat přihlášený uživatel v galerii a proto
šablonu root/templates/galerie.html
také upravíme:
{% extends "base.html" %} {% block header %} Galerie<br> {{ super() }} {% endblock %} {% block content %} <form method="POST" class="row justify-content-center" enctype="multipart/form-data"> {{ form.hidden_tag() }} <table> <tr><td>{{ form.soubor.label }}:</td><td>{{ form.soubor }}</td></tr> <tr><td>{{ form.submit.label }}</td><td> {{ form.submit }}<td></tr> </table> </form> <div class="text-center"> {% for field, errors in form.errors.items() %} {% for error in errors %} <span style="color: red;">Chyba pole - {{ field }} hláška - {{ error }}</span><br> {% endfor %} {% endfor %} <hr> </div> {% for obrazek in obrazky %} <img src="{{ url_for('static', filename='uploads/' + obrazek) }}"> {% endfor %} {% endblock %}
Ukázkovou stránku s kalkulačkou již nepotřebujeme a proto ji
odstraníme, namísto toho budeme přesměrovávat uživatele na galerii.
Smažeme template root/templates/template.html
, jelikož ho již
nepotřebujeme. Dále smažeme třídu MujFormular
a upravíme
funkci kalkulacka()
v main.py
:
@app.route("/") def kalkulacka(): return redirect(url_for("galerie"))
Výsledný web vypadá takto:
Modulární Aplikace
Microframework Flask obsahuje takzvané Blueprinty, které
nám dovolují rozdělit aplikaci na jednotlivé moduly jako například
login
a galerie
. Určitě uznáte, že náš soubor
main.py
je již poměrně nepřehledný a to máme stále velmi
triviální aplikaci. Abychom si aplikaci mohli rozdělit na moduly, musíme si
nejdříve upravit adresářovou strukturu:
root
static
- ...
templates
base.html
login
login.py
templates
login.html
galerie
galerie.py
templates
galerie.html
__init__.py
main.py
V jednotlivých modulech vytvoříme instanci třídy Blueprint
s názvem název modulu + _page
. Prvním parametrem je název
modulu, ten se používá například pro routování. Druhým parametrem je
název importovaného modulu, ten Flask používá pro získání cesty k
modulu. My nastavíme také template_folder
, což je složka s
šablonami.
Hlavní main.py
nyní slouží jen pro spuštění aplikace a
jeho obsah je následující:
from root import app if __name__ == "__main__": app.run(debug=True)
root/__init__.py
obsahuje základ/inicializaci aplikace a
přesměrování na galerii:
from flask import Flask, redirect, url_for from root.galerie.galerie import galerie_page from root.login.login import login_page app = Flask(__name__) # Musíme nastavit SECRET_KEY, pokud chceme používat CSRF app.config["SECRET_KEY"] = "super tajny klic" app.register_blueprint(galerie_page) app.register_blueprint(login_page) # Nastavíme složku, kam se budou obrázky ukládat UPLOAD_FOLDER = app.static_folder + "/uploads/" app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER @app.route("/") def kalkulacka(): return redirect(url_for("galerie_page.galerie"))
Soubor root/login/login.py
obsahuje logiku stránky pro
přihlášení:
from flask import render_template, url_for, session, redirect, Blueprint from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, SubmitField from wtforms.validators import InputRequired login_page = Blueprint("login_page", __name__, template_folder = "templates") class LoginForm(FlaskForm): user = StringField("Uživatel", validators = [InputRequired()], render_kw = dict(class_ = "form-control")) #Přihlašovací jméno password = PasswordField("Heslo", validators = [InputRequired()], render_kw = dict(class_ = "form-control")) #Heslo submit = SubmitField("Odeslat", render_kw = dict(class_ = "btn btn-outline-primary btn-block")) @login_page.route("/login/", methods = ["GET", "POST"]) def login(): form = LoginForm() if form.validate_on_submit(): user = form.user.data password = form.password.data if password == "letmein": session["user"] = user return render_template("login.html", form = form) @login_page.route("/logout/", methods = ["GET", "POST"]) def odhlasit(): session.pop("user", None) return redirect(url_for("login_page.login"))
A soubor root/galerie/galerie.py
obsahuje logiku stránky s
galerií:
from flask import render_template, Blueprint from flask_wtf import FlaskForm from wtforms import FileField, SubmitField from flask_wtf.file import FileRequired from werkzeug.utils import secure_filename import os import root galerie_page = Blueprint("galerie_page", __name__, template_folder = "templates") class FileFormular(FlaskForm): soubor = FileField("Vlož obrázek", validators = [FileRequired()]) submit = SubmitField("Odeslat", render_kw = dict(class_ = "btn btn-outline-primary btn-block")) @galerie_page.route("/galerie/", methods = ["GET", "POST"]) def galerie(): form = FileFormular() if form.validate_on_submit(): soubor = form.soubor.data nazev = secure_filename(soubor.filename) soubor.save(os.path.join(root.app.config['UPLOAD_FOLDER'], nazev)) obrazky = os.listdir(root.app.static_folder + "/uploads") return render_template("galerie.html", form = form, obrazky = obrazky)
Jednotlivé šablony zůstávají stejné Pokud jste se při rozdělení aplikace do modulů ztratili, stáhněte si přiloženou ukázku.
V příští lekci, Zobrazení zpráv a vlastní 404 stránka ve Flask, se naučíme používat mechanismus uživatelských zpráv a nastavíme si vlastní chybovou stránku.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 157x (649.88 kB)
Aplikace je včetně zdrojových kódů v jazyce Python