Flask基础

flask是什么?

它是一个Python编写微web框架,包括了一个核心两个库(jinja2模板引擎和WSGI工具集)

创建一个独立的虚拟环境

deactivate 退出

mkvirtualenv flask 创建虚拟环境

workon flask 切换到创建的虚拟环境

安装flask

pip install flask
包名及版本 功能
Jinja2-2.10 渲染模板引擎工具集
MarkupSafe-1.1.0 可以识别HTML转义规则。HTML字符转义工具集
Werkzeug-0.14.1 Web 框架的底层库,提供了请求及响应以及开发服务器的功能,简称WSGI工具集
click-7.0 命令行工具集
itsdangerous-1.1.0 加密工具集

我们安装这5个包,这就是一个基本的flask

✔提示:这些库均由Flask团队开发

创建一个简单的flask

from  flask import Flask

app=Flask(__name__)

@app.route("/")
def index():
    return "ojbk"


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

源码分析

启动

启动代码为app.run()

    def __call__(self, environ, start_response):
        return self.wsgi_app(environ, start_response)

核心

    def wsgi_app(self, environ, start_response):
        ctx = self.request_context(environ)
        error = None
        try:
            try:
                ctx.push()
                response = self.full_dispatch_request()
            except Exception as e:
                error = e
                response = self.handle_exception(e)
            except:  # noqa: B001
                error = sys.exc_info()[1]
                raise
            return response(environ, start_response)
        finally:
            if self.should_ignore_error(error):
                error = None
            ctx.auto_pop(error)

run

run主要做的就是用werkzeug来启动服务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

Flask基本响应

from flask import Flask,render_template,redirect,jsonify,request
app=Flask(__name__)

'''
1 返回字符串
2 返回模板:render_template
3 跳转:redirect
4 json返回

'''

@app.route("/")
def index():

    return "zx"

@app.route("/index")
def index1():
    return render_template("index.html")

@app.route("/home")
def home():
    return redirect("/index")

@app.route("/json")
def json1():
    data_dic={'name':"zx",'age':18}
    return jsonify(data_dic)


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

基本配置

可配置的选项

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

配置方式

from  flask import Flask
app = Flask(__name__)
#方式一
# 只能配置下面俩个
# app.debug=True
# app.secret_key="asdjja"
#方式二,以字典的形式
# app.config['DEBUG']=True
#方式三 ,以文件的形式
# app.config.from_pyfile("settings.py")
#方式四 ,以类的形式
app.config.from_object("settings.test")


@app.route("/")
def index():
    return "ok"

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

类的形式

一般都是以类的形式,可以通过继承的方式实现公共提取,部分修改,行程多套配置

class common:
	#公共配置
    
#开发配置
class test(common):
    DEBUG = True

#生产配置
class onlin(common):
    DEBUG = False

路由本质

#我们的路由是依靠这个实现的
@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

所以在view里面,装饰器路由就等同于

    @setupmethod
    def add_url_rule(
        self,
        rule,
        endpoint=None,
        view_func=None,
        provide_automatic_options=None,
        **options
    ):

app.add_url_rule(rule, endpoint, f, **options)

最终

就和Django的路由映射非常的相似了

#路由和函数关系映射
app.add_url_rule("/index", view_func=index)

CBV

#简单模式
#默认执行的就是dispatch_request方法
#name="index1"用于方法和路由的反射

 def login(f):
 	pass
 class IndexView(views.View):
 	 #允许的请求方式
     methods = ['GET']
     #给dispatch_request加装饰器
     #decorators = [login, ]
     def dispatch_request(self):
         print('Index')
         return 'Index!'

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

#通常用此方式
#会根据请求方式直接找对应的方法
class IndexView(views.MethodView):
        methods = ['GET']
        # decorators = [auth, ]

        def get(self):
            return 'Index.GET'

        def post(self):
            return 'Index.POST'
app.add_url_rule('/index', view_func=IndexView.as_view(name='index'))  # name=endpoint

路由参数

@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"]

redirect_to

这个重定向是不执行里面代码直接重定向的,和之前的不同

@app.route("/index/",endpoint="a",methods=["POST","GET"],strict_slashes=True,redirect_to="/index2")
def index():
    print(url_for("a"))
    return "ok"

@app.route("/index2")
def index2():
    return "吃饭去了"

路由转化器

本质

就是通过路由传递参数

from  flask import Flask
app = Flask(__name__)

@app.route("/<string:flag>")
def index(flag):
    return f"jason is sb ? {flag}"

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

自定义路由转化器

#1 写类,继承BaseConverter
#2 注册:app.url_map.converters['regex'] = RegexConverter
# 3 使用:@app.route('/index/<regex("\d+"):nid>')  正则表达式会当作第二个参数传递到类中
from flask import Flask, views, url_for
from werkzeug.routing import BaseConverter

