flask学习一

@(python之路)[flask学习一]
flask学习一

flask简单使用

装饰器

装饰器基础:

def wapper(func):
    def inner(*args,**kwargs):
        return func(*args,**kwargs)
    return inner
'''
1. 立即执行wapper函数,并将下面装饰的函数单座参数传递
2. 将wapper函数返回值获取,在index赋值
'''
@wapper
def index():
    print("函数内容")
 # 这里实际执行的inner函数,inner函数内部调用原函数
index()

装饰器晋升:
这里我们引入一个functools模块,这个模块是它能将装饰过的函数的特殊属性保留。

import  functools
def wapper(func):
	@functools.wraps(func)
    def inner(*args,**kwargs):
        return func(*args,**kwargs)
    return inner
@wapper
def index():
    print("hello world ,from index")
@wapper
def order():
    print("hell world ,from order")

print(index.__name__) 
print(order.__name__) 
##########  使用functools.wraps(函数) #############
index
order
########## 不使用functools.wraps(函数)#####################
inner
inner

注:
对于这个functools.wraps装饰器,相当于wrapper = functools.wraps(func)(wrapper)。

面像对象的封装

  • 将数据封装到对象中
  • 对同一类方法封装到类中

什么后面可以添加括号

def f1():
    print('f1')

class F2(object):
    pass

class F3(object):
    def __init__(self):
        pass

    def ff3(self):
        print('ff3')

class F4(object):
    def __init__(self):
        pass
    def __call__(self, *args, **kwargs):
        print('f4')
def func(arg)
	arg()

由于arg在函数中加括号,所以他只有4中表现形式
函数:

# 1、函数,内部执行函数
func(f1)

类:

# 2、类,内部执行__init__方法
func(F2)

方法:

# 3、 方法,obj.ff3
obj1 = F3()
func(obj1.ff3)

对象:

# 4、 对象
obj2 = F4()
func(obj2)

什么是对象
对象 = 属性(特征)+ 方法(行为)

class F4(object):
    def __init__(self):
        print("构造方法")
    def __call__(self, *args, **kwargs):
        print("F4")
obj = F4()
obj()
######### 显示 #########
构造方法
F4

web框架本质

web框架的本质就是socket

import socket
def main():
    sock = socket.socket()
    sock.bind(("localhost",80))
    sock.listen(5)
    while True:
        connection , address = sock.accept()
        buf = connection.recv(1024)
        connection.send(b"HTTP/1.1 200 ok\r\n\r\n")
        connection.send(b"No No No")
        connection.close()
if __name__ == '__main__':
    main()

wsgi

Fask是基于werkzeug模块实现了wsgi协议(规范模块)
djang是基于wsgiref
代码上线: 一般我们在正式环境上使用的是uwsgi

flask入门

使用Flask内部的werkzeug模块实现wsgi协议(模块)的规范
基于函数

from werkzeug.wrappers import Request,Response
from werkzeug.serving import  run_simple

@Request.application
def hello(request):
    return Response("no no no ")
if __name__ == '__main__':
    run_simple("localhost",4000,hello)

使用Flask实现简单的访问页面
示例1:

from flask import Flask
# 1. 实例化Flask对象
app = Flask('xxxx')
"""
1. 执行 app.route('/index')并获取返回值 xx
2. 
    @xx
    def index():
        return 'Hello World'
3. 执行 index = xx(index)
本质: 
    {
        '/index': index
    }
"""
@app.route('/index')
def index():
    return 'Hello World'

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

源码分析:
app.run()到底做了什么?
我们查看一下app.run方法

def run(self, host=None, port=None, debug=None, **options):
from werkzeug.serving import run_simple
        if host is None:
            host = '127.0.0.1'
        if port is None:
            server_name = self.config['SERVER_NAME']
            if server_name and ':' in server_name:
                port = int(server_name.rsplit(':', 1)[1])
            else:
                port = 5000
        if debug is not None:
            self.debug = bool(debug)
        options.setdefault('use_reloader', self.debug)
        options.setdefault('use_debugger', self.debug)
        try:
            # 这里就是app = Flask(),然后将app传入;
            # run_simple(host,port,self,**options)
            # 这里的self就是,app = Flask();在回顾一下什么情况下执行(),函数,对象,方法,类;app就是对象()
            # 所以在他源码的请求入口就是__call__方法;也就是Flask框架的入口;
            # def __call__(self,environ,start_response):
            #     return self.wsgi_app(environ,start_response)

            run_simple(host, port, self, **options)
        finally:
            self._got_first_request = False

