IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

5. diel - Prihlásenie užívateľov vo Flask

V minulej lekcii, Static files a upload súborov vo Flask , sme si ukázali, ako používať static files a nahrávať súbory do galérie. V dnešnom Python tutoriále si ukážeme, ako za použitia Flask frameworku vytvoriť prihlasovací formulár, používať session a aplikáciu si rozdelíme na jednotlivé moduly pomocou Blueprint, pretože písať celú aplikáciu do jediného súboru nie je efektívne.

Prihlasovací formulár

Zo všetkého najskôr potrebujeme formulár, ktorý vytvoríme pomocou WTForms. Stále pokračujeme s našou učebné aplikácií, do main.py pridáme ďalšie importy a triedu reprezentujúci prihlasovací formulár:

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árame 2 vstupné pole na užívateľské meno a heslo (+ submit tlačidlo). Aplikujeme validátor InputRequired(), keďže chceme, aby užívateľ musel obe polia vyplniť.

Teraz vytvoríme logiku stránky, ktorá sa bude nachádzať na adrese http://127.0.0.1:5000/login/. Opäť 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)

Najskôr si importujeme session. Slovensky sa často používa preklad "relácie" a práve tento mechanizmus nám umožňuje zapamätať si, že je daný užívateľ prihlásený. Vnútorne sessions fungujú pomocou cookies v prehliadači používateľa a ďalšieho mechanizmu na serveri, ktorý tieto cookies overuje. Zadané údaje sme si do session uložili pod kľúč "user".

Ako vždy si vytvárame inštanciu formuláre, vykonáme validáciu a získame dáta. Potom kontrolujeme, či je heslo "letmein" a do session uložíme meno používateľa. Zatiaľ užívateľa neukladáme do databázy, ale máme jedno heslo pre všetkých. Ďalej v kurze aplikácii ešte vylepšíme.

Keďže sa poriadny web nezaobíde bez stránky pre odhlásenie užívateľov, vytvoríme si tiež jednu. Stránka pre odhlásenie sa bude nachádzať na adrese http://127.0.0.1:5000/logout/. Pridáme teda ďalší route a metódu 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"))

Keďže session funguje rovnako ako dictionary čiže slovník, tak pomocou funkcie session.pop(klíč, hodnota) našej session s kľúčom "user" odstránime. Nakoniec užívateľa presmerujeme na stránku s loginom. Druhý parameter funkciu odovzdávame, aby nám v prípade neexistujúceho kľúča nevyvolala error.

Toto je error, ktorý by na nás vyskočil, bez druhého parametru vo funkcii session.pop():

Vyvolanie chyby session.pop v frameworku Flask pre Python - Flask framework pre Python

Vytvoríme si šablónu pre stránku s prihlasovacím formulárom 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ú šablónu base.html, u ktorej odstránime skript root/static/main.js (ten môžete zmazať) a v header budeme vypisovať aktuálneho užívateľa:

<!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>

Tiež chceme, aby sa vypisovať prihlásený používateľ v galérii a preto šablónu root/templates/galerie.html tiež 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ážkovú stránku s kalkulačkou už nepotrebujeme a preto ju odstránime, namiesto toho budeme presmerovávať používateľov na galériu. Zmažeme template root/templates/template.html, keďže ho už nepotrebujeme. Ďalej zmažeme triedu MujFormular a upravíme funkciu kalkulacka() v main.py:

@app.route("/")
def kalkulacka():
    return redirect(url_for("galerie"))

Výsledný web vyzerá takto:

Prihlasovanie užívateľov vo Flask frameworku pre Python - Flask framework pre Python

Modulárny Aplikácia

Microframework Flask obsahuje takzvané Blueprint, ktoré nám dovoľujú rozdeliť aplikáciu na jednotlivé moduly ako napríklad login a galerie. Určite uznáte, že náš súbor main.py je už pomerne neprehľadný a to máme stále veľmi triviálne aplikáciu. Aby sme si aplikáciu mohli rozdeliť na moduly, musíme si najskôr upraviť adresárovú štruktúru:

  • root
    • static
      • ...
    • templates
      • base.html
    • login
      • login.py
      • templates
        • login.html
    • galerie
      • galerie.py
      • templates
        • galerie.html
    • __init__.py
  • main.py
V jednotlivých moduloch vytvoríme inštanciu triedy Blueprint s názvom název modulu + _page. Prvým parametrom je názov modulu, ten sa používa napríklad pre routovanie. Druhým parametrom je názov importovaného modulu, ten Flask používa na získanie cesty k modulu. My nastavíme tiež template_folder, čo je zložka s šablónami.

Hlavné main.py teraz slúži len pre spustenie aplikácie a jeho obsah je nasledujúci:

from root import app
if __name__ == "__main__":
    app.run(debug=True)

root/__init__.py obsahuje základ / inicializácii aplikácie a presmerovanie na galériu:

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"))

Súbor root/login/login.py obsahuje logiku stránky pre prihlásenie:

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 súbor root/galerie/galerie.py obsahuje logiku stránky s galériou:

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é šablóny zostávajú rovnaké :) Ak ste sa pri rozdelení aplikácie do modulov stratili, stiahnite si priloženú ukážku.

V budúcej lekcii, Zobrazenie správ a vlastné 404 stránka vo Flask , sa naučíme používať mechanizmus užívateľských správ a nastavíme si vlastný chybovú stránku.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 144x (649.88 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Python

 

Predchádzajúci článok
Static files a upload súborov vo Flask
Všetky články v sekcii
Flask framework pre Python
Preskočiť článok
(neodporúčame)
Zobrazenie správ a vlastné 404 stránka vo Flask
Článok pre vás napísal MQ .
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Používám hlavně Python a zajímám se o Deep Learning a vše kolem.
Aktivity