app = Flask(import_name=__name__)

class RegexConverter(BaseConverter):
    """
    自定义URL匹配正则表达式
    """
    #__init__固定写法
    def __init__(self, map, regex):
        super(RegexConverter, self).__init__(map)
        self.regex = regex

    def to_python(self, value):
        """
        路由匹配时,匹配成功后传递给视图函数中参数的值
        """
        return int(value)+123

    def to_url(self, value):
        """
        使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
        """
        #固定写法,调用父类的
        val = super(RegexConverter, self).to_url(value)
        return val+"json"

# 添加到flask中
app.url_map.converters['regex'] = RegexConverter

#nid会先去to_python进行处理完,之后再把结果返回给函数的nid
@app.route('/index/<regex("\d+"):nid>')
def index(nid):
   # print(nid)
    print(url_for('index', nid='888'))
    return 'Index'

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

常见模板语法

数据

USERS = {
    1:{'name':'张三','age':18,'gender':'男','text':"道路千万条"},
    2:{'name':'李四','age':28,'gender':'男','text':"安全第一条"},
    3:{'name':'王五','age':18,'gender':'女','text':"行车不规范"},
}


def func1(flag):
    return Markup(f"<h1>jason dsb ?{flag}</h1>" )

@app.route("/index")
def index():
    data=USERS
    flag=True
    htm="<h1>jason dsb</h1>"
    return render_template("index1.html",data=data,flag=flag,name="sb",name1="sb1",htm=htm,func=func1)

模板

//循环
{% for k,v in data.items() %}
        <tr>
            <td>{{k}}</td>
            <td>{{v.name}}</td>
            <td>{{v['name']}}</td>
            <td>{{v.get('name')}}</td>
            <td><a href="/detail/{{k}}">查看详细</a></td>
        </tr>
{% endfor %}

//条件判断
{% if flag %}
    {{name}}
{% else %}
    {{name1}}
{% endif %}
 <br>

//两种后端直接传递html
{{htm|safe}}

{{func("yes")}}


session

原理

我们知道flask默认是没有连接数据库的,那么它的session是怎么存储的呢?

其实和token的原理类似,将数据存储在客户端的cookies里面,然后到后端解密取值

from  flask import Flask,session
app = Flask(__name__)
#加密的盐
app.secret_key="zxxzxzcas"
#存在客户端的cookies名
app.config["SESSION_COOKIE_NAME"]="zx"

# app.session_interface

@app.route("/")
def index():
    session['name']="zx"
    return "ok"

@app.route("/index")
def index1():
    print(session['name'])
    return "123"

#客户端存的cookies就类似这个,后面的是加密的数据
zx:sadasduasgdhjasghjdjasgdhja

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

request&response高级

注意

rerequest不是通过传参的方式拿到的,而是import

response想要设置cookie或者响应头,需要先创建respon对象

可以是返回字符串和静态界面

response = make_response("内容")

from flask import Flask,request,make_response,render_template

app = Flask(__name__)

@app.route('/login.html', methods=['GET', "POST"])
def login():
    # 请求相关信息
    # request.method  提交的方法
    # request.args  get请求提及的数据
    # request.form   post请求提交的数据
    # request.values  post和get提交的数据总和
    # request.cookies  客户端所带的cookie
    # request.headers  请求头
    # request.path     不带域名,请求路径
    # request.full_path  不带域名,带参数的请求路径
    # request.script_root
    # request.url           带域名带参数的请求路径
    # request.base_url      带域名请求路径
    # request.url_root      域名
    # request.host_url      域名
    # request.host          127.0.0.1:500
    # 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({'k1':'v1'})

    # response = make_response(render_template('index.html'))

    # response是flask.wrappers.Response类型
    #删除cookie
    # response.delete_cookie('key')
    #设置cookie
    # response.set_cookie('key', 'value')
    #设置响应头
    # response.headers['X-Something'] = 'A value'
    # return response

    #返回字符串
    #response = make_response("内容")
    return response

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

local原理

我们知道如果是使用Django的话,每一次的request都是直接写写在方法参数的,这样就很好理解,一次请求就会把请求的参数封装为一个request

flask

但是flask我们使用session,竟然是import,是不是不理解,其实它底层是这样实现的

_request_ctx_stack是一个LocalStack实例的全局变量,来一个请求就把ctx存到LOCAL对象的storage中去,以线程id,协程id为区分

ctx.push()里面执行了
_request_ctx_stack.push(self):self是ctx
                
