欢迎来到Louis的博客

人生三从境界:昨夜西风凋碧树,独上高楼,望尽天涯路。 衣带渐宽终不悔,为伊消得人憔悴。 众里寻他千百度,蓦然回首,那人却在灯火阑珊处。
扩大
缩小

Flask - 装饰器使用,路由,实例化配置,对象配置,蓝图,特殊装饰器

一.视图函数多个装饰器报错问题

  在我们使用多个装饰器对视图函数进行装饰时,会出现以下报错:

AssertionError: View function mapping is overwriting an existing endpoint function: warrper

   我们会发现上有一个词是endpoint,也就是端点,在flask中路由到视图的过程中实际上还有一层,(URL-->endpoint-->viewfunction)从根本上来说,端点就是程序中一组逻辑处理单元的ID,该ID对应的代码决定了对此ID请求应该作出何种响应。通常,端点与视图函数同名,但是在当前的环境下,端点的名字必须是唯一的,这样在必要的时候就可以修改它了,

解决方案一:

from flask import Flask
from flask import (redirect,
                   render_template)
from flask import request, session
from flask import jsonify
from flask import send_file
from functools import wraps

app = Flask(__name__)
app.secret_key = 'asbhwui189asdnw'

account_info = {
    "alex": ["123", 'man', 30, 'CEO'],
    "zj": ["234", 'man', '27', 'student'],
    "wusir": ["123", 'man', '35', 'manager']
}

# 校验装饰器
def login_require(func):
    def warrper(*args, **kwargs):
        if not session.get('username'):
            return redirect('/login')
        return func(*args, **kwargs)
    return warrper

# 设置endpoint
@app.route("/", endpoint='index')
@login_require
def index():
    name = 'alex'
    return render_template('index.html', name=name)

# 设置endpoint
@app.route("/userinfo", endpoint='user_info')
@login_require
def user_info():
    return render_template('info.html', username=session.get('username'),
                           info=account_info[session.get('username')][1:])


@app.route("/login", methods=['GET', 'POST'])
def login():
    # 获取请求方式
    if request.method == 'GET':
        return render_template('login.html')
    # 获取formdata中的数据
    if request.form.get('username') in account_info.keys() and \
            request.form.get('password') == account_info[request.form.get('username')][0]:
        session['username'] = request.form.get('username')
        return redirect('/')
    else:
        return "用户名或密码错误!"

  上面的代码中我们使用了login_require装饰器,多个装饰器,在执行过程中是先执行login_require,返回了warrper的函数对象,然后再使用app.route对warrper进行装饰,这个过程中如果不指定endpoint,当前的视图endpoint就会成为warrper,多个视图函数使用了login_require就会出现endpoint重名的情况,也就出现了上面的报错,所以直接指定endpoint就可以解决这个问题。

解决方案二:

  使用functool工具

from functools import wraps

def login_require(func):
    @wraps(func)
    def warrper(*args, **kwargs):
        if not session.get('username'):
            return redirect('/login')
        return func(*args, **kwargs)
    return warrper

  使用这种方法之后就无须再指定endpoint了。

二.Flask中的路由

1.endpoint

  看下面两段代码:

@app.route('/greeting/<name>')
def give_greeting(name):
    return 'Hello, {0}!'.format(name)

  注意,add_url_rule函数实现了同样的目的,只不过没有使用装饰器,因此,下面的程序是等价的:

# 抬头没有使用路由装饰器,我们在最后用另一种方法添加路由.
def give_greeting(name):
    return 'Hello, {0}!'.format(name)

app.add_url_rule('/greeting/<name>', 'give_greeting', give_greeting)
    def route(self, rule, **options):
        def decorator(f):
            endpoint = options.pop('endpoint', None)
            self.add_url_rule(rule, endpoint, f, **options)
            return f
        return decorator
route源码

 

   我们看route的源码时,发现也是调用了app.add_url_rule方法

  而 add_url_rule()中3个参数依次是rule、view_func、endpoint.

  当用户输入url请求过来的时候,拿着这个url地址到路由表中做匹配,flask发现这个地址指向了某个视图函数。
  然而,当我们用这种最常用的方法创建视图时,flask却向我们隐藏了一些其他的细节信息。在这个场景中,flask并没有直接从URL地址跳转到应该响应它请求的视图函数上:

  事实上,这里还有另一个步骤--把URL地址映射到端点上(URL-->endpoint-->viewfunction)

  从根本上来说,端点就是程序中一组逻辑处理单元的ID,该ID对应的代码决定了对此ID请求应该作出何种响应

2.endpoint有什么作用(url_for)

  端点通常用作反向查询URL地址(viewfunction-->endpoint-->URL)。例如,在flask中有个视图,你想把它关联到另一个视图上(或从站点的一处连接到另一处)。不用去千辛万苦的写它对应的URL地址,直接使用URL_for()就可以啦:

from flask import Flask
from flask import url_for

app = Flask(__name__)


@app.route('/home', endpoint='myhome')
def home():
    get_path()
    return 'Home'


def get_path():
# url_for 通过端点查找对应的视图函数路由名称
    print(url_for('myhome'))
    # 打印 /home

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

 

3.methods(请求方式)

@app.route('/home', methods=['get', 'post'])
def home():
    return 'Home'


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

 

  在设置路由时,还有指定只处理哪些类型的请求,

  注意:这里如果不指定methods默认时只支持get方式的请求,如果指定了methods,则只处理该参数中选项的请求。

4.默认参数(后续补充)

defaults={"nid":"123456"} 默认参数

5.路由严格模式

strict_slashes=True #是否严格遵循路由地址

 

from flask import Flask

app = Flask(__name__)


