使用flask_sqlalchemy、flask_login、flask_admin模拟最简单的管理员后台

一、功能描述

使用flask_sqlalchemy、flask_login、flask_admin,模拟管理后台的登录认证过程。目的是只让已经登录的用户访问index和admin页面。

二、功能及效果

1、 访问index或者后台admin页面,自动重定向到首页

image

2、登录账号,如果数据库里面检索到记录,登录,并重定向到管理页面。同时,可以访问index页面了,也可以让管理员管理已经注册到admin的model
image
image
image

3、 使用注销链接之后,重新返回登录页面

image

三、完整代码

HTML

{% extends 'BASE.html' %}

{% block Content %}
    <h1>lOGIN</h1>
    
    {% for message in get_flashed_messages() %}
        <p>{{ message }}</p>    
    {% endfor %}

    <form action="{{ url_for('login') }}" method="post">
        {% for item in form %}
            <p>{{ item.label }}</p>
            <p>{{ item }}</p>
        {% endfor %}
        <p>
            <input type="submit" value="提交">
        </p>
        <p>
            <a href="{{ url_for('logout') }}">注销</a>
        </p>
    </form>
{% endblock %}

PYTHON

from flask import Flask,render_template,redirect,url_for,request,flash
from flask_sqlalchemy import SQLAlchemy
from flask_admin import Admin,AdminIndexView,menu
from flask_admin.contrib.sqla import ModelView
from flask_login import LoginManager,UserMixin,login_user,current_user,logout_user,login_required
from flask_wtf import Form
from wtforms import StringField,PasswordField


app = Flask("flask_login_admin")
app.secret_key = "mykey"
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///TestDB.sqlite3"

# 使用flask_sqlalchemy,用orm创建用户数据库Model,注意一定要继承UserMixin类
db = SQLAlchemy(app=app)
class Admin_Table(db.Model,UserMixin):
    id = db.Column(db.Integer,primary_key=True)
    username = db.Column(db.String(20),unique=True)
    password = db.Column(db.String(20))


login_manager = LoginManager(app=app)
# 你必须提供一个 user_loader 回调。这个回调用于从会话中存储的用户 ID 重新加载用户对象。它应该接受一个用户的 unicode ID 作为参数,并且返回相应的用户对象。
@login_manager.user_loader
def user_log(user_id):
    return Admin_Table.query.get(user_id)

# 使用@login_required装饰器的时候,如果没有登录,默认返回错误401,如果使用了这个装饰器,可以设置成重定向到登录页面
@login_manager.unauthorized_handler
def unauthorized_handler():
    flash("请先登录!")
    return redirect(url_for("login"))


# 使用flask_wtf在前端创建登录表单
class Login_Form(Form):
    username = StringField("用户名")
    password = PasswordField("密码")

@app.route("/login/",endpoint="login",methods=["GET","POST"])
def login():
    if request.method == "GET":
        # 如果已经登录的话,直接重定向到admin页面
        if current_user.is_authenticated:
            return redirect(url_for("admin.index"))
        else:
            form = Login_Form()
            return render_template("login.html",form=form)
    elif request.method == "POST":
        form = Login_Form(request.form)
        if form.validate():
            username = form.username.data
            password = form.password.data
            # 检查数据库里面是否有对应的账密
            user = Admin_Table.query.filter(Admin_Table.username == username,Admin_Table.password == password).first()
            if user:
                # 如果数据库里面有记录,将当前用户的orm对象添加到flask_login里面,之后current_user.is_authenticated返回的就是True了
                login_user(user)
                flash("登录成功!")
                return redirect(url_for("admin.index"))
        flash("没有相关的用户名和密码!")
        return render_template("login.html",form=form)

# 注意,login_required一下要放在route装饰器的下面,如果没有登录,自动使用unauthorized_handler()重定向到login页面
@app.route("/",endpoint="index")
@login_required
def index():
    return render_template("index.html")

# 用于注销的视图函数,为了保证只有在登录状态下才能logout,所以也要加一个@logout_required装饰器
@app.route("/logout/",endpoint="logout")
@login_required
def logout():
    logout_user()
    flash("注销成功")
    return redirect(url_for("login"))

# 如果只对flask_admin注册的其中一个视图函数进行认证,继承ModelView类,然后改写里面的方法就行了,也就是说在没有认证的前提下,还是可以访问/admin/路径,只不过不显示注册的视图函数
class MyModelView(ModelView):
    def is_accessible(self):
        return current_user.is_authenticated
    def inaccessible_callback(self, name, **kwargs):
        return redirect(url_for("login"))

# 对整个admin后台进行认证,没有登录认证的话,访问/admin/就直接重定向到login页面,和上面的MyModelView类选一个即可实现功能
class MyAdminIndexView(AdminIndexView):
    def is_accessible(self):
        return current_user.is_authenticated
    def inaccessible_callback(self, name, ** kwargs):
        flash("请先登录!")
        return redirect(url_for("login"))

# 在admin页面的导航添加注销按钮
class LogoutMenu(menu.MenuLink):
    def is_accessible(self):
        return current_user.is_authenticated

admin = Admin(app=app,name="Admin测试",endpoint="admin",template_mode="bootstrap4",index_view=MyAdminIndexView())
admin.add_view(MyModelView(Admin_Table,db.session))
admin.add_link(LogoutMenu(name="Logout",endpoint="logout"))


if __name__ == '__main__':
    db.create_all()
    app.run(debug=True)
posted @ 2022-03-10 18:08  左舷弹幕薄  阅读(921)  评论(0)    收藏  举报