在app.py中需要添加下面两个勾子函数
1.before_request
from flask import Flask, session, g
@app.before_request
def my_before_request():
user_id = session.get("user_id")
if user_id:
user = UserModel.query.get(user_id)
setattr(g, "user", user)
else:
setattr(g, "user", None)
- 这个钩子在每个请求处理之前被调用,也就是说,每当有一个新的请求到达 Flask 应用时,
before_request
钩子函数就会被执行。 - 它通常用于执行一些请求前需要处理的通用任务,比如身份验证、权限检查、设置请求上下文等。
- 在代码中,
my_before_request
函数首先尝试从session
中获取user_id
。session
是 Flask 的一个全局对象,用于在请求之间存储信息。 - 如果
user_id
存在,函数会尝试从数据库中获取对应的用户对象,并将其设置到 Flask 的g
对象中。g
对象是一个全局对象,用于在应用上下文中存储临时数据,它在请求处理过程中是持久的。 - 如果
user_id
不存在,g.user
被设置为None
,表示当前没有用户登录。 - 这个钩子函数的目的是确保每个请求处理函数都可以访问到当前用户的信息,而不需要每次都去查询。
2.context_processor
from flask import Flask, session, g
@app.context_processor
def my_context_processor():
return {"user": g.user}
- 这个钩子用于模板上下文处理,它允许你在渲染模板之前向模板上下文中添加额外的变量。
- 模板上下文处理器在每次渲染模板之前被调用,可以将一些全局变量或函数添加到模板的上下文中,这样在模板中就可以直接使用这些变量或函数。
- 在代码中,
my_context_processor
函数返回一个字典,其中包含了g.user
。这意味着在任何模板中,都可以直接使用user
变量,而不需要在每个模板中都显式地传递它。 - 这样做的好处是简化了模板的编写,因为不需要在每个模板中都传递用户信息,而且可以在模板中直接使用
user
变量,提高了代码的可读性和可维护性。
3.登录
更改base.html,如果用户已经登录了显示用户和退出登录,如果未登录才显示登录和注册
<ul class="navbar-nav">
{% if user %}
<li class="nav-item">
<span class="nav-link">{{ user.username }}</span>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('auth.logout') }}">退出登录</a>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('auth.login') }}">登录</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('auth.register') }}">注册</a>
</li>
{% endif %}
</ul>
4.退出登录
上面已经吧退出登录的url制作了,下面需要完善下退出登录的函数
在auth.py中增加退出登录函数
退出登录就是把session清理下就行了
@bp.route("logout")
def logout():
session.clear()
return redirect("/")
测试登录与未登录
登录的状态
点击退出登录,会跳转到首页
再次访问http://127.0.0.1:5000/auth/login
会提示没有登录
app.py完整代码
from flask import Flask,session,g
import config
from exts import db,mail
from blueprints.qa import bp as qa_bp
from blueprints.auth import bp as auth_bp
from models import UserModel
from flask_migrate import Migrate
app = Flask(__name__)
app.config.from_object(config)
db.init_app(app)
mail.init_app(app)
#将模型映射到数据库中
migrate = Migrate(app,db)
#注册蓝图
app.register_blueprint(qa_bp)
app.register_blueprint(auth_bp)
@app.before_request
def my_before_request():
user_id = session.get("user_id")
if user_id:
user = UserModel.query.get(user_id)
setattr(g, "user", user)
else:
setattr(g, "user", None)
@app.context_processor
def my_context_processor():
return {"user": g.user}
if __name__ == '__main__':
app.run()
auth.py完整代码
from flask import Blueprint,render_template,jsonify,redirect,url_for,session
from exts import mail,db
from flask_mail import Message
from flask import request
import string,random
from models import EmailCaptchaModel,UserModel
from .forms import Registerform,Loginform
from werkzeug.security import generate_password_hash,check_password_hash
# /auth
bp = Blueprint("auth",__name__,url_prefix="/auth")
@bp.route("/login",methods=['GET','POST'])
def login():
if request.method == "GET":
return render_template("login.html")
else:
form = Loginform(request.form)
if form.validate():
email = form.email.data
password = form.password.data
user = UserModel.query.filter_by(email=email).first()
if not user:
print("邮箱在数据库中不存在!")
return redirect(url_for("auth.login"))
#检查密码是否正确
if check_password_hash(user.password,password):
#存储session
#flask中的session,是经过加密后存储到cookie中的
session['user_id'] = user.id
return redirect("/")
else:
print("密码错误!")
return redirect(url_for("auth.login"))
else:
print(form.errors)
return redirect(url_for("auth.login"))
@bp.route("/register",methods=['GET','POST']) #即可以使用GET请求,也可以使用POST请求
def register():
if request.method == "GET":
return render_template("register.html")
else:
#表单验证,flask-wtf:wtfarms
#验证用户提交的邮箱和验证是否正确
form = Registerform(request.form)
if form.validate():
email = form.email.data
username = form.username.data
password = form.password.data
user = UserModel(email=email,username=username,password=generate_password_hash(password))
db.session.add(user)
db.session.commit()
return redirect(url_for("auth.login"))
else:
print(form.errors)
return redirect(url_for("auth.register"))
@bp.route("logout")
def logout():
session.clear()
return redirect("/")
@bp.route("/captcha/email")
def get_email_captcha():
# /capthca/email/<email>
# /capthca/email?email=xxx@qq.com
email = request.args.get("email")
source = string.digits*4
captcha = random.sample(source,4)
captcha = "".join(captcha)
message = Message(subject="问答平台注册验证码", recipients=[email], body=f"你的验证码是:{captcha}")
mail.send(message)
#存储验证码到数据库
email_captcha = EmailCaptchaModel(email=email,captcha=captcha)
db.session.add(email_captcha)
db.session.commit()
# RESTful API
result = {"code":200,"message":"","data":None}
return jsonify(result)
@bp.route("/mail/test")
def mail_test():
message = Message(subject="邮箱测试",recipients=["507303922@qq.com"],body="这是一条测试邮件")
mail.send(message)
return "邮件发送测试"