Flask

Flask简介:

  Flask是一个Python编写的Web 微框架,让我们可以使用Python语言快速实现一个网站或Web服务,在介绍Flask之前首先来聊下它和Django的联系以及区别,django个大而全的web框架,它内置许多模块,flask是一个小而精的轻量级框架,Django功能大而全,Flask只包含基本的配置 Django的一站式解决的思路,能让开发者不用在开发之前就在选择应用的基础设施上花费大量时间。Django有模板,表单,路由,认证,基本的数据库管理等等内建功能。与之相反,Flask只是一个内核,默认依赖于两个外部库: Jinja2 模板引擎和 Werkzeug WSGI 工具集,其他很多功能都是以扩展的形式进行嵌入使用,相比于其他Web框架例如Django,更加灵活也更加简洁,如下几行代码就可以写出一个小程序:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello Flask!'

if __name__ == '__main__':
    app.run()

接下来我们按照执行顺序来了解下Flask

配置文件

app.config.from_object("settings.DevelopmentConfig") 


 

路由

  路由通过使用Flask的app.route装饰器来设置

动态路由(url传参)

@app.route('/user/<username>')
def show_user_profile(username):
    # show the user profile for that user
    return 'User %s' % username

@app.route('/post/<int:post_id>')
def show_post(post_id):
    # show the post with the given id, the id is an integer
    return 'Post %d' % post_id

如果希望获取/article/1这样的路径参数,就需要使用路径变量。路径变量的语法是/path/<converter:varname>。在路径变量前还可以使用可选的转换器,有以下几种转换器。

转换器作用
string 默认选项,接受除了斜杠之外的字符串
int 接受整数
float 接受浮点数
path 和string类似,不过可以接受带斜杠的字符串
any 匹配任何一种转换器
uuid 接受UUID字符串

 

 

 

 

 

 

 

HTTP方法

如果需要处理具体的HTTP方法,在Flask中也很容易,使用route装饰器的methods参数设置即可。

  • GET 将未经加密的信息发送的服务器. 是最通用的方法.
  • HEAD 该方法除了服务端不返回响应内容只返回头信息之外, 同GET是一样的.
  • POST 用于向服务器发送HTML表单数据. POST请求不会被缓存.
  • PUT 将URL所指示的资源用上传的内容替换.
  • DELETE 将URL所指示的资源删除.
from flask import request,render_template

@app.route('/login', methods=['GET', 'POST'])    #如果不加methods,Flask路由默认处理的是GET请求
def login():
    if request.method == 'POST':
        do_the_login()
    else:
        return render_template('xxx.html')

 

url_for 和 endpoint

from flask import Flask,url_for
app=Flask(__name__)
@app.route('/<path:url>',endpoint='XXX')        #endpoint默认为函数名
def demo(url):
    print(url_for('XXX',url=url)) #如果设置了url参数,url_for(别名,加参数)
    return 'Hello World'

if __name__ == '__main__':
    app.run()

 

处理请求

Request 对象

Request 对象是一个全局对象,利用它的属性和方法,我们可以方便的获取从页面传递过来的参数。

request.method 获取请求方法
request.form 获取POST类型的表单提交的数据和ajax请求的数据
request.args 获取get请求参数 
request.values 获取GET和POST请求携带的所有参数(GET/POST通用)
request.cookies 获取cookies信息
request.headers 获取请求头信息
request.path 获取cookies信息
request.full_path 获取获取用户访问的完整url地址+参数 例如(/index/?page=1)
request.url 获取完整路径
request.base_url 获取访问url地址(不获取参数),例如 http://127.0.0.1:5000/;
request.files 获取用户上传的文件

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

响应处理

 

  • 返回字符串   ----return “asdf”    
  • 返回模板       ----return render_template('xxx.html')   
  • 返回重定向   ----return redirect(' /')
  • 返回字典       ----jsonify({'k1':'v1'})

  默认的错误页面是一个空页面,如果需要自定义错误页面,可以使用errorhandler装饰器。

from flask import render_template

@app.errorhandler(404)
def page_not_found(error):
    return render_template('page_not_found.html'), 404

  我们也可以自己决定如何设置响应对象,方法也很简单,使用make_response函数即可。

@app.errorhandler(404)
def not_found(error):
    resp = make_response(render_template('error.html'), 404)
    resp.headers['X-Something'] = 'A value'
    return resp

 

session

    我们可以使用全局对象session来管理用户会话。Sesison 是建立在 Cookie 技术上的,不过在 Flask 中,我们还可以为 Session 指定密钥,这样存储在 Cookie 中的信息就会被加密,从而更加安全。

 

from flask import Flask, session, redirect, url_for, escape, request

