欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Flask 系列之 LoginManager

程序员文章站 2022-05-25 17:30:51
说明 操作系统:Windows 10 Python 版本:3.7x 虚拟环境管理器:virtualenv 代码编辑器:VS Code 实验目标 通过使用 flask login 进行会话管理的相关操作,并完成用户合法性登陆和退出。 安装 使用 首先,在 中创建 login_manager ,并进行相 ......

Flask 系列之 LoginManager

说明

  • 操作系统:windows 10
  • python 版本:3.7x
  • 虚拟环境管理器:virtualenv
  • 代码编辑器:vs code

实验目标

通过使用 flask-login 进行会话管理的相关操作,并完成用户合法性登陆和退出。

安装

pip install flask-login

使用

首先,在 todlist\app\__init__.py 中创建 login_manager,并进行相关配置,示例代码如下所示:

from flask import flask
from flask_sqlalchemy import sqlalchemy
from flask_bootstrap import bootstrap
from flask_login import loginmanager
from config import config

app = flask(__name__)
app.config.from_object(config)

db = sqlalchemy(app)
bootstrap = bootstrap(app)

login_manager = loginmanager(app)
login_manager.login_view = 'login'
login_manager.login_message = '你必须登陆后才能访问该页面'
login_manager.login_message_category = "info"

from app import views

然后,完善我们 todolist\models.py 中关于用户密码校验部分的逻辑处理,示例代码如下所示:

from app import db, login_manager
from datetime import datetime
from werkzeug.security import check_password_hash, generate_password_hash
from flask_login import usermixin, login_user


@login_manager.user_loader
def load_user(user_id):
    user = user.query.get(user_id)
    return user


class user(db.model, usermixin):
    __tablename__ = 'users'
    # __table_args__ = {"useexisting": true}

    id = db.column(db.integer, primary_key=true)
    name = db.column(db.string(20), nullable=false, unique=true)
    email = db.column(db.string(120), nullable=false, unique=true)
    pwd = db.column(db.string(120), nullable=false)

    things = db.relationship('thing', backref='user', lazy='dynamic')

    def __repr__(self):
        return "<user %r>" % self.name

    def generate_password_hash(self, pwd):
        self.pwd = generate_password_hash(pwd)

    def check_password_hash(self, pwd):
        return check_password_hash(self.pwd, pwd)


class thing(db.model):
    __tablename__ = 'things'
    # __table_args__ = {"useexisting": true}

    id = db.column(db.integer, primary_key=true)
    user_id = db.column(db.integer, db.foreignkey('users.id'))
    title = db.column(db.string(20), nullable=false)
    text = db.column(db.text, nullable=false)
    add_date = db.column(db.datetime, default=datetime.now)

    def __repr__(self):
        return "<todo %r>" % self.id

然后在 todolist\forms.py 中添加一个用于处理用户登陆的表单提交类,示例代码如下所示:

from flask_wtf import flaskform
from wtforms import stringfield, submitfield, textareafield, passwordfield
from wtforms.validators import datarequired, length, email, equalto, validationerror
from models import user


class registerform(flaskform):
    username = stringfield('用户名:', validators=[
                           datarequired(), length(min=6, max=20)])
    email = stringfield('邮箱:', validators=[datarequired(), email()])
    pwd = passwordfield('密码:', validators=[
        datarequired(), length(min=8, max=120)])
    confirm = passwordfield('确认密码:', validators=[
                            datarequired(), equalto('pwd')])
    submit = submitfield('提交')

    def validate_username(self, username):
        user = user.query.filter_by(name=username.data).first()
        if user:
            raise validationerror("用户昵称已存在。")

    def validate_email(self, email):
        user = user.query.filter_by(email=email.data).first()
        if user:
            raise validationerror('邮箱已存在.')


class loginform(flaskform):
    username = stringfield('用户名:', validators=[
                           datarequired(), length(min=6, max=20)])
    password = passwordfield('密码:', validators=[datarequired()])
    submit = submitfield('登陆')

    def validate_username(self, username):
        user = user.query.filter_by(name=username.data)
        if not user:
            raise validationerror('用户名不存在。')

