Flask基本操作

配置文件

Flask 中所有的配置文件可以通过Flask(_name_).config查看。实际上是一个flask.config.Config对象

from flask import Flask
app = Flask(__name__)
print(app.config)

默认配置文件

{
    '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,
}

修改配置文件的方式

基于类

class DevConfig:
    DEBUG = True
	# ......
    
   
app.config.from_object(DevConfig)

其他修改方式

# 直接修改
app.config['DEBUG'] = True


# py文件
app.config.from_pyfile("settings.py")
# settings.py文件
DEBUG = True


# 环境变量的值为python文件名称名称,内部调用from_pyfile方法
pp.config.from_envvar("环境变量名称")


# json文件
app.config.from_json("setting.json")
# setting.json 文件
{"DEBUG": true}


# python字典
app.config.from_mapping({'DEBUG':True})

路由系统

url反向生成

endpoint 反向生成url,endpoint默认就是函数名

from flask import url_for

@app.route('/index', methods=['GET', "POST"], endpoint='xxx')
def index():
    print(url_for('xxx'))
    ....
    

url动态传参

@app.route('/index/<int:nid>')			# 整数
def index(nid):
    print(nid,type(nid))
    return 'index'

其他常见写法

@app.route('/index/<int:nid>')			# 整数
@app.route('/index/<float:nid>')		# 小数
@app.route('/index/<path:nid>')			# 路径
@app.route('/index/<uuid:nid>')			# uuid
@app.route('/index/<nid>')			# 字符串/@app.route('/index/<str:nid>')
@app.route('/index')				# 啥都不传

如果想反向生成url就需要额外传参

print(url_for('index', nid=123))		# 传入的数据必须和url匹配的类型一样

所有路由的对应关系

DEFAULT_CONVERTERS = {
    'default':          UnicodeConverter,
    'string':           UnicodeConverter,
    'any':              AnyConverter,
    'path':             PathConverter,
    'int':              IntegerConverter,
    'float':            FloatConverter,
    'uuid':             UUIDConverter,
}

不采用装饰器的写法

def index():
    return 'xxx'

app.add_url_rule(rule='/index', view_func=index)

参数说明

rule					url路径
view_func				视图函数
defaults=None			        url传参{'k':'v'}
endpoint=None			        别名,为None时则等于函数名
methods					允许的请求方式
strict_slashes=True		        对url最后的 / 符号是否严格要求
redirect_to=None		        重定向地址
subdomain=None			        子域名访问

自定义路由匹配

from werkzeug.routing import BaseConverter
# 自定义一个类,继承BaseConverter
class MyRouter(BaseConverter):
    regex = '\d+'
    def to_python(self, value):
        return int(value)

    def to_url(self, value):
        return str(value)

# 添加到转换器中
app.url_map.converters['m_r'] = MyRouter

# 使用自定义路由匹配
@app.route('/index/<m_r:nid>')
def index(nid):
    print(nid)
    return 'xxx'

万能版

from werkzeug.routing import BaseConverter
class MyRouter(BaseConverter):
    def __init__(self, map, regex):
        super().__init__(map)
        self.regex = regex

    def to_python(self, value):
        return int(value)

    def to_url(self, value):
        return str(value)


app.url_map.converters['MyRouter'] = MyRouter


@app.route('/index/<MyRouter("\d{3}"):nid>')
def index(nid):
    print(nid)
    return 'xxx'

视图

FBV

from flask import Flask

app = Flask(__name__)


@app.route('/index')
def index():
    return 'index'


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

加装饰器