@app.route('/home', methods=['get', 'post'], strict_slashes=True)
def home():
    return 'Home'

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

  不设置该参数,默认值为flase,是非严格模式,可以匹配/home和/home/的路由,设置了严格模式之后就只能匹配/home。

6.永久重定向

  redirect_to="/login" 永久重定向 301

from flask import Flask

app = Flask(__name__)

# 重定向到百度
@app.route('/home', methods=['get', 'post'], redirect_to='http://www.baidu.com')
def home():
    return 'Home'


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

7.动态路由匹配

  在日常访问的url中,会发现很多地址中都包含可变部分,在flask中,可以直接在路由上设置动态路由匹配。

@app.route('/book/<page>')
def book(page):
    return page

 

  上面代码中的page就是动态可变的,默认的是string类型,如果你想让接收的字符是数字可以写成<int:page>的形式。

 

三.flask实例化配置

   1.template_folder="temp" 默认模板路径 templates
    2.static_folder="static", 默认静态文件路径 static
    3.static_url_path="/static" 访问静态文件路由地址 默认是"/"+static_folder
    
    4.static_host=None 指定静态文件服务器地址
    5.host_matching = False,  # 如果不是特别需要的话,慎用,否则所有的route 都需要host=""的参数
    6.subdomain_matching = False,  # 理论上来说是用来限制SERVER_NAME子域名的,但是目前还没有感觉出来区别在哪里
    7.instance_path = None,  # 指向另一个Flask实例的路径
    8.instance_relative_config = False  # 是否加载另一个实例的配置
    9.root_path = None  # 主模块所在的目录的绝对路径,默认项目目录

   示例:

from flask import Flask, render_template

# 设置模板目录,设置静态文件目录,设置静态文件访问路由url前缀
app = Flask(__name__, template_folder='tmp_template', static_folder='tmp_static', static_url_path='/static')


@app.route('/index', methods=['get', 'post'])
def home():
    return render_template('index.html')


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

 

四.flask对象配置 

    'DEBUG': False,  # 是否开启Debug模式
    'TESTING': False,  # 是否开启测试模式
    'SECRET_KEY': None # 在启用Flask内置Session的时候/开启flash,一定要有它
    'PERMANENT_SESSION_LIFETIME': 31,  # days , Session的生命周期(天)默认31天
    'SESSION_COOKIE_NAME': 'session',  # 在cookies中存放session加密字符串的名字

 

  使用对象的方式来对app对象进行配置

class FlaskDebug(object):
    DEBUG = True
    SECRET_KEY = "DEBUGmoshidesecret_key"
    PERMANENT_SESSION_LIFETIME = 7
    SESSION_COOKIE_NAME = "debug_session"


class FlaskProduct(object):
    DEBUG = False
    SECRET_KEY = "Dsecret_key&*(JKjk^VJ"
    PERMANENT_SESSION_LIFETIME = 30
    SESSION_COOKIE_NAME = "session"
from flask import Flask, render_template
from app_config import FlaskDebug, FlaskProduct

# 设置模板目录,设置静态文件目录,设置静态文件访问路由url前缀
app = Flask(__name__, template_folder='tmp_template', static_folder='tmp_static', static_url_path='/static')

# app.config.from_object
app.config.from_object(FlaskProduct)


@app.route('/index', methods=['get', 'post'])
def home():
    return render_template('index.html')


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

 

 五.蓝图(blueprint)

  可以把Blueprint 当成一个不能被启动的 app Flask示例

create.views

from flask import Blueprint, render_template

# url_prefix 路由前缀,必须为路由地址要加'/'
create_app = Blueprint("create", __name__, template_folder='template', url_prefix='/create')

@create_app.route('/create')
def create():
    return render_template('create.html')

s1

from flask import Flask
from create import views

app = Flask(__name__)
# 将蓝图注册到app中
app.register_blueprint(views.create_app)

app.run(debug=True)

   使用蓝图,可以发现几乎跟实例化一个flask对象是相同的语法,使用蓝图可以让程序结构更加清晰,每一个蓝图都实现预期的功能,也有助于后期代码维护。

六.flash(待补充)

from flask import flash,get_flashed_messages

flash("message","tag")
get_flashed_messages()

 

七.flask特殊装饰器(类似与django中间件)

from flask import Blueprint, render_template

create_app = Blueprint("create", __name__, template_folder='template', url_prefix='/create')


@create_app.route('/create')
def create():
    return render_template('create.html')


# before_request 处理请求前,如果return不是None,则直接跳过视图函数
@create_app.before_request
def be1():
    print('be1')


@create_app.before_request
def be2():
    print('be2')


@create_app.before_request
def be3():
    print('be3')

# 请求处理之后,函数必须接收一个返回值(响应体),必须有一个返回值(响应体),
@create_app.after_request
def af1(res):
    print('af1')
    return res


@create_app.after_request
def af2(res):
    print('af2')
    return res


@create_app.after_request
def af3(res):
    print('af3')
    return res

@app.before_request # 请求进入视图函数之前
@app.after_request # 响应返回客户端之前
正常情况下流程:be1 - be2 - be3 - af3 - af2 - af1
异常情况下流程:be1 - af3 - af2 - af1

@app.errorhandler(404) # 重定义错误页面返回信息

from flask import Flask
from create import views

app = Flask(__name__)
app.register_blueprint(views.create_app)

# errorhandker只能使用flask实例设置,蓝图设置无效,接收一个参数(错误信息),返回一个响应对象。
# 404表示捕获的错误code,也可以定义405等其他的错误码
@app.errorhandler(404)
def error404(args):
    print(args)
    return '页面被吃掉啦!'


app.run(debug=True)

   这些特殊的装饰器,我们可以用于身份校验,限流等全局策略。

posted on 2019-01-09 17:23  Louiszj  阅读(315)  评论(0)    收藏  举报

导航