LocalStack里面的push方法,obj参数是ctx
def push(self, obj):
    rv = getattr(self._local, "stack", None)
    if rv is None:
       self._local.stack = rv = []
       #把obj,也就是ctx存到了LOCAL对象中的storage,存的格式为: storage={"执行id":{'stack':[ctx,]}}
       
    rv.append(obj)
    return rv
#rv是啥     
rv = getattr(self._local, "stack", None)这个self._local是LOCAL对象

核心

有使用到getattr和setattr

    __local=LOCAL()
    __local.stack=[ctx,]
    storage={stack:[ctx,]}
    storage={"执行id":{'stack':[ctx,]}}

    print(__local.stack)
    storage["执行id"].get("stack")[-1]

    storage["执行id"]['stack']

闪现

注意

1.由于闪现是基于session的所以必须要先设置

app.secret_key = 'asfkjabm'

2.闪现的k值可以重复,打印出来是元组的形式展示

原理**

flash是基于session实现的

特点

闪现的取值只能出现在一次请求内部,它会先去请求的内部变量去找值,没有的话,去session,pop获取存在内部变量里面,这个变量的生命周期就是一次请求内

然后第二次请求原先的session内的值已经pop掉了,内存中也清空了

使用

#设置闪现值
@app.route('/index')
def index():
	#默认闪现的,k值为message
    flash('超时错误')
    #也可以手动设置闪现值
    flash('哈哈',category="zx")
    return "ssdsdsdfsd"
    
#取闪现值
@app.route('/error')
def error():
	#with_categories默认为false,是否显示k值
	#category_filter默认为空,过滤器,显示选定的k的闪现值
    data = get_flashed_messages(with_categories=True,category_filter=('zx',))
    data2 = get_flashed_messages()
    print(data)
    print(data2)
    return  "ojbk"

请求扩展

说明

和Django的中间件很相似

源码

full_dispatch_request()

    def wsgi_app(self, environ, start_response):
        ctx = self.request_context(environ)
        error = None
        try:
            try:
                ctx.push()
                response = self.full_dispatch_request()
            except Exception as e:
                error = e
                response = self.handle_exception(e)
            except:  # noqa: B001
                error = sys.exc_info()[1]
                raise
            return response(environ, start_response)
        finally:
            if self.should_ignore_error(error):
                error = None
            ctx.auto_pop(error)

full_dispatch_request()

请求和响应部分的源码

    def full_dispatch_request(self):
        self.try_trigger_before_first_request_functions()
        try:
            request_started.send(self)
            rv = self.preprocess_request()
            #返回为None就执行视图函数
            if rv is None:
                rv = self.dispatch_request()
        except Exception as e:
            rv = self.handle_user_exception(e)
        return self.finalize_request(rv)

请求之前执行-before_request

按顺序执行,如果中途返回值不为None,直接断掉,去执行所有的响应过滤函数

@app.before_request
def sb():
    print(request)
    print("我是请求之前")

@app.before_request
def  sb1():
    print("我是请求之前2")
    #return "我是请求之前2的返回"

响应之后执行-after_request

执行顺序相反

@app.after_request
def process_response1(response):
    print('process_response1 走了')
    return response

@app.after_request
def process_response2(response):
    print('process_response2 走了')
    return response

第一次请求执行-before_first_request

原理就是定义个全部变量,第一次来赋值

 @app.before_first_request
 def first():
    print("我的第一次")

异常-teardown_request

无论有没有异常都会执行,会在视图函数执行后执行

@app.teardown_request
def ter(e):
    print(e)
    print("我是异常")

异常捕获-errorhandler

#要捕获的错误码
@app.errorhandler(500)
def error_404(arg):
    print(arg)
    #返回给前端,可以为界面
    return "500错误了"

全局定义函数-template_global

使用的时候不需要传递,直接在模板界面使用

@app.template_global()
def sb(a1, a2):
    return a1 + a2

{{sb(1,2)}}

全局过滤器-template_filter

@app.template_filter()
def db(a1, a2, a3):
    return a1 + a2 + a3
    
{{12|db(1,1)}}

中间件

重写源码,然后调用源码核心部分就行

from flask import Flask,flash,get_flashed_messages,request

app = Flask(__name__)

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

    def __call__(self, environ, start_response):

        print("中间件前")
        res=self.wsgi_app(environ, start_response)
        print("中间件后")
        print(res)
        return res

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

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

偏函数

偏函数和闭包函数类似

from functools import partial
def test(a,b,c,d):
    return a+b+c+d

tes=partial(test,a=1,b=2)

print(tes(c=3,d=4))

参考链接

https://www.cnblogs.com/xiaoyuanqujing/category/1561311.html

posted @ 2019-12-15 16:01  zx125  阅读(135)  评论(0编辑  收藏  举报