示例2:

from flask import Flask,render_template,request
# 在Flask中将所有的请求信息都凡在了request中,这里和django不一样,
# 所以我们在使用的时候需要导入request;
# 在Flask中也有render方法和django类似,只不过这里需要定义模板路径,
# 所以我们在实例化Flask中定义,template_folder中定义模板路径,这里
# 我写的是默认路径
# 有一些相关的设置需要看一下Flask()源码
# __init__(selef,……)
app = Flask("xxx",template_folder="templates")

# 如果在Flask中想让其支持其他请求,就需要在@app.route()中添加
# methods=["GET","POST"] 这样的化就支持了get和post请求
@app.route("/login",methods=["GET","POST"])
def login():
    if request.method == "GET":
        print(123)
        return render_template("login.html")
    else:
        print(456)
        user = request.form.get("user")
        pwd = request.form.get("pwd")   # from 请求体
        if user == "tom" and pwd == "123":
            return "登陆成功"
        # return render_template("login.html",msg="用户名密码错误")
        # 也可以用字典的方式传入参数()jinja2
        return render_template("login.html",**{"msg":"用户名密码错误"})
if __name__ == '__main__':
    app.run()
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>登陆页面</h1>
<form method="post">

    <input type="text" name="user">
    <input type="password" name="pwd">
    <input type="submit" value="提交">{{msg}}
</form>
</body>
</html>

示例3:

from flask import Flask,render_template,request,session,redirect
import functools
app = Flask("xxx",template_folder="templates")
# 这里的session是需要自己写,相比较django,我们可以调用django的内置方法使用;
# 这里需要一个secrt key将产生的session随机字符串放到浏览器中;
app.secret_key = "flkjalsfjlaksjflsafjslfj"

def auth(func):
    @functools.wraps(func)
    def inner(*args,**kwargs):
        user_info = session.get("user_info")
        if not user_info:
            return redirect("/login")
        return func(*args,**kwargs)
    return inner
@app.route("/index",methods=["GET",])
@auth
def index():
    return "is index"

@app.route("/login",methods=["GET","POST"])
def login():
    if request.method == "GET":
        print(123)
        return render_template("login.html")
    else:
        print(456)
        user = request.form.get("user")
        pwd = request.form.get("pwd")   # from 请求体
        if user == "tom" and pwd == "123":
            session["user_info"] = user
            return "登陆成功"
        # return render_template("login.html",msg="用户名密码错误")
        # 也可以用字典的方式传入参数()jinja2
        return render_template("login.html",**{"msg":"用户名密码错误"})
@app.route("/logout",methods=["GET"])
def logout():
    # 删除session
    del session["user_info"]
    return redirect("/login")
if __name__ == '__main__':
    app.run()

Flask配置文件

flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:
    {
        'DEBUG':                                get_debug_flag(default=False),  是否开启Debug模式
        '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["DEBUG"] = True
由于config对象本质上就是字典,所以还可以 使用app.config.update(……)
方式二:
创建一个setting.py文件

class BaseConfig(object):
	DEBUG = False
	SESSION_REFRESH_EACH_REQUEST = True
class ProConfig(BaseConfig)
	pass
class DevConfig(BaseConfig):
	DEBUG = True

我们在配置文件可以调用,

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

这里他是用配置文件的形式来处理,我们可以参照这种方式来写文件。
例如:我们写cmdb我们需要发送报警信息。
我们建立一个目录为notify,在这个目录下我们填写报警模块,email、msg和wechat,

#email.py
class Email(object):
    def send(self):
        print('发送邮件提醒')
#msg.py
class Msg(object):
    def send(self):
        print('发送短信提醒')
#wechat.py
class Wechat(object):
    def send(self):
        print('发送微信')

这些就是我们要编写的文件
在notify目录下我们需要在__init__.py文件中,将setting文件中的信息提取出来。

#__init__.py
import settings
import importlib
def send_notify():
    for path in settings.NOTIFY_LIST:
        # 'notify.email.Email',
        # 'notify.msg.Msg',
        module_path,cls_name = path.rsplit('.',maxsplit=1)
        # m = importlib.import_module("notify.email") # import  notify.email
        m = importlib.import_module(module_path)
        # 我们通过反射的方式找到这个类,并实例化
        cls = getattr(m,cls_name)
        obj = cls()
        obj.send()

在notify的同级目录下定义settings.py,就像django的中间件一样。

# settings.py

NOTIFY_LIST = [
	# 这里的Email就是类;
    'notify.email.Email',
    'notify.wechat.Wechat',
    'notify.msg.Msg',
]

我们书写启动接口;还是notify的同级目录

# run.py
from notify import send_notify

def run():
    send_notify()
if __name__ == '__main__':
    run()

这里我们是仿照django中间价的形式,书写配置文件。当我们添加新的报警机制模块是,就可以像django的中间件添加

路由系统

添加路由的本质
闭包

import functools
from flask import Flask
# 配置:模板/静态文件
app = Flask('xxxx',template_folder="templates")
"""
{
    '/index': index函数
}
1. decorator = app.route('/index')
2. 
    @decorator
    def index():
        return "index"
3. decorator(index)
"""
# 遵循从上到下执行,执行route
@app.route('/index')
def index():
    return "index"
def order():
    return 'Order'
# 这里我们根据下方源码分析可得;
app.add_url_rule('/order', None, order)
if __name__ == '__main__':
    app.run()

注意:
 route路由系统的本质就是一个闭包,在里边可以。
源码分析

def route(self, rule, **options):
		# 创建一个闭包把rule = /index 和
		# self = app对象封装到route中。
		
        def decorator(f):
            # f = index函数
            # endpoint = None 默认
            endpoint = options.pop('endpoint', None)
            # 把index、endpoint、函数添加到字典里
			# 它的本质就是调用自己的app(self),add_url_rule;这里rule是index函数,endpoint默认为None
            self.add_url_rule(rule, endpoint, f, **options)     

            return f
        return decorator

根据上面代码分析,我们得出结论路由的本质就是self.add_url_rule(rule, endpoint, f, **options)(self = app),无论是谁都会走到这里。下面我们就从源码分析一下内部原理。
self.add_url_rule(rule, endpoint, f, **options)源码分析

   def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
        # 把 rule/index/m endpoint=None view_func=函数
        # 如果endpoint为空的化就创建一个函数,这个函数就是我们传进来的函数。
        if endpoint is None:
            '''
            我们来看一下_endpoint_from_view_func(view_func)做了什么
            def _endpoint_from_view_func(view_func):
                assert view_func is not None, 'expected view func if endpoint ' \
                                  'is not provided.'
                return view_func.__name__
            这里返回的函数名.__name__;就是函数的名称;
            '''
            endpoint = _endpoint_from_view_func(view_func)
       options['endpoint'] = endpoint
        # 这里的methods就是函数所支持的方法。
        methods = options.pop('methods', None)
        if methods is None:
            methods = getattr(view_func, 'methods', None) or ('GET',)
        if isinstance(methods, string_types):
            raise TypeError('Allowed methods have to be iterables of strings, '
                            'for example: @app.route(..., methods=["POST"])')
        methods = set(item.upper() for item in methods)
        required_methods = set(getattr(view_func, 'required_methods', ()))
        provide_automatic_options = getattr(view_func,
            'provide_automatic_options', None)

        if provide_automatic_options is None:
            if 'OPTIONS' not in methods:
                provide_automatic_options = True
                required_methods.add('OPTIONS')
            else:
                provide_automatic_options = False

        methods |= required_methods
        # 我们看下一url_rule_class到底做了什么
        # 这里相当于我们把rule = Rule(rule=/index/,endpoint=None,view_func=函数)
        # 封装到这个类中
        rule = self.url_rule_class(rule, methods=methods, **options)
        rule.provide_automatic_options = provide_automatic_options
        # 这里的url_map相当于一个大字典,把每一个路哟关系放到字典里
        # 可以理解这里就是一个列表,这里就相当于存储的是好多个rule对象,
        # 存储所有的路由关系
        # 例如:
        """
        Map() = [
            Rule(rule=/index endpoint=None  view_func=函数),
            Rule(rule=/order endpoint=None  view_func=order),
        ]
        这里具体的化我们可以看它的url_map源码。再查看map做了什么,找到它的app;
        def add(self._rules):
            pass
        我们着重看这里,self._rules.append(rule);
        他把对象传到一个列表里,这个列表在def __init__()方法下的self._rules=[]中
        """
        self.url_map.add(rule)
        if view_func is not None:
            old_func = self.view_functions.get(endpoint)
            if old_func is not None and old_func != view_func:
                raise AssertionError('View function mapping is overwriting an '
                                     'existing endpoint function: %s' % endpoint)
            self.view_functions[endpoint] = view_func

基于CBV书写

from flask import Flask,views

app = Flask("xxxx",template_folder="templates")
# 例1:
class TestView(views.View):
    methods = ['GET']
    def dispatch_request(self):
        return 'test!'

app.add_url_rule('/test', view_func=TestView.as_view(name='test'))  # name=endpoint
# app.add_url_rule('/test', view_func=view函数)  # name=endpoint

def auth(func):
    def inner(*args, **kwargs):
        print('before')
        result = func(*args, **kwargs)
        print('after')
        return result
    return inner


# 例2:
class X1View(views.MethodView):
    methods = ['GET','POST']
	# 如果想要加装饰器的话,我们可以直接把装饰器添加到decorators 这个列表里,注意“,”;
    decorators = [auth, ]

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

    def post(self):
        return 'x1.POST'

app.add_url_rule('/x1', view_func=X1View.as_view(name='x1'))  # name=endpoint

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

源码分析:
这里我们分析例2:views.MethodView

class MethodView(with_metaclass(MethodViewType, View)):
    def dispatch_request(self, *args, **kwargs):
	    # 这里通过request.method.lower()找到请求方法;
        # 通过反射执(getattr)执行请求下的方法;
        meth = getattr(self, request.method.lower(), None)
        if meth is None and request.method == 'HEAD':
            meth = getattr(self, 'get', None)
        assert meth is not None, 'Unimplemented method %r' % request.method
        # 找到方法后一执行,如果事get方法就执行get.
        return meth(*args, **kwargs)

这里就是路由的本质

Flask之源码流程基本分析

from flask import Flask

app = Flask("xxx",)
@app.route("/index")
def index():
    return "index"
def order():
    return "Order"

app.add_url_rule("/order",None,order)

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

源码分析:
我们 已经在前边分析到,flask的请求入口时__call__方法。所以,我们看一它的__call__做了什么?

    def __call__(self, environ, start_response):
        """Shortcut for :attr:`wsgi_app`."""
        # Flask框架的请求入口
        # environ用户请求相关数据
        # start_response 用户响应数据
        return self.wsgi_app(environ, start_response)

我们把请求来的 数据转交给wsgi_app去处理;所以,Flask的代码开始是从这里

def wsgi_app(self, environ, start_response):
		# environ请求相关的数据
		# 我们就可以把相关的数据提取出来
		# 这里我们需要知道request_context都做了什么;
		'''
		def request_context(self, environ):
			return RequestContext(self, environ)
		'''
		# 类加构造传了两个值,构造方法一般用于封装;
		# 这里的self就是我们的请求对象就是app得数据,
		# ctx = RequestContext对象(app对象,请求相关所有数据)
		我们需要分析一下RequestContext到底做了什么,
		'''
		def __init__(self, app, environ, request=None):
	        self.app = app
	        if request is None:
		        # request = Request(environ)
	            request = app.request_class(environ)
	        self.request = request
	        self.url_adapter = app.create_url_adapter(self.request)
	        self.flashes = None
	        self.session = None
	        ……
	        # 路由匹配;获取 用户请求的url跟url_map中的对应关系进行比较
	        # 接下来就是执行rul,在接下来就是执行读url_map
            self.match_request()
		'''
		# 这里分装了好多东西,request我们没有传,如果request=None
		# app.request_class(environ)
		# 这里的request_class可能是个类,类加()实例化;
		# 这里是把请求相关的东西又做了一层封装。
		# 所以就是封装封装在封装。ctx里封装了app和request;
		# 而request又封装了请求相关的内容
		# 路由匹配
        ctx = self.request_context(environ)
        
        # 将请求放在特殊的位置
        ctx.push()
        error = None
        try:
            try:
	            # 这里就是找到视图函数并执行
                response = self.full_dispatch_request()
            except Exception as e:
                error = e
                response = self.handle_exception(e)
            except:
                error = sys.exc_info()[1]
                raise
            return response(environ, start_response)
        finally:
            if self.should_ignore_error(error):
                error = None

源码流程:

  • a、生成路由关系
    [Rule("index,函数,
    Rule("index",函数,]
  • b、用户请求来了
    获取用户请求,并进行匹配
    讲ctx(分装了请求相关所有数据的对象)放置到“特殊的位置“
  • c、执行视图函数,也就是我们写的代码
  • d、将函数的返回值返回给用户
  • e、最后讲ctx在特殊的位置移除。

PS:所有框架的流程都是相似的

Flask之before_request和after_request

import functools
from flask import Flask,render_template,request,redirect,session

app = Flask('xxxx',template_folder="templates")
app.secret_key = 'as923lrjks9d8fwlkxlduf'


@app.before_request
def bf1():
    print('before_request1')
    # return "BF1"
@app.before_request
def bf2():
    print('before_request2')

@app.after_request
def af1(response):
    print('after_request1')
    return response

@app.after_request
def af2(response):
    print('after_request2')
    return response

# app.before_request_funcs = {None: [bf1,bf2]}
# app.after_request_funcs = {None: [af1,af2]}

@app.route('/index',methods=['GET'])
def index():
    print('index')
    return "index"


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

########### 打印结果 ##############
before_request1
before_request2
index
after_request2
after_request1

注意:
@app.before_request@app.after_request这里需要注意,befor在执行视图之前执行,after在执行完视图之后执行。特别注意在befor装饰器下的函数不能又return值。从他的打印结果我们可以总结出请求进入时候,先执行顺序。这里跟django(1.10以后)又区别,例如:django的中间件,访问开始当中间件走到第三个时候,中间件验证不通过,直接从第三个中间件返回给用户。而Flask却恰恰相反。将所有的befor_request——>after_request走完,再从after_request——>befor_request全部执行完。

Flask之befor_request和after_request源码分析

&emsp:由上边代码可知,他是一个befor_request是一个装饰器。
我们先来看一下before_request做了什么

@setupmethod
def before_request(self, f):
     # setdefault默认是一个列表,将一个函数添加到一饿列表里,也就是把app.before_request装饰器下的函数,append到一个列表里
     # 本质上before_request_funcs = self.before_request_funcs = {}是一个字典,setdfault给这个字典设置值,这个key=None,[]可以理解为是一个列表;
     # {None:[bf1,bf2]}
     
     self.before_request_funcs.setdefault(None, []).append(f)
     return f

我们继续分析after_request

@setupmethod
def after_request(self, f):
		# after_request_funcs = self.after_request_funcs = {}
        self.after_request_funcs.setdefault(None, []).append(f)
        return f

和before_reqeust原理一样。
当请求过来时候还是走app.run()中的__call__方法;

    def __call__(self, environ, start_response):
        """Shortcut for :attr:`wsgi_app`."""
        # Flask框架的请求入口
        # environ用户请求相关数据
        # start_response 用户响应数据
        return self.wsgi_app(environ, start_response)

我们看一下wsgi_app

def wsgi_app(self, environ, start_response):
       ctx = self.request_context(environ)
      ctx.push()
      error = None
      try:
          try:
	          # 我们需要往里看,所以我们需要看一下full_dispatch_request
              '''
				def full_dispatch_request(self):
				       self.try_trigger_before_first_request_functions()
				        try:
				            request_started.send(self)
				            # 我们看一下啊preprocess_request,主要看这里
				            # funcs = self.before_request_funcs.get(None, ())
				            # 取key=None的,取到所有函数
				            # 接下来就是循环和执行,函数默认为空,一旦有一个不为空了
				            # 就会终止
				            # 这里就是执行所有的before_request
				            rv = self.preprocess_request()
				            # before_request全部通过
				            if rv is None:
					            # 执行视图函数
				                rv = self.dispatch_request()
				        except Exception as e:
				            rv = self.handle_user_exception(e)
				        # 我们看一下finalize_request函数做了什么,不管有没有返回值,都会执行这个地方。
				        # 我们主要看他里边的这个方法:response = self.process_response(response)做了什么
				        '''
				        def process_response(self, response):
				                ctx = _request_ctx_stack.top
						        bp = ctx.request.blueprint
						        funcs = ctx._after_request_functions
						        # 这里执行了after_request_func函数就是拿到了app.after_request的所有函数{None:[af1,af2]}
						        if bp is not None and bp in self.after_request_funcs:
						            funcs = chain(funcs, reversed(self.after_request_funcs[bp]))

								# 
						        if None in self.after_request_funcs:
							        # 这里拿到[af2,af1]就形成了倒叙
						            funcs = chain(funcs, reversed(self.after_request_funcs[None]))
					            # 这里就形成了我们看到的打印结果回去时倒叙
						        for handler in funcs:
						            response = handler(response)
						        if not self.session_interface.is_null_session(ctx.session):
						            self.save_session(ctx.session, response)
						        return response
				        '''
				        return self.finalize_request(rv)
	      	  '''
	      	  
              response = self.full_dispatch_request()
          except Exception as e:
              error = e
              response = self.handle_exception(e)
          except:
              error = sys.exc_info()[1]
              raise
          return response(environ, start_response)
      finally:
          if self.should_ignore_error(error):
              error = None
          ctx.auto_pop(error)


请求和响应

http请求

# 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')


响应头+Cookie:
from flask import make_response

# response = make_response("字符串")
# response = make_response(render_template('index.html'))
# response = make_response(redirect('/index.html'))

# response.delete_cookie('key')
# response.set_cookie('key', 'value')
# response.headers['X-Something'] = 'A value'
# return response

模板引擎

1、模板的使用
Flask使用的时Jinja2模板,所以其语法和Django无差别
2、自定义模板方法
Flask中定义模板方法的方式和Bottle相似,创建一个函数并通过参数形式传入render_template:
例如:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>自定义函数</h1>
    {{ww()|safe}}

</body>
</html>
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Flask,render_template
app = Flask(__name__)
 
 
def wupeiqi():
    return '<h1>Wupeiqi</h1>'
 
@app.route('/login', methods=['GET', 'POST'])
def login():
    return render_template('login.html', ww=wupeiqi)
 
app.run()

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>


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

    {{ input('n1') }}

    {% include 'tp.html' %}

    <h1>asdf{{ v.k1}}</h1>
</body>
</html>

注意:Markup等价django的mark_safe

session详解

除请求对象之外,还有一个 session 对象。它允许你在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥。

  • 设置:session["username"] = "xxx"
  • 删除:session.pop("username",None)
from flask import Flask,session,request,redirect,Markup,render_template
app = Flask("xx",)
app.secret_key = "jklfajfjoqenvcouqwova"
@app.route("/index",methods=["GET"])
def index():
    # session就是一个特殊的字典;本质上就是继承了dict;

    session["k1"] = 1231
    session["k2"] = 321
    del session["k1"]
    return "index"

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

请求来了之后,会先执行__call__方法。进入__call__方法可以看到有一个 return self.wsgi_app(environ, start_response)进入wsgi_app;这里边执行了一个berfor_request和view

def wsgi_app(self, environ, start_response):
       ctx = self.request_context(environ)
       ctx.push()
       '''
		def push(self):
		
			……
			# 这时候self.session就有值了;
			# 去用户请求的cookie中获取值,有就放上值,没有就是空的;
			# 特殊的字典= from flask import session
			# 我们猜测:返回一个继承自dict类的对象,所以
			# open_session方法中又返回了一个session_interface.open_session(self,request)
			# 当我们查看session_interface这个对象时他就 = SecureCookieSessionInterface()
			# 也就是 SecureCookieSessionInterface对象的open_session方法;
			'''
		   def open_session(self, app, request):
			        # s 是加密规则
			        s = self.get_signing_serializer(app)
			        if s is None:
			            return None
			        # 去用户cookies中取值
			        val = request.cookies.get(app.session_cookie_name)
			        # 没找到,用户第一次来
			        if not val:
			            # 这里session_class(是一个特殊的空字典,
			            # session_class = SecureCookieSession 它的父类一定会有一个dict
			            return self.session_class()
                   max_age = total_seconds(app.permanent_session_lifetime)
			       try:
				       # 创建特殊的字典,并将原来cookie中存储的值,再次放入内存,共用户使用。
			           data = s.loads(val, max_age=max_age)
			           return self.session_class(data)
			       except BadSignature:
			           return self.session_class()
			'''
			self.session = self.app.open_session(self.request)
	        if self.session is None:
	            self.session = self.app.make_null_session()

       '''
       error = None
       try:
           try:
               # 1、before_request
               # 2、视图函数,
               # 在执行这两个方法之前要执行ctx.push()
               
               response = self.full_dispatch_request()
           except Exception as e:
               error = e
               response = self.handle_exception(e)
           except:
               error = sys.exc_info()[1]
               raise
           return response(environ, start_response)
       finally:
           if self.should_ignore_error(error):
               error = None
           ctx.auto_pop(error)


flask的session请求流程

其实self.session的值就是从secureCookieSession(dict)中—__init__返回来的。在requestContext封装了很多的东西,其中就有session。咱们导入的session其实就是requestContex中拿到的session。
我们from flask import session的 实质就是requestContext中拿到session,这只是第一步,请求刚进来我们把session放到这里

未完待续………………

摘抄博客: 武Sir

posted @ 2018-04-17 10:02  zz小公子  阅读(233)  评论(0编辑  收藏  举报