然后修改我们的 todolist\app\views.py ,添加用户登陆和登出的路由处理函数,示例代码如下所示:

from flask import render_template, redirect, url_for, flash
from flask_login import login_user, login_required, current_user, logout_user
from app import app, db
from forms import registerform, loginform
from models import user


@app.context_processor
def inject_user():
    user = user.query.first()
    return dict(user=user)


@app.route('/')
@app.route('/index')
def index():
    if not current_user.is_authenticated:
        return redirect(url_for('login'))
    return render_template('index.html', title="首页")


@app.route('/login', methods=['post', 'get'])
def login():
    form = loginform()
    if form.validate_on_submit():
        name = form.username.data
        pwd = form.password.data
        user = user.query.filter_by(name=name).first()
        if user and user.check_password_hash(pwd):
            login_user(user)
            flash('登陆成功。', category='info')
            return redirect(url_for('index'))
        else:
            flash("密码或账户错误。", category='error')
    return render_template('login.html', title='登录', form=form)


@app.route('/logout')
@login_required
def logout():
    logout_user()
    flash('再见!')
    return redirect(url_for('login'))


@app.route('/register', methods=['post', 'get'])
def register():
    form = registerform()
    if form.validate_on_submit():
        username = form.username.data
        email = form.email.data
        pwd = form.pwd.data
        user = user(name=username, email=email)
        user.generate_password_hash(pwd)
        db.session.add(user)
        db.session.commit()
        flash('注册成功', category='info')
        return redirect(url_for('login'))
    return render_template('register.html', title='注册', form=form)

接着,修改我们 todolist\app\templates\login.html 页面,添加用户登陆的表单,示例代码如下所示:

{% extends 'base.html' %} {% block content %}
<h1>登录页面</h1>
{% from 'bootstrap/form.html' import render_form %} {{ render_form(form) }}
{% endblock %}

修改我们的 todolist\app\templates\nav.html 页面,完善菜单栏的逻辑控制,示例代码如下所示:

<nav class="navbar navbar-expand-lg navbar-light bg-light">
    <div class="container">
        <a class="navbar-brand" href="#">愿望清单</a>

        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsupportedcontent"
            aria-controls="navbarsupportedcontent" aria-expanded="false" aria-label="toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>

        <div class="collapse navbar-collapse" id="navbarsupportedcontent">
            <ul class="navbar-nav mr-auto">
                <li class="nav-item {% if request.endpoint == 'index' %} active {% endif %}">
                    <a class="nav-link" href="{{ url_for('index') }}">首页<span class="sr-only">(current)</span></a>
                </li>
            </ul>

            <ul class="navbar-nav">
                {% if current_user.is_authenticated and user %}
                <li class="nav-item {% if request.endpoint == 'logout' %} active {% endif %}">
                    <a class="nav-link" href="{{ url_for('logout') }}">登出</a>
                </li>
                {% else %}
                <li class="nav-item {% if request.endpoint == 'login' %} active {% endif %}">
                    <a class="nav-link" href="{{ url_for('login') }}">登录</a>
                </li>
                <li class="nav-item {% if request.endpoint == 'register' %} active {% endif %}">
                    <a class="nav-link" href="{{ url_for('register') }}">注册</a>
                </li>
                {% endif %}
            </ul>
        </div>
    </div>
</nav>

修改我们的 todolist\app\templates\index.html 页面,显示当前登陆的用户,示例代码如下所示:

{% extends 'base.html' %} {% block content %}

{% if current_user.is_authenticated and user %}
<h1>{{ current_user.name }},欢迎回来</h1>
{% else %}
<h1>首页</h1>
{% endif %}

{% endblock %}

此时,当我们运行起我们的网站后进入注册页面 http://127.0.0.1:5000 就可以进行用户的注册、登陆和登出了。

参考连接