app = Flask(__name__)
app.secret_key = 'qwertyuiop'
@app.route('/')
def index():
    if 'username' in session:
        return 'Logged in as %s' % escape(session['username'])
    return 'You are not logged in'

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        session['username'] = request.form['username']
        return redirect(url_for('index'))
    return '''
        <form method="post">
            <p><input type=text name=username>
            <p><input type=submit value=Login>
        </form>
    '''
@app.route('/logout')
def logout():
    # remove the username from the session if it's there
    session.pop('username', None)
    return redirect(url_for('index'))

 

Blueprint

  - 目录结构划分
  - 前缀
  - 特殊装饰

- 某一类url添加前缀url_prefix
@h.route('',methods=[],url_prefix='')
- 给一类url添加before_request

 

 

 

 

 

特殊装饰器

app.before_request 修饰器在开发中用处非常大,比如判断某个ip是否有恶意访问行为,从而进行拦截等操作。

app.after_request 修饰器是在用户请求得到函数响应后被执行,不过需要注意的是这个执行是在函数返回数据前被调用,即请求已经被app.route修饰的函数响应过了,已经形成了response,但还未返回给用户的时候,调用的。

from flask import Flask, Request, render_template
app = Flask(__name__, template_folder='templates')
app.debug = True


@app.before_first_request  #第1个请求到来执行
def before_first_request():
    print('before_first_request')


@app.before_request     #在进入视图前执行
def before_request():
   if request.path=='/login':
      return None    if session['user']:
      return None
   return redirect('/login') 
   @app.errorhandler(404) def page_not_found(error): return 'This page does not exist', 404 @app.route('/index') def index(): return "Hello World" @app.after_request #视图之后执行 def after_request1(response): print('after_request1', response) return response @app.after_request #先执行 after_request2 def after_request2(response): print('after_request2', response) return response if __name__ == '__main__': app.run()

 

模板

  使用方式和Django的模板渲染类似:{{ }} 和 {% %}。

  基本方法不太一样,函数需要加括号执行,类似于python的语法:

    - {{ dict.get() }} 或 {{ dict["key"] }}

    - {{ list[0] }}

    - {% for 循环 %} {% endfor %}

    - {% if 判断 %} {% endif %}

  还可以自定义全局函数,在模板中使用:

@app.template_global()
def sum(a1, a2):
    return a1 + a2
# 模板中使用{{ sum(2, 3) }}

# 类似的
@app.template_filter()
def sum(a1, a2, a3):
    return a1 + a2 + a3
# 调用方式不同{{ 1|sum(2,3) }}   

  模板继承:

{% extends "base.html"%}

{% block content %}
    自定义内容
{% endblock %}

{% include "组件.html" %}    

  定义宏:

{% macro input(name, type='text', value=' ') %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}

# 使用
<form>
{{ input('username') }}
{{ input('password', type="password") }}
{{ input(' ', type="submit", value="提交") }}
</form>

  安全:

# 前端
{{ data|safe }}

# 后端
from flask import Markup
data = Markup(data)

 

Flash

 

  flask中暂时储存数据的一个方式

from flask import flash, get_flashed_messages

# 储存数据
flash("临时数据", "name")

# 获取数据, 通过pop获取并移除
msg = get_flashed_messages(category_filter=["name"])

 

wtforms

  安装:pip3 install wtforms 

用户登录注册示例

1. 用户登录

当用户登录时候,需要对用户提交的用户名和密码进行多种格式校验。如:

用户不能为空;用户长度必须大于6;

密码不能为空;密码长度必须大于12;密码必须包含 字母、数字、特殊字符等(自定义正则);

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms.fields import core
from wtforms.fields import html5
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets

app = Flask(__name__, template_folder='templates')
app.debug = True


class LoginForm(Form):
    name = simple.StringField(
        label='用户名',
        validators=[
            validators.DataRequired(message='用户名不能为空.'),
            validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
        ],
        widget=widgets.TextInput(),
        render_kw={'class': 'form-control'}

    )
    pwd = simple.PasswordField(
        label='密码',
        validators=[
            validators.DataRequired(message='密码不能为空.'),
            validators.Length(min=8, message='用户名长度必须大于%(min)d'),
            validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",
                              message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')

        ],
        widget=widgets.PasswordInput(),
        render_kw={'class': 'form-control'}
    )



@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        form = LoginForm()
        return render_template('login.html', form=form)
    else:
        form = LoginForm(formdata=request.form)
        if form.validate():
            print('用户提交数据通过格式验证,提交的值为:', form.data)
        else:
            print(form.errors)
        return render_template('login.html', form=form)

if __name__ == '__main__':
    app.run()
app
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms.fields import core
from wtforms.fields import html5
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets

app = Flask(__name__, template_folder='templates')
app.debug = True


