在注册和登录功能实现(1)中,我们已经获取到了页面POST过来的登录或者注册数据,接下来我们需要与数据库中的数据做验证,验证通过才能登录或者注册。我们平时在登录网站时,如果输入的用户名或者密码错误,有的网站是在登录框附近提示错误,也有的是跳转到一个页面提示出错,并经过几秒倒计时再返回原来的页面。
我们在后续做搜索功能的时候,用页面跳转来处理未找到结果的情形,这里就通过使用Flask的flash功能,直接在当前页面显示错误提示。简单来说,步骤就是在视图函数中flash一个字符串,在html模板中使用get_flashed_messages()去获取这个字符串,并显示在网页中。
首先,我们先新建一个exts.py,用于存放一些功能性的函数,在其中写一个去验证登录和注册信息的函数,如下:
from models import Users
def validate(username, password1, password2=None):
user = Users.query.filter(Users.username == username).first()
if password2:
if user:
return '用户名已经存在'
else:
if len(username) < 4:
return '用户名长度至少4个字符'
elif password1 != password2:
return '两次密码不一致'
elif len(password1) < 6:
return '密码长度至少6个字符'
else:
return '注册成功'
else:
if user:
if user.password == password1:
return '登录成功'
else:
return '密码错误'
else:
return '用户名不存在'
要使用flash功能,还得设置一个名为SECRET_KEY的参数,用于加密数据,我们在config.py中写进去,随便取个值SECRET_KEY = “THIS-A-SECRET-KEY”。然后在HarpQA.py中,从flask中导入flash,从exts.py中导入validate,修改register视图函数,如下:
from flask import Flask, render_template, request, flash
from models import db
from exts import validate
import config
app = Flask(__name__)
app.config.from_object(config)
db.init_app(app)
...
@app.route('/register/', methods=['GET', 'POST'])
def register():
if request.method == 'GET':
return render_template('register.html')
else:
username = request.form.get('username')
password1 = request.form.get('password1')
password2 = request.form.get('password2')
message = validate(username, password1, password2)
flash(message)
return render_template('register.html')
在视图函数中flash了message,接下来我们需要在html中显示它,我们再去修改base.html(这样对register.html和login.html都能起作用),在body区域尾部增加如下代码(直接从Flask官方文档复制修改的):
...
<nav><!-- 导航条内容 -->
...
</nav>
<div class="body-container">
{% block body_part %}
{% endblock %}
</div>
<div class="flash-message">
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
</div>
</body>
然后运行程序,访问注册页,随便输入几个试试,发现已经能用了:
只是不太美观,我们再用css调整一下,借助Boostrap中的警告框样式,最终结果如下:
登录也是同理,就不演示啦。
2017年1月17日补充内容:
当我们登录或注册出现问题,出现的警告框是如上图红色的,显然我们不希望登录或者注册成功的时候也是红色的,我们将提示成功的颜色设置为蓝色,然后传入一个参数给模板,告诉模板现在是成功还是失败的情况,然后在html中增加if,现在的html代码如下:
<div class="flash-message">
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for message in messages %}
{% if status == 'OK' %}
<li><div class="alert alert-info" role="alert">{{ message }}</div></li>
{% else %}
<li><div class="alert alert-danger" role="alert">{{ message }}</div></li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
{% endwith %}
</div>
那么status这个参数是怎么传递给模板呢,我们之前提到在render_templates函数中传入,现在我们使用@app.context_processor这个装饰器,它其实是上下文管理器,其装饰的函数返回的内容对所有html模板都起作用,用法如下:
@app.context_processor
def my_context_processor():
status = session.get('status', '')
return {'status': status}
将其放在HarpQA.py中,它返回一个字典,在任意html中使用{{ key }},就能得到这个字典key对应的value。那么session是什么?在现在这个case中,我们简单理解其为一个保存数据的容器,在登录和注册的视图函数中,验证完账号密码之后,将验证结果的信息存入session,例如注册函数修改如下:
@app.route('/register/', methods=['GET', 'POST'])
def register():
if request.method == 'GET':
return render_template('register.html')
else:
username = request.form.get('username')
password1 = request.form.get('password1')
password2 = request.form.get('password2')
message = validate(username, password1, password2)
flash(message)
if message == '注册成功':
session['status'] = 'OK'
return redirect(url_for('login'))
else:
session['status'] = 'BAD'
return render_template('register.html')
如果注册成功,就向session中写入status,值为’OK’,反之则为’BAD’。这样html模板就能根据status显示不同的颜色了。flask中还有个g对象也可用于保存和共享数据,但g对象是基于每一个请求的,不能跨请求使用。我们注册成功之后,要跳转到登录页,此时g就无法使用了,而session是基于这一次http连接的,不同请求都能使用。
实际上更简单的方法是,我们直接对传入的{{ message }}进行判断,如果带有’成功’字符串,就显示蓝色,否则就显示红色。上文主要是为了说明@app.context_processor这个装饰器,以及session和g对象的区别。