Vánoční nadílka Vánoční nadílka
Až 80% zdarma! Předvánoční BLACK FRIDAY akce. Více informací

Lekce 5 - Přihlášení uživatelů ve Flask

Python Flask Přihlášení uživatelů ve Flask

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

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

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

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.


 

Stáhnout

Staženo 3x (649.88 kB)
Aplikace je včetně zdrojových kódů v jazyce Python

 

 

Článek pro vás napsal MQ .
Avatar
Jak se ti líbí článek?
1 hlasů
Autor je srdcem Pythonista.
Aktivity (2)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!