class LoginForm(Form):
    name = simple.StringField(
        label='用户名',
        validators=[
            validators.DataRequired(message='用户名不能为空.'),
            validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
        ],
        widget=widgets.TextInput(),
        render_kw={'class': 'form-control'}

    )
    pwd = simple.PasswordField(
        label='密码',
        validators=[
            validators.DataRequired(message='密码不能为空.'),
            validators.Length(min=8, message='用户名长度必须大于%(min)d'),
            validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",
                              message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')

        ],
        widget=widgets.PasswordInput(),
        render_kw={'class': 'form-control'}
    )



@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        form = LoginForm()
        return render_template('login.html', form=form)
    else:
        form = LoginForm(formdata=request.form)
        if form.validate():
            print('用户提交数据通过格式验证,提交的值为:', form.data)
        else:
            print(form.errors)
        return render_template('login.html', form=form)

if __name__ == '__main__':
    app.run()
login.html

2.注册

from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms.fields import core
from wtforms.fields import html5
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets

app = Flask(__name__, template_folder='templates')
app.debug = True



class RegisterForm(Form):
    name = simple.StringField(
        label='用户名',
        validators=[
            validators.DataRequired()
        ],
        widget=widgets.TextInput(),
        render_kw={'class': 'form-control'},
        default='alex'
    )

    pwd = simple.PasswordField(
        label='密码',
        validators=[
            validators.DataRequired(message='密码不能为空.')
        ],
        widget=widgets.PasswordInput(),
        render_kw={'class': 'form-control'}
    )

    pwd_confirm = simple.PasswordField(
        label='重复密码',
        validators=[
            validators.DataRequired(message='重复密码不能为空.'),
            validators.EqualTo('pwd', message="两次密码输入不一致")
        ],
        widget=widgets.PasswordInput(),
        render_kw={'class': 'form-control'}
    )

    email = html5.EmailField(
        label='邮箱',
        validators=[
            validators.DataRequired(message='邮箱不能为空.'),
            validators.Email(message='邮箱格式错误')
        ],
        widget=widgets.TextInput(input_type='email'),
        render_kw={'class': 'form-control'}
    )

    gender = core.RadioField(
        label='性别',
        choices=(
            (1, ''),
            (2, ''),
        ),
        coerce=int
    )
    city = core.SelectField(
        label='城市',
        choices=(
            ('bj', '北京'),
            ('sh', '上海'),
        )
    )

    hobby = core.SelectMultipleField(
        label='爱好',
        choices=(
            (1, '篮球'),
            (2, '足球'),
        ),
        coerce=int
    )

    favor = core.SelectMultipleField(
        label='喜好',
        choices=(
            (1, '篮球'),
            (2, '足球'),
        ),
        widget=widgets.ListWidget(prefix_label=False),
        option_widget=widgets.CheckboxInput(),
        coerce=int,
        default=[1, 2]
    )

    def __init__(self, *args, **kwargs):
        super(RegisterForm, self).__init__(*args, **kwargs)
        self.favor.choices = ((1, '篮球'), (2, '足球'), (3, '羽毛球'))

    def validate_pwd_confirm(self, field):    #局部钩子,全局钩子为 def validate()
        """
        自定义pwd_confirm字段规则,例:与pwd字段是否一致
        :param field: 
        :return: 
        """
        # 最开始初始化时,self.data中已经有所有的值

        if field.data != self.data['pwd']:
            # raise validators.ValidationError("密码不一致") # 继续后续验证
            raise validators.StopValidation("密码不一致")  # 不再继续后续验证


@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'GET':
        form = RegisterForm(data={'gender': 1})
        return render_template('register.html', form=form)
    else:
        form = RegisterForm(formdata=request.form)
        if form.validate():
            print('用户提交数据通过格式验证,提交的值为:', form.data)
        else:
            print(form.errors)
        return render_template('register.html', form=form)



if __name__ == '__main__':
    app.run()
register app
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>用户注册</h1>
<form method="post" novalidate style="padding:0  50px">
    {% for item in form %}
    <p>{{item.label}}: {{item}} {{item.errors[0] }}</p>
    {% endfor %}
    <input type="submit" value="提交">
</form>
</body>
</html>
register.html

 

flask-session组件

在app的__init__中

from flask import Flask
from flask_session import Session

# 第一步:导入并实例化SQLAlchemy
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()

from .views.account import ac
from .views.user import us

from .models import *

def create_app():
    app = Flask(__name__)
    app.config.from_object('settings.Config')

    app.register_blueprint(ac)
    app.register_blueprint(us)

    # Flask-Session: 第一步示例Session
    # Session(app)

    # 第三步:依赖app中的配置文件
    db.init_app(app)

    return app

在settings中

class Config(object): 
      #配置
  SESSION_TYPE = 'redis'   SESSION_REDIS = Redis(host='192.168.0.94', port='6379')
  SECRET_KEY='QWERT' #使用session,必须配置secret_key 否则会报错

 

 

posted @ 2018-06-19 20:47  排骨南  阅读(635)  评论(0编辑  收藏  举报