from functools import wraps
def decorate(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        print('前')
        rest = f(*args, **kwargs)
        print('后')
        return rest

    return wrapper

@app.route('/index')
@decorate
def index():
    return 'index'

CBV

from flask import Flask
from flask import views
from functools import wraps

app = Flask(__name__)


def decorate(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        print('前')
        rest = f(*args, **kwargs)
        print('后')
        return rest

    return wrapper


class IndexView(views.MethodView):
    methods = ['GET', 'POST']
    decorators = [decorate, ]

    def get(self, *args, **kwargs):
        return 'GET'

    def post(self, *args, **kwargs):
        return 'POST'


app.add_url_rule('/index', view_func=IndexView.as_view('index'))

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

请求&响应

from flask import request
from flask import make_response
from flask import jsonify
from flask import redirect
from flask import render_template
from werkzeug import secure_filename

@app.route('/index/<int:nid>', methods=['GET', "POST"])
def index(nid):
    # 请求相关信息
    # request.method
    # request.args
    # request.form
    # request.values
    # request.data
    # request.json
    # 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')
    # return jsonify({'k':'v'})
    
    # 定制cookie,响应头
    # response 是 flask.wrappers.Response 类型
    # response = make_response(render_template('index.html'))
    # response.delete_cookie('key')
    # response.set_cookie('key', 'value')
    # response.headers['X-Something'] = 'A value'
    # return response

模板渲染(jinja2)

jinja2 模板语法和 DjangoTemplates 相似,但是和Django模板相比更贴近Python的语法

官方文档 中文文档

定义宏

jinja2 中的类似python中的函数

# 定义一个宏
{% macro input(name, type='text', value='') -%}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{%- endmacro %}

# 调用宏
{{ input('username') }}
{{ input('password', type='password') }}

自定义标签和过滤器(特殊装饰器)

@app.template_global()
def func(v1, v2):
    return v1 + v2


@app.template_filter()
def func2(v1, v2, v3):
    return v1 + v2 + v3


@app.route('/index')
def index():
    return render_template('index.html', a=1)
{{ func(1, 2) }}
{{ a | func2(2, 3) }}

session

在 Flask 中操作 session 的方式和操作 Python 字典的方式几乎一样,跟Django的session一样,Flask也有SECRET_KEY,但是Flask中默认SECRET_KEY=None需要手动指定

app.secret_key = 'xxx'

或者直接在配置文件中指定

class DevConfig:
    DEBUG = True
    SECRET_KEY = 'xxx'
	# ......
app.config.from_object(DevConfig)
  • 当请到来时,Flask会读取cookie中session这个key对应的value,并且将value解密、反序列化为Python的字典,然后放到内存中,供视图函数使用
  • 当请求结束时,Flask会读取内存中session字典的值,然后再进行序列化、加密,最后写入到cookie中

session常用操作

from flask import session

session.update({"is_login":True, 'user':'xxx'})
session.get('is_login')
del session['xxx']
session.items()
session.clear()

闪现(flash)

flash一个基于session实现的用于保存数据的集合,读取时使用pop将其移出,所以闪现的特点是:使用一次就删除。

from flask import flash, get_flashed_messages

@app.route('/page1')
def page1():
    flash(1,'info')
    flash(11,'error')
    flash([1,2,3],'xxx')
    return '1'


@app.route('/page2')
def page2():
    print(get_flashed_messages(category_filter=['info']))
    return '2'

闪现机制内部源码

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
    )
    

def get_flashed_messages(with_categories=False, category_filter=()):
    flashes = _request_ctx_stack.top.flashes
    if flashes is None:
        _request_ctx_stack.top.flashes = flashes = (
            session.pop("_flashes") if "_flashes" in session else []
        )
    if category_filter:
        flashes = list(filter(lambda f: f[0] in category_filter, flashes))
    if not with_categories:
        return [x[1] for x in flashes]
    return flashes

中间件

一个简单的flask示例

from flask import Flask

app = Flask(__name__)


@app.route('/index')
def index():
    return 'index'


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

当程序执行时,就会执行 app.run() 方法,在run方法内部又执行了run_simple,所以run_simple就是整个程序的入口,源码如下

def run(self, host=None, port=None, debug=None, load_dotenv=True, **options):
    if os.environ.get("FLASK_RUN_FROM_CLI") == "true":
        from .debughelpers import explain_ignored_app_run

        explain_ignored_app_run()
        return

    if get_load_dotenv(load_dotenv):
        cli.load_dotenv()

        # if set, let env vars override previous values
        if "FLASK_ENV" in os.environ:
            self.env = get_env()
            self.debug = get_debug_flag()
        elif "FLASK_DEBUG" in os.environ:
            self.debug = get_debug_flag()

    # debug passed to method overrides all other sources
    if debug is not None:
        self.debug = bool(debug)

    _host = "127.0.0.1"
    _port = 5000
    server_name = self.config.get("SERVER_NAME")
    sn_host, sn_port = None, None

    if server_name:
        sn_host, _, sn_port = server_name.partition(":")

    host = host or sn_host or _host
    # pick the first value that's not None (0 is allowed)
    port = int(next((p for p in (port, sn_port) if p is not None), _port))

    options.setdefault("use_reloader", self.debug)
    options.setdefault("use_debugger", self.debug)
    options.setdefault("threaded", True)

    cli.show_server_banner(self.env, self.debug, self.name, False)

    from werkzeug.serving import run_simple

    try:
        run_simple(host, port, self, **options)
    finally:
        # reset the first request information if the development server
        # reset normally.  This makes it possible to restart the server
        # without reloader and that stuff from an interactive shell.
        self._got_first_request = False

当请求进来时,就会执行app(),此时就会调用app.__call____call__源码如下

def __call__(self, environ, start_response):
    """The WSGI server calls the Flask application object as the
    WSGI application. This calls :meth:`wsgi_app` which can be
    wrapped to applying middleware."""
    return self.wsgi_app(environ, start_response)

通过源码分析,我们可以人为的添加一个中间件,但是基本上我们不用这种方式的中间件。但是在开发中一般用before_requestafter_request装饰器,在执行业务代码之前或之后进行一些操作

from flask import Flask

app = Flask(__name__)


@app.route('/index')
def index():
    return 'index'


class Middleware:
    def __init__(self, wsgi_app):
        self.wsgi_app = wsgi_app

    def __call__(self, *args, **kwargs):
        print('请求来了')
        rest = self.wsgi_app(*args, **kwargs)
        print('请求走了')
        return rest


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

蓝图(BluePrint)

作用:给开发者提供目录结构

小蓝图代码示例

  • 子域名控制(任选其一)
    • user = Blueprint('user', _name_, subdomain='user')
    • app.register_blueprint(user, subdomain='user')
  • url前缀(任选其一)
    • app.register_blueprint(ac, url_prefix='/account')
    • ac= Blueprint('ac', _name_, url_prefix='/account')

大型蓝图类似Django代码示例

特殊装饰器

before_request,after_request

类似Django process_request process_response。用户登陆验证例子

# coding=utf-8
from flask import Flask
from flask import request
from flask import session
from flask import render_template
from flask import redirect
from flask import url_for

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

@app.before_request
def before_request():
    if request.path != url_for('login'):
        if not session.get('is_login'):
            return redirect('login')


@app.after_request
def after_request(response):
    print(f'用户访问了:{request.path}')
    return response


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')

    user = request.form.get('username')
    pwd = request.form.get('password')
    if user == 'test' and pwd == '123':
        session.update({"is_login": True, 'user': user})
        return redirect('index')
    return redirect('login')


@app.route('/index')
def index():
    user = session.get('user')
    return f"首页! 欢迎 {user}"


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

如果有多个before_request,after_request,before_request按定义的顺序执行,after_request倒叙执行

before_first_request

只有第一个请求过来时才执行,剩下的请求不执行

@app.before_first_request
def first():
    print('xxx')

errorhandler

@app.errorhandler(404)
def page404(msg):
    print(msg)
    return '404'
posted @ 2020-05-30 22:27  _Otis  阅读(1005)  评论(0编辑  收藏  举报