博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Flask基础
阅读量:4708 次
发布时间:2019-06-10

本文共 14921 字,大约阅读时间需要 49 分钟。

配置

django中的配置是通过settings.py文件指定的,flask的配置是通过app.config加载的。app.config是一个继承于字典的对象,在字典之上还封装了一些其它的方法。

默认配置如下

default_config = ImmutableDict({        'DEBUG':                                get_debug_flag(default=False),        'TESTING':                              False,        'PROPAGATE_EXCEPTIONS':                 None,        'PRESERVE_CONTEXT_ON_EXCEPTION':        None,        'SECRET_KEY':                           None,        'PERMANENT_SESSION_LIFETIME':           timedelta(days=31),        'USE_X_SENDFILE':                       False,        'LOGGER_NAME':                          None,        'LOGGER_HANDLER_POLICY':               'always',        'SERVER_NAME':                          None,        'APPLICATION_ROOT':                     None,        'SESSION_COOKIE_NAME':                  'session',        'SESSION_COOKIE_DOMAIN':                None,        'SESSION_COOKIE_PATH':                  None,        'SESSION_COOKIE_HTTPONLY':              True,        'SESSION_COOKIE_SECURE':                False,        'SESSION_REFRESH_EACH_REQUEST':         True,        'MAX_CONTENT_LENGTH':                   None,        'SEND_FILE_MAX_AGE_DEFAULT':            timedelta(hours=12),        'TRAP_BAD_REQUEST_ERRORS':              False,        'TRAP_HTTP_EXCEPTIONS':                 False,        'EXPLAIN_TEMPLATE_LOADING':             False,        'PREFERRED_URL_SCHEME':                 'http',        'JSON_AS_ASCII':                        True,        'JSON_SORT_KEYS':                       True,        'JSONIFY_PRETTYPRINT_REGULAR':          True,        'JSONIFY_MIMETYPE':                     'application/json',        'TEMPLATES_AUTO_RELOAD':                None,    })

想更改配置有如下几个方法:

方式一:
利用继承自字典的特性

app.config['DUBUG'] = True或者app.config.update(map)

方式二:

从文件中读,文件名不一定是以.py结尾

settings.cfg:DEBUG=Trueapp.config.from_pyfile("settings.cfg")

方式三:

从对象中读

app.config.from_object('flask_test.settings.TestingConfig') class Config(object):    DEBUG = False    TESTING = False    DATABASE_URI = 'sqlite://:memory:'class ProductionConfig(Config):    DATABASE_URI = 'mysql://user@localhost/foo'class DevelopmentConfig(Config):    DEBUG = Trueclass TestingConfig(Config):    TESTING = True

常用的就是方式二和方式三,当然还有其他不常用的方式:

app.config.from_json("json文件名称"):  JSON文件名称,必须是json格式,因为内部会执行json.loads app.config.from_mapping({'DEBUG':True}):  字典格式

在这里需要注意一点:我们给flask应用设置的配置,有些是给flask使用的,有些是给我们程序使用的。那在我们的python程序中怎么去读取设置给程序使用的配置呢?app.config.get('xxx')。如果在当前的视图函数所在作用域无法拿到app变量,那么可以使用current_app,这里可以先理解成一个app实例变量的代理,后续会分析其源码流程

在这里,着重强调一下from_object方法,因为这也是经常使用的。看源码

1112571-20180813090901450-901475338.png
1112571-20180813091015428-551180797.png
其实就是通过字符串的方式去导入,类似于django配置文件导入中间件只需要写路径的字符串形式就可以。
settings.py

class Test(object):    pass
import importlibsetting = 'settings.Test'module_str, class_str= setting.rsplit('.', maxsplit=1)m = importlib.import_module(module_str)print(getattr(m, class_str))

路由

查看所有路由

