python-flask(五)

钩子函数

作用:发表文章时的登录验证,可以使用钩子函数或者装饰器。
python-flask(五)_第1张图片

# 测试函数
@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

python-flask(五)_第2张图片

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 文件上传
步骤:

  1. 定义表单类继承Form,并定义表单字段 StringField PasswordFiled
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}} &nbsp;&nbsp; <span>{
     % if rform.username.errors %} {
     {
     rform.username.errors.0}} {
     %endif%}</span>
        <br>
        {
     {
     rform.password.label(class="control-label")}} {
     {
     rform.password}} &nbsp;&nbsp;<span> {
     % if rform.password.errors %} {
     {
     rform.password.errors.0}} {
     %endif%}</span>
        <br>
        {
     {
     rform.repassword.label(class="control-label")}} {
     {
     rform.repassword}} &nbsp;&nbsp;<span> {
     % if rform.repassword.errors %} {
     {
     rform.repassword.errors.0}} {
     %endif%}</span>
        <br>
        {
     {
     rform.phone.label(class="control-label")}} {
     {
     rform.phone}} &nbsp;&nbsp; <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 }} ------->字典结构

CSRF 跨站请求伪造

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 %}


你可能感兴趣的:(flask,python,flask)