Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

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():

Vyvolání chyby session.pop v frameworku Flask pro Python - Flask framework pro Python

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:

Přihlašování uživatelů ve Flask frameworku pro Python - Flask framework pro Python

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 123x (649.88 kB)
Aplikace je včetně zdrojových kódů v jazyce Python

 

Předchozí článek
Static files a upload souborů ve Flask
Všechny články v sekci
Flask framework pro Python
Přeskočit článek
(nedoporučujeme)
Zobrazení zpráv a vlastní 404 stránka ve Flask
Článek pro vás napsal MQ .
Avatar
Uživatelské hodnocení:
22 hlasů
Používám hlavně Python a zajímám se o Deep Learning a vše kolem.
Aktivity