app.url_mapMap([
index>,
' (GET, HEAD, OPTIONS) -> static>])

Map对象,可以和django的urls.py一样,路由看作是一个列表,既然是列表就是有序的,也就是从前到后匹配,匹配成功就不会往下匹配。这也就是为什么给同一个路由绑定不同函数,最终执行的时候只有绑定的第一个函数才会被调用的原因。Map里放的是url对象,这个对象里封装了请求方法,路径等。

同一视图多个路由装饰

@app.route('/python')@app.route('/index')def index():    return 'xx'Map([
index>,
index>,
' (GET, HEAD, OPTIONS) -> static>])

我们看到index最终对应两个路由了。要理解这个,看app.route干了什么事情?这里唯一需要注意的一点是app,route不是一个装饰器,app.route()执行得到的结果才是装饰器,知道这一点看源码就清楚了

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

我们发现decorator做了两件事,一件就是做路由注册,另一件就是把视图函数原封不动地返回,所以最上面的装饰器装饰的也是原视图函数,只不过又再注册了一个路由而已。这里其实还用到了闭包,decorator可以访问route函数的参数。

限制请求方式

@app.route('/index', methods=['poSt', "gEt"])def index():    return 'xx'

methods 列表中的字符串大小写无所谓,最终都会转为大写

url_for进行反解析

url和视图函数进行绑定之后,我们通过url就能找到函数并执行,那么我们可以<font color='red'通过函数名找到对应的url吗?答案是肯定的,通过url_for函数就能实现

@app.route('/index', methods=['poSt', "gEt"], endpoint='xxx')def index():    return 'xx'@app.route('/login')def login():    url = url_for('xxx')    print(url)    return url

当一个函数在路由注册的时候没有指定endpoint,默认的endpoint就是函数名。url_for(endpoint, **values)根据*values可以创建动态url

app.route 参数

@app.route和app.add_url_rule参数:            rule,                       URL规则            view_func,                  视图函数名称            defaults=None,              默认值,当URL中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数            endpoint=None,              名称,用于反向生成URL,即: url_for('名称')            methods=None,               允许的请求方式,如:["GET","POST"]                        strict_slashes=None,        对URL最后的 / 符号是否严格要求,                                        如:                                            @app.route('/index',strict_slashes=False),                                                访问 http://www.xx.com/index/ 或 http://www.xx.com/index均可                                            @app.route('/index',strict_slashes=True)                                                仅访问 http://www.xx.com/index             redirect_to=None,           重定向到指定地址                                        如:                                            @app.route('/index/
', redirect_to='/home/
') 或 def func(adapter, nid): return "/home/888" @app.route('/index/
', redirect_to=func) subdomain=None, 子域名访问 from flask import Flask, views, url_for app = Flask(import_name=__name__) app.config['SERVER_NAME'] = 'jack.com:5000' # http://admin.jack.com:5000/ @app.route("/", subdomain="admin") def static_index(): """Flask supports static subdomains This is available at static.your-domain.tld""" return "admin.xxx.com"              #动态生成 # http://common.jack.com:5000/dynamic @app.route("/dynamic", subdomain="
") def username_index(username): """Dynamic subdomains are also supported Try going to user1.your-domain.tld/dynamic""" return username + ".your-domain.tld" if __name__ == '__main__':

动态路由

默认支持6种路由转换器,不仅可以按照规则匹配路由,还可以对匹配到的内容进行转换之后再传给视图函数

@app.route('/user/
') #常用的 不加参数的时候默认是字符串形式的@app.route('/user/
') #常用的 @app.route('/post/
') #常用的 #指定int,说明是整型的@app.route('/post/
')@app.route('/post/
')@app.route('/post/
')@app.route('/post/
')
from werkzeug.routing import BaseConverter, DEFAULT_CONVERTERSDEFAULT_CONVERTERS = {    'default':          UnicodeConverter,    'string':           UnicodeConverter,    'any':              AnyConverter,    'path':             PathConverter,    'int':              IntegerConverter,    'float':            FloatConverter,    'uuid':             UUIDConverter,}

自定义转换器类

from werkzeug.routing import BaseConverterfrom werkzeug.urls import url_quoteapp = Flask(__name__)app.config.from_pyfile('settings.cfg')class MyConverter(BaseConverter):    def __init__(self, url_map, regex):        super(MyConverter, self).__init__(url_map)        # regex 这个属性不是我们自定义的,是BaseConverter里的,我们只是重新赋值        self.regex = regex    def to_python(self, value):        # 匹配成功之后,例如匹配手机号13739191111成功,在把这个值作为变量传给视图函数之前会调用这个方法,并且把这个方法        # 的返回值才最终传给视图函数        print(value)        return value    def to_url(self, value):        # 进行url_for的时候进行调用, 会把值先传入to_url,我们可以对value做一些自定义,自定义之后的值再放到路由里        print(value)        return url_quote(value, charset=self.map.charset)# 注册到app.url_map.converters中app.url_map.converters['re'] = MyConverter@app.route('/index/
', endpoint='xxx')def index(phone): return 'xx'@app.route('/login')def login(): url = url_for('xxx', phone='1213123122') return url

1112571-20180807105858501-1792599455.png

请求参数

1112571-20180807105941712-1007789686.png

request.body 里放的是请求体数据,如果请求体是urlencoded编码的格式,那么flask就会帮我们把数据解析到request.form里面,并且会清空request.data里的数据

如果前端name字段传了两次值,那么get方式只能拿第一个值,要想拿所有值,用getlist方法,得到的就是一个列表

上传文件

from flask import request@app.route('/upload', methods=['GET', 'POST'])def upload_file():    if request.method == 'POST':        # xx 是form表单的name值,而不是文件名        f = request.files['xx']        # save方法是flask帮我们封装的,这样我们就不需要自己打开文件读取内容再自己写进去了         f.save('/var/www/uploads/' + secure_filename(f.filename))

abort函数与自定义异常处理

我们在视图函数想要提前终止函数的执行,可以使用return。但是函数如果执行逻辑会有出错的地方,我们通过raise抛出的错误会显示在页面上。abort的作用就是提前终止函数,并抛出错误,而这种错误和之间raise错误是有区别的。

abort一般有两种用法,要么传字符串参数,要么传http状态码(flask根据不同状态码都定制了相应的页面,我们可以重写这些页面)

from from flask import abort@app.route('/index',  endpoint='xxx', methods=['POST', 'GET'])def index():    abort(404)    return 'xx'@app.errorhandler(404)def error(e):    return '您请求的页面不存在了,请确认后再次访问!%s'%e

返回响应数据

返回响应数据有两种形式,一种是返回元组,一种是返回Response对象

元组:(response, status, headers) 或者 (response, status) 其中status可以是’404'这样额数字字符串,也可以是带有code说明信息的字符串'666 not find',其中not find就是666的这个自定义状态码的说明信息
Response对象

from flask import  make_responseresp = make_response()resp.headers[“sample”] = “value”resp.status = “404 not found”

使用jsonify返回json数据

如果想返回json数据,自己写代码是这样的

@app.route('/index',  endpoint='xxx', methods=['POST', 'GET'])def index():    re_str = json.dumps({'name':"jack"})    return re_str, 200, {"Content-type":'json'}

jsonify 实现的就是re_str, 200, {"Content-type":'json'}的功能,jsonify可以穿一个字典,也可以传键值对的形式

请求响应相关

from flask import Flask    from flask import request    from flask import render_template    from flask import redirect    from flask import make_response    app = Flask(__name__)    @app.route('/login.html', methods=['GET', "POST"])    def login():        # 请求相关信息        # request.method        # request.args        # request.form        # request.values        # request.cookies        # request.headers        # request.path        # request.full_path        # request.script_root        # request.url        # request.base_url        # request.url_root        # request.host_url        # request.host        # request.files        # obj = request.files['the_file_name']        # obj.save('/var/www/uploads/' + secure_filename(f.filename))        # 响应相关信息        # return "字符串"        # return render_template('html模板路径',**{})        # return redirect('/index.html')        # response = make_response(render_template('index.html'))        # response是flask.wrappers.Response类型        # response.delete_cookie('key')        # response.set_cookie('key', 'value')        # response.headers['X-Something'] = 'A value'        # return response        return "内容"    if __name__ == '__main__':        app.run()

session

基本使用当中字典来使用即可,后续源码分析会解析session流程

from flask import Flask, jsonify, sessionapp = Flask(__name__)app.config.from_pyfile('settings.cfg')app.secret_key = 'asdasd'app.config['SESSION_COOKIE_NAME'] = 'xxx'@app.route('/index',  endpoint='xxx', methods=['POST', 'GET'])def index():    print(session.get("a"))    return jsonify({'a':1})@app.route('/login')def login():    session['a'] = 1    return 'login'if __name__ == '__main__':    print(app.url_map)    app.run()

flash

flask的flash(闪现)是基于session来做的,我们知道session使用的时候可以看做是一个字典,字典里的值可以取多次,但是flash与其不同,flash存储的值取完就没有了。其实没有flash,我们完全可以自己实现,取完就没有无外乎就是一个pop操作。flask的闪现使用是用两个方法:flash和get_flashed_messages

from flask import Flask,flash, get_flashed_messagesapp = Flask(__name__)app.secret_key = 'adasd'@app.route("/")def index():    # flash({'a':1})    # get_flashed_messages()  取到是一个列表 [{'a': 1}]    # 借助catogary对数据进行分类    flash('xiyouji','book')    flash('monkey', 'animal')    return 'index'@app.route('/index')def i():    # 需要指明category_filter参数,否则get_flashed_messages('animal')取到的是所有的值    print(get_flashed_messages(category_filter='animal'))    return 'i'if __name__ == '__main__':    app.__call__    app.run()

flash函数源码

def flash(message, category='message'):    flashes = session.get('_flashes', [])    flashes.append((category, message))    session['_flashes'] = flashes    message_flashed.send(current_app._get_current_object(),                         message=message, category=category)

说白了,就是在session的那个大字典加一个key:value, 其中key是_flashes.

全局中间件

1112571-20180813092009291-97591402.png

1112571-20180813092028111-1715273620.png
请求进来执行self(), 其中self是一个对象,self.__call__, 也就是app.__call__
1112571-20180813092148972-1493473492.png
现在问题来了,我想要在执行self.wsgi_app(environ, start_response)之前和之后做点事情,比如说打印什么东西,应该怎么做?纳尼,改源码?好想法,但是你写的程序别人要使用难不成让别人机器上的flask源码都要修改一下吗?这肯定不行,要么是装饰器,要么是继承。装饰器也是需要改源码,那么只能用继承了。

class MyFlask(Flask):    def __call__(self, environ, start_response):        print('进来了')        ret =  super(MyFlask, self).__call__(environ, start_response)        print('出去了')        return ret

这样我们的app只需要用MyFlask来实例化就行。但是如果我就想使用Flask来实例化,那么应该怎么做呢?因为self.wsgi_app是一个函数,加括号运行,那么这里我们能不能把self.wsgi_app封装成一个类的对象呢,然后调用对象的__call__ 方法。

class MyWsgiApp(object):    def __init__(self, wsgi_app):        self.wsgi_app = wsgi_app    def __call__(self, *args, **kwargs):        print('come in')        ret = self.wsgi_app(*args, **kwargs)        print('out')        return retif __name__ == '__main__':    app.wsgi_app = MyWsgiApp(app.wsgi_app)    app.run()

flask中的装饰器

想要每次请求判断是否登录,登录之后才能访问某些网页,没有登录就跳转到登录页面

from flask import Flask,session, redirectapp = Flask(__name__)app.secret_key = 'adasd'app.debug = Truedef auth(func):    def inner(*args, **kwargs):        sid = session.get('sid')        if sid:            ret = func(*args, **kwargs)            return ret        else:            return redirect('/login')    return inner@app.route("/")@authdef index():    return 'index'@app.route('/login')@authdef login():    return 'login'if __name__ == '__main__':    app.run()

发现报错了

1112571-20180813095117514-1585451120.png
问题就出在不同的路径对应相同的endpoint,那么通过url_for反推路径的时候应该取不同路径中的哪一个呢?所以就报错了,现在从源码角度看看
1112571-20180813095601581-2063819963.png
因为使用装饰器的时候,所有的被装饰函数的__name__都是inner,但是得到的对象都是不一样的(内部的inner函数可以看做一个对象,每次执行一次auth就会生成一个新的inner对象)。
装饰器修改如下:

from functools import wrapsdef auth(func):    @wraps(func)    def inner(*args, **kwargs):        sid = session.get('sid')        if sid:            ret = func(*args, **kwargs)            return ret        else:            return redirect('/login')    return inner

flask的CBV

from flask.views import MethodViewclass MyView(MethodView):    # 对所有请求的装饰器    decorators = []    methods = []    def get(self, *args, **kwargs):        pass    def post(self, *args, **kwargs):        pass# xxx 就是app.add_url_rule('/', view_func=MyView.as_view(name='xxx'))

转载于:https://www.cnblogs.com/longyunfeigu/p/9435759.html

你可能感兴趣的文章
booking.sh
查看>>
机器学习--避免过度拟合 笔记
查看>>
Linux SSH 基于密钥交换的自动登录原理简介及配置说明
查看>>
mariadb connector bug
查看>>
KnockoutJs学习笔记(十二)
查看>>
Noip2018游记
查看>>
Oracle11g数据库在Win系统下的安装
查看>>
初识Python
查看>>
nodejs+mysql入门实例(改)
查看>>
表达式语言
查看>>
jQuery EasyUI实现关闭全部tabs
查看>>
iOS项目之WKWebView替换UIWebView相关
查看>>
Lambda表达式效率问题
查看>>
【转载】iOS 设置Launch Image 启动图片(适用iOS9)
查看>>
最快得到MYSQL两个表的差集
查看>>
KVC中setValuesForKeysWithDictionary: (转载)
查看>>
UML类图关系
查看>>
清理Visual Studio打开的项目和文件、查找和最近引用组件痕迹
查看>>
10.11NOIP模拟题(3)
查看>>
正则表达式速查表
查看>>