# 测试函数
@blog_bp.route('/test', endpoint='test')
def user_test():
print('===============>user_test')
# abort(404)
# abort(500)
return 'OK'
@blog_bp.before_app_first_request
def first_test():
print('------------------》 first_test')
@blog_bp.before_app_request
def process_request():
# print('+++++++++++++++> before_app_request')
# print(request.path)
if request.path in required_login:
# 需要进行登录验证
username = session.get('uname')
if not username:
return render_template('login.html', msg='用户未登录,请登录!')
@blog_bp.after_app_request
def process_response(response):
print('*****************> after_app_request')
# response = make_response('error')
return response
article_view.py
@article_bp.before_app_request
def process_request():
if request.path=='/article/add':
username=session.get('uname')
if not username:
return render_template('login.html',,sg='用户未登录,请登录!')
abort() flask和os中都有,用于中止报错
处理错误页面
@blog_bp.errorhandler(404)
def handler_404(error):
print(error)
return render_template('404.html')
@blog_bp.errorhandler(500)
def handler_500(error):
print(error) # error 表示的是error对象
return render_template('500.html')
分页
@blog_bp.route('/', endpoint='index')
def index():
# 获取页码数 设置默认值
page = request.args.get('page', 1)
# 分页器对象
paginate = Article.query.paginate(page=int(page), per_page=3)
print(paginate.page)
return render_template('index.html', paginate=paginate)
分页:
SQLAlchemy里面封装好的:Pagination类
# 分页器对象
paginate = Article.query.paginate(page=page, per_page=3) —》 paginate对象
属性:
print(paginate.pages) # 一共多少页
print(paginate.has_next) # True
print(paginate.has_prev) # False
print(paginate.next_num) # 获取下一页的页码数
print(paginate.prev_num) # 获取上一页的页码数
print(paginate.items) # 获取当前页码的数据对象
print(paginate.page) 当前页码数
flask第三方插件:Flask-WTF
pip install flask-wtf 表单验证
功能:
集成 wtforms。
带有 csrf 令牌的安全表单。
全局的 csrf 保护。
支持验证码(Recaptcha)。
与 Flask-Uploads 一起支持文件上传。
国际化集成。
pip install flask-uploads 文件上传
步骤:
forms/user_forms.py
from flask_wtf.file import FileRequired, FileAllowed
from wtforms import StringField, PasswordField, Form, DateTimeField, FileField, IntegerField
from wtforms.validators import Length, EqualTo
class RegisterForm(Form):
username = StringField(label='用户名',
validators=[Length(max=12, min=6, message='用户名长度必须在6~12位之间')]) # ----> < input type='text' >
password = PasswordField(label='密码', validators=[Length(max=12, min=6, message='密码长度必须在6~12位之间')])
repassword = PasswordField(label='确认密码',
validators=[Length(max=12, min=6, message='密码长度必须在6~12位之间'),
EqualTo('password', message='两次密码不一致')])
phone = StringField(label='手机号码', validators=[Length(max=11, min=11, message='手机号码必须11位')])
class UserForm(Form):
id = IntegerField(label='id')
username = StringField(label='用户名',
validators=[Length(max=12, min=6, message='用户名长度必须在6~12位之间')])
phone = StringField(label='手机号码', validators=[Length(max=11, min=11, message='手机号码必须11位')])
rdatetime = DateTimeField(label='注册时间')
icon = FileField(label='用户头像', validators=[FileAllowed(['jpg', 'png'], 'Images only!')])
视图函数
from werkzeug.security import generate_password_hash, check_password_hash
@blog_bp.route('/register', endpoint='register', methods=['GET', 'POST'])
def user_register():
if request.method == 'GET':
rform = RegisterForm() # 创建表单类对象
return render_template('register1.html', rform=rform)
else:
rform = RegisterForm(request.form)
if rform.validate(): # 表示所有的验证是通过的
username = rform.data.get('username')
password = rform.data.get('password')
phone = rform.data.get('phone')
# 加密
password = generate_password_hash(password)
# 保存到数据库中
# 1. 创建模型对象
user = User()
# 2. 给对象赋值
user.username = username
user.password = password
user.phone = phone
# 3. 向数据库提交数据
db.session.add(user)
db.session.commit()
return redirect(url_for('blog.index'))
else:
return render_template('register1.html', rform=rform)
注册页面
{
% extends 'base.html' %}
{
% import 'bootstrap/wtf.html' as wtf %}
{
% block title %} 用户注册 {
% endblock %}
{
% block content %}
<div class="container">
<p style="color:red;">{
{
msg}}</p>
<p style="color:red;">{
% if rform.errors %}
{
% for error in rform.errors.values() %}
<div>{
{
error.0}}</div>
{
% endfor %}
{
% endif %}
</p>
<form action="{
{ url_for('blog.register') }}" method="post">
<input type="hidden" name="csrf_token" value="{
{csrf_token()}}">
{
{
rform.username.label(class=" control-label")}} {
{
rform.username}} <span>{
% if rform.username.errors %} {
{
rform.username.errors.0}} {
%endif%}</span>
<br>
{
{
rform.password.label(class="control-label")}} {
{
rform.password}} <span> {
% if rform.password.errors %} {
{
rform.password.errors.0}} {
%endif%}</span>
<br>
{
{
rform.repassword.label(class="control-label")}} {
{
rform.repassword}} <span> {
% if rform.repassword.errors %} {
{
rform.repassword.errors.0}} {
%endif%}</span>
<br>
{
{
rform.phone.label(class="control-label")}} {
{
rform.phone}} <span>{
% if rform.phone.errors %} {
{
rform.phone.errors.0}} {
%endif%}</span>
<br>
<button type="submit" class="btn btn-info">用户注册</button>
</form>
<hr>
</div>
{
% endblock %}
使用表单类:
在视图函数中使用:
1. 创建表单对象
2. 将表单对象渲染到模板页面
3. 在模板页面上使用: { { rform.username}} ----> < input type=‘text’ …>
4. 提交表单:
post请求的时候:
将post提交的数据封装到表单对象中
rform = RegisterForm(request.form)
5. 对象rform进行验证:
rform.validator() ----> 返回值True,False
True: 验证通过
6. 进行用户注册 ----》 密码加密:from werkzeug.security import generate_password_hash, check_password_hash
password = generate_password_hash(password) ----> 数据库的字段长度要大
7. 首页跳转
False: 验证不通过
6. 再次回到注册页面
7. 在注册页面获取错误信息
{ { rform.errors }} ------->字典结构
Cross-site request forgery
CSRF攻击攻击原理及过程如下:
1. 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
2.在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
2. 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
3. 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
4. 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
exit/__init__.py
from flask_wtf import CsrfProtect
csrf = CsrfProtect()
apps/__init__.py
def create_app():
csrf.init_app(app)
register.html
<form action="{
{ url_for('blog.register') }}" method="post">
<input type="hidden" name="csrf_token" value="{
{csrf_token()}}">
user_form.py
class UserForm(Form):
id = IntegerField(label='id')
username = StringField(label='用户名',
validators=[Length(max=12, min=6, message='用户名长度必须在6~12位之间')])
phone = StringField(label='手机号码', validators=[Length(max=11, min=11, message='手机号码必须11位')])
rdatetime = DateTimeField(label='注册时间')
icon = FileField(label='用户头像', validators=[FileAllowed(['jpg', 'png'], 'Images only!')])
# 用户详情
@blog_bp.route('/udetial', endpoint='udetail')
def user_detail():
uname = session.get('uname')
user = User.query.filter(User.username == uname).first()
if user:
# 用户的详情页
uform = UserForm(obj=user)
return render_template('user_detail.html', uform=uform, user=user)
user_detail.html
{
% extends 'base.html' %}
{
% block title %} 用户详情 {
% endblock %}
{
% block content %}
<div class="container">
<p style="color:red;">{
{
msg}}</p>
<form action="{
{ url_for('blog.update') }}" method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="{
{csrf_token()}}">
<div class="form-group">
<label>用户名</label>
{
{
uform.username}}
</div>
<div class="form-group">
<label>手机号码</label>
{
{
uform.phone }}
</div>
<div class="form-group">
<label>注册时间</label>
{
{
uform.rdatetime}}
</div>
<div class="form-group">
<label>用户头像</label>
{
{
uform.icon}} <img src="{% if not user.icon %} {
{url_for('static',filename='upload/default.jpg')}} {%else%} {
{url_for('static',filename=user.icon)}} {%endif%}" alt="" class="img-circle">
</div>
<input type="hidden" name="id" value="{
{user.id}}">
<button type="submit" class="btn btn-info">更新用户</button>
</form>
</div>
{
% endblock %}