(五)flask搭建博客系列之LoginManager

本篇博客主要介绍 flask-login 会话管理方面的内容,完成用户合法的登录和登出,以及一些页面视图的保护功能。

1.相关库安装

pip install flask-login

2.相关代码

在 extensions.py 中添加 flask-login 的扩展实例和其需要的加载用户函数,以及相关的登录控制:

from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_bootstrap import Bootstrap
from flask_login import LoginManager


db = SQLAlchemy()
migrate = Migrate()
bootstrap = Bootstrap()
login_manager = LoginManager()


@login_manager.user_loader
def load_user(user_id):
    from myblog.models import Admin
    user = Admin.query.get(int(user_id))
    return user


login_manager.login_view = 'admin.login'
login_manager.login_message = '你必须登陆后才能访问该页面!'
login_manager.login_message_category = "info"

在 __init__.py 中添加初始化 flask-login 的代码:

from flask import Flask

from myblog.home.blog import blog_bp
from myblog.home.admin import admin_bp
from myblog.extensions import db, migrate, bootstrap, login_manager
from myblog.config import Config


def create_app():
    app = Flask(__name__)
    app.config.from_object(Config)

    register_blueprints(app)
    register_extensions(app)

    return app


def register_blueprints(app):
    app.register_blueprint(blog_bp)
    app.register_blueprint(admin_bp, url_prefix='/admin')


def register_extensions(app):
    db.init_app(app)
    db.create_all(app=app)
    migrate.init_app(app, db)
    bootstrap.init_app(app)
    login_manager.init_app(app)

修改 models.py 中的 Admin 类,使其继承自 UserMixin 类:

from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin

from myblog.extensions import db


class Admin(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(20))
    password_hash = db.Column(db.String(128))

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def validate_password(self, password):
        return check_password_hash(self.password_hash, password)

修改 home/admin.py 中的视图函数,添加登录登出、视图保护的代码:

from flask import request, Blueprint, render_template, redirect, url_for, flash
from flask_login import login_user, login_required, current_user, logout_user

from myblog.models import Article, Category, Comment, Admin
from myblog.forms import LoginForm, AddForm
from myblog.extensions import db


admin_bp = Blueprint('admin', __name__)


@admin_bp.route('/login', methods=['POST', 'GET']) 
def login():
    form = LoginForm()
    if form.validate_on_submit():
        username = form.username.data
        password = form.password.data
        remember = form.remember.data
        admin = Admin.query.first()

        if username == admin.username and admin.validate_password(password):
            login_user(admin)
            flash('登录成功', category='info')
            return redirect(url_for('admin.edit'))
        else:
            flash('登录失败', category='warning')
    return render_template('admin/login.html', form=form)


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


@admin_bp.route('/edit')
@login_required
def edit():
    page = request.args.get('page', 1, type=int)
    articles = Article.query.order_by(Article.timestamp.desc()).paginate(page, 10, False)

    return render_template('admin/edit.html', articles=articles)


@admin_bp.route('/delete/<int:article_id>')
@login_required
def delete(article_id):
    article = Article.query.filter(Article.id == article_id).first()
    db.session.delete(article)
    db.session.commit()
    flash('删除成功', category='info')

    return redirect(url_for('admin.edit')) 


@admin_bp.route('/add', methods=['POST', 'GET'])
@login_required
def add():
    form = AddForm()
    if form.validate_on_submit():
        title = form.title.data
        body = form.body.data
        category = form.category.data

        article = Article(title=title, body=body, category_id=category)
        db.session.add(article)
        db.session.commit()
        flash('新建博客成功', category='info')
        return redirect(url_for('admin.edit'))

    return render_template('admin/add.html', form=form)

在 base.html 中修改导航栏页面,视登录情况显示登录或登出按钮:

<!DOCTYPE html>
<html lang="en">

<head>
    {% block head %}
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> 
        {% block styles %}
            <link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}"> {{ bootstrap.load_css() }} 
        {% endblock %} 
        <title>{% block title %}{% endblock %}</title>
    {% endblock %}
</head>

<body>
    {% for message in get_flashed_messages() %}
        <div class="alert alert-primary" role="alert">
            {{ message }}
        </div>
        {% endfor %}
    {% block nav %}
        <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 == 'blog.index' %} active {% endif %}">
                            <a class="nav-link" href="{{ url_for('blog.index') }}">首页<span class="sr-only">(current)</span></a>
                        </li>
                    </ul>
                    
                    <ul class="navbar-nav">
                        {% if current_user.is_authenticated %}
                        <li class="nav-item {% if request.endpoint == 'admin.logout' %} active {% endif %}">
                            <a class="nav-link" href="{{ url_for('admin.logout') }}">登出</a>
                        </li>
                        {% else %}
                        <li class="nav-item {% if request.endpoint == 'admin.login' %} active {% endif %}">
                            <a class="nav-link" href="{{ url_for('admin.login') }}">登录</a>
                        </li>                
                        {% endif %}
                    </ul>
                </div>
            </div>
        </nav>
    {% endblock nav %}

    <div class="container">
        {% block content %}{% endblock%}
    </div>

    {% block scripts %}
        {{ bootstrap.load_js() }} 
    {% endblock %}

</body>

</html>

3.相关页面展示

登录后的后台页面:

未登录时,后台页面的 edit 视图需要登录才能查看:

posted @ 2020-10-10 14:54  qxcheng  阅读(550)  评论(0编辑  收藏  举报