• Flask基础


       开篇引入

    1.django和flask的相同点和不同点?

    共同点:都是基于wsgi的
    不同点:
    (1) django是一个大而全的框架,提供了方便内置的框架,orm,admin,分页,form,model_form,缓存,信号等很多方便的组件,
    我们只要在配置文件中一修改就可以使用。
    (2) flask是一个短小精悍,可扩展性非常强,flask适合开发小型的网站,也可以开发大型网站,因为它给我们提供有许多第三方组件,我们就可以结合这些第三方组件集成一个像django一样拥有很多功能的web框架。可定制性非常强。
    (3) flask和django最大的不同点:request/session
    flask是直接导入的,request在全局起作用。
    django是依附于request参数,通过参数传导。
    两个框架没有优劣之分,具体应用看实际需求。

    2.什么是wsgi?

    web服务网关接口,wsgi是一个协议,实现该写一个的模块:
    - wsgiref
    - werkzeug
    实现协议的模块本质上就是socket服务端用于接收用户请求,并处理。
    一般web框架基于wsgi实现,这样实现关注点分离,主要负责业务处理。
     1 from wsgiref.simple_server import make_server
     2 
     3 def run_server(environ, start_response):
     4 start_response('200 OK', [('Content-Type', 'text/html')])
     5 return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ]
     6 
     7 
     8 if __name__ == '__main__':
     9 httpd = make_server('127.0.0.1', 8000, run_server)
    10 httpd.serve_forever()
    wsgiref示例
    1 from werkzeug.wrappers import Response
    2 from werkzeug.serving import run_simple
    3 
    4 def run_server(environ, start_response):
    5     response = Response('hello')
    6     return response(environ, start_response)
    7 
    8 if __name__ == '__main__':
    9     run_simple('127.0.0.1', 8000, run_server)
    werkzeug示例
    from werkzeug.wrappers import Response
    from werkzeug.serving import run_simple
    
    class Flask(object):
        def __call__(self,environ, start_response):
            response = Response('hello')
            return response(environ, start_response)
    
        def run(self):
            run_simple('127.0.0.1', 8000, self)
    
    
    
    app = Flask()
    
    if __name__ == '__main__':
        app.run()
    flask源码入口

      基础知识

    安装

      pip3 install flask

    特点: 短小精悍、可扩展强 的一个Web框架。

    特色:上下文管理机制
    wsgi:web service getway interface web服务网管接口
    依赖wsgi:werkzurg

    from werkzeug.wrappers import Request, Response
    from werkzeug.serving import run_simple
    
    def run(environ,start_response):
        response = Response('hello')
        return response(environ, start_response)
    
    if __name__ == '__main__':
        run_simple('localhost', 4000, run)
    werkzurg示例一
    from werkzeug.wrappers import Request, Response
    
    @Request.application
    def hello(request):
        return Response('Hello World!')
    
    if __name__ == '__main__':
        from werkzeug.serving import run_simple
        run_simple('localhost', 4000, hello)
    werkzurg示例2

       一、配置文件

    1.使用:所有配置都在app.config中

    • 方式一:app.config['DEBUG'] = True

        PS: 由于Config对象本质上是字典,所以还可以使用app.config.update(...)

    • 方式二:app.config.from_object('类的路径')
    • 方式三:app.config.from_pyfile('py文件路径')
    class Config(object):
        DEBUG = False
        TESTING = False
        DATABASE_URI = 'sqlite://:memory:'
    
    
    class ProductionConfig(Config):
        DATABASE_URI = 'mysql://user@localhost/foo'
    
    
    class DevelopmentConfig(Config):
        DEBUG = True
    
    
    class TestingConfig(Config):
        TESTING = True
    settings.py

    2.实现原理
      指定一个字符串(类的路径/文件路径),importlib-->getattr 找到这个类/模块把这个类/模块的所有字段(大写)一个一个获取,获取的时候判断isupper(),给一个路径'settings.Foo',可以找到类并获取其中大写的静态字段。

    import importlib
    
    path = 'settings.Foo'
    
    p,c = path.rsplit('.',maxsplit=1)
    m = importlib.import_module(p)
    # m = __import__(p)
    cls = getattr(m,c)
    
    
    for key in dir(cls):
        if key.isupper():
            print(key,getattr(cls,key))
    实现原理.py
    'ENV':                                  None,
    'DEBUG':                                None,                        是否开启DEBUG模式
    '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,
    })
    默认配置文件

      二、路由系统

    重点:基于装饰器实现

    技术点:-functools.wraps(func):保留原函数的原信息

    装饰器(带参数)

    • methods=['GET','POST']
    • endpoint:反向生成url
    • url_for(endpoint)

    自定义装饰器放下面
      注意事项:

    • - endpoint默认是函数名
    • - 不要让endpoint重名,如果重名函数也一定要相同。
    • - 加装饰器时要加functools.wraps(func) + functools.partial
    # -*- coding: utf-8 -*-
    
    """
    @Datetime: 2018/12/21
    @Author: Zhang Yafei
    """
    """1.装饰器"""
    from functools import wraps
    
    
    def auth(func):
        @wraps(func)   # 伪装的更彻底
        def inner(*args,**kwargs):
            print('')
            ret = func(*args,**kwargs)
            print('')
            return ret
        return inner
    
    
    @auth
    def index():
        print('index')
    
    
    @auth
    def detail():
        print('index')
    
    
    print(index.__name__)
    print(detail.__name__)
    
    """2.endpoint默认是函数名"""
    装饰器注意点

    路由设置的两种方式 

    方式一
        @app.route('/xxx')
            def index():
                return "index"
    
    方式二
        def index():
            return "index"
        app.add_url_rule("/xxx",None,index)
    路由设置的两种方式
    -动态路由
        /index/<int:nid>
        def index(nid):
            print(nid)
            return 'index'
    动态路由
    1 rule,                       URL规则
    2 view_func,                  视图函数名称
    3 endpoint=None,              名称,用于反向生成URL,即: url_for('名称')
    4 methods=None,               允许的请求方式,如:["GET","POST"]
    5 strict_slashes=None,        对URL最后的 / 符号是否严格要求,
    6 redirect_to=None,           重定向到指定地址
    7 
    8 defaults=None,              默认值,当URL中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数
    9 subdomain=None,             子域名访问
    参数
    from flask import Flask,url_for
    
    app = Flask(__name__)
    
    # 步骤一:定制类
    from werkzeug.routing import BaseConverter
    class RegexConverter(BaseConverter):
        """
        自定义URL匹配正则表达式
        """
    
        def __init__(self, map, regex):
            super(RegexConverter, self).__init__(map)
            self.regex = regex
    
        def to_python(self, value):
            """
            路由匹配时,匹配成功后传递给视图函数中参数的值
            :param value:
            :return:
            """
            return int(value)
    
        def to_url(self, value):
            """
            使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
            :param value:
            :return:
            """
            val = super(RegexConverter, self).to_url(value)
            return val
    
    # 步骤二:添加到转换器
    app.url_map.converters['reg'] = RegexConverter
    
    """
    1.用户发送请求
    2.flask内部进行正则匹配
    3.调用to_python(正则匹配的结果)方法
    4.to_python方法的返回值会交给视图的函数
    """
    
    # 步骤三:使用自定义正则
    @app.route('/index/<reg("d+"):nid>')
    def index(nid):
        print(nid,type(nid))
        print(url_for('index',nid=987))
        return 'index'
    
    
    if __name__ == '__main__':
        app.run()
    自定义正则
    @main.route('/data_list/<table_name>')
    def data_list(table_name):
        df = reader.enable_admins[table_name]
        page_obj = Pagination(df['data'].shape[0], request.args.get('p'),per_page_num=5)
        page_str = page_obj.page_str(base_url=url_for('main.data_list', table_name=table_name))
        admin_class = df['admin_class']
        data_list = df['data'][page_obj.start:page_obj.end]
        context = {'table_name':table_name, 'data_list':data_list,
                   'page_obj':page_obj, 'page_str':page_str,
                   'admin_class':admin_class,
                   }
        return render_template('data_list.html', **context)
    动态路由2

      三、视图

     视图:FBV和CBV 

     技术点:反射

    视图函数中获取request或session
        方式一:直接找LocalStack获取
                from flask.globals import _request_ctx_stack
                print(_request_ctx_stack.top.request.method)
                
        方式二:通过代理LocalProxy(小东北)获取
                from flask import Flask,request
                print(request.method)
    FBV
    import functools
    from flask import Flask,views
    app = Flask(__name__)
    
    
    def wrapper(func):
        @functools.wraps(func)
        def inner(*args,**kwargs):
            return func(*args,**kwargs)
    
        return inner
    
    
    class UserView(views.MethodView):
        methods = ['GET']
        decorators = [wrapper,]
    
        def get(self,*args,**kwargs):
            return 'GET'
    
        def post(self,*args,**kwargs):
            return 'POST'
    
    app.add_url_rule('/user',None,UserView.as_view('uuuu'))
    
    if __name__ == '__main__':
        app.run()
    CBV

      四、请求相关

      技术点:面向对象的封装

      request属性

     # 请求相关信息
            # request.method  请求方法 GET/POST
            # request.args   GET请求参数    
            # request.form   POST请求参数
            # request.values   GET和POST请求参数
            # 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
            # request.files
            # obj = request.files['the_file_name']
            # obj.save('/var/www/uploads/' + secure_filename(f.filename))
        print('headers:', request.headers)
        print('cookies:', request.cookies)
        print('path:', request.path)
        print('full_path:', request.full_path)
        print('url:', request.url)
        print('script_root:', request.script_root)
        print('base_url:', request.base_url)
        print('url_root:', request.url_root)
        print('host:', request.host)
        print('host_url:', request.host_url)
    
    headers: Host: 172.25.0.246:8999
    Connection: keep-alive
    Content-Length: 37
    Pragma: no-cache
    Cache-Control: no-cache
    User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 S
    afari/601.1 wechatdevtools/1.02.1901230 MicroMessenger/6.7.3 Language/zh_CN webview/ token/a46ac17280ff1fcb82b684b2084ee168
    Origin: http://127.0.0.1:23672
    Authorization: f3c4e30debf4030ace5c3cdf40e6332d#1
    Content-Type: application/x-www-form-urlencoded
    Accept: */*
    Referer: https://servicewechat.com/wx49bec3712cb29bf5/devtools/page-frame.html
    Accept-Encoding: gzip, deflate
    
    
    cookies: {}
    path: /api/member/check-reg
    full_path: /api/member/check-reg?
    url: http://172.25.0.246:8999/api/member/check-reg
    script_root:
    base_url: http://172.25.0.246:8999/api/member/check-reg
    url_root: http://172.25.0.246:8999/
    host: 172.25.0.246:8999
    host_url: http://172.25.0.246:8999/
    示例
    @main.route('/data_list/<table_name>')
    def data_list(table_name):
        df = reader.enable_admins[table_name]
        page_obj = Pagination(df['data'].shape[0], request.args.get('p'),per_page_num=5)
        page_str = page_obj.page_str(base_url=url_for('main.data_list', table_name=table_name))
        admin_class = df['admin_class']
        data_list = df['data'][page_obj.start:page_obj.end]
        context = {'table_name':table_name, 'data_list':data_list,
                   'page_obj':page_obj, 'page_str':page_str,
                   'admin_class':admin_class,
                   }
        return render_template('data_list.html', **context)
    get请求
    @ac.route('/login',methods=['GET', 'POST'])
    def login():
        if request.method == 'GET':
            return render_template('login.html')
        username = request.form.get('username')
        password = request.form.get('pwd')
        """数据库验证"""
        password = get_md5(password)
        # conn = Connect(host='localhost', user='root', password='0000', database='flask_code', charset='utf8')
        # cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)   # 每一行是字典
        # cursor.execute("select id,username,nickname from userinfo where username=%(us)s and password=%(pw)s",{'us':username,'pw':password})
        # data = cursor.fetchone()
        data = fetchone("select id,username,nickname from userinfo where username=%(us)s and password=%(pw)s",{'us':username,'pw':password})
        if not data:
            return render_template('login.html',error='用户名或密码错误')
    
        session['user_info'] = {'id':data['id'],'username':data['username'],'nickname':data['nickname']}
        if request.form.get('remember'):
            session.permanent = True
            ac.permanent_session_lifetime = timedelta(days=31)
    
        return redirect('/home')
    post请求

       五、响应相关

    技术点:面向对象的封装

    响应体:4种

    return '欢迎使用'
    return jsonify({'k1':'v1'})
    return render_template('xxx.html')
    return redirect('/index')
    4种响应体

    定制响应头

    obj = make_response(render_template('index.html'))
    obj.headers['xxxxx'] = '123'
    obj.set_cookie('key','value')
    return obj
    定制响应头

    示例程序:用户访问限制

    @app.route('/detail/<int:nid>')
    def detail(nid):
        if not session.get('user'):
            return redirect(url_for('login'))
        info = STUDENT_DICT[nid]
        return render_template('detail.html', info=info)
    版本一
    def auth(func):
        @wraps(func)
        def inner(*args,**kwargs):
            if not session.get('user'):
                return redirect(url_for('login'))
            ret = func(*args,**kwargs)
            return ret
        return inner
            
    @app.route('/index')
    @auth
    def index():
        return render_template('index.html',stu_dict=STUDENT_DICT)
        
    应用场景:比较少的函数中需要添加额外的功能
    版本二
    @app.before_request
    def xxxx():
        if request.path == '/login':
            return None
        if not session.get('user'):
            return None
        return redirect(url_for('login'))
    
    应用场景:比较多的函数中需要添加额外的功能
    版本三

      六、模板渲染

    基本数据类型:可以执行python语法,如:dict.get()   list['xx']

    控制代码块:条件语句和循环语句

    - if/else if /else / endif
    - for / endfor
        <ul class="pagination">
            {% if page_obj.num_pages > 1 %}
                {{ page_str }}
            {% else %}
            {% endif %}
        </ul>
    if else
       <table class="table table-bordered">
            <thead>
            <tr style="background-color: pink">
                <th>序号</th>
                <th>文件名</th>
                <th>样本数</th>
                <th>特征数</th>
                <th>操作</th>
            </tr>
            </thead>
            <tbody>
            {% for k,v in data_list.items() %}
                <tr>
                    <td>{{ loop.index }}</td>
                    <td><a href="{{ url_for('main.data_list',table_name=k)}}">{{ k }}</a></td>
                    <td>{{ v['data'].shape }}</td>
                    <td>{{ v['data'].shape }}</td>
                    <td><a href="{{ url_for('main.data_list',table_name=k)}}">查看</a></td>
                </tr>
            {% endfor %}
            </tbody>
        </table>
    for循环

    除此之外,还有一个特殊的循环函数loop

    • 在循环内部,你可以使用一个叫做loop的特殊变量来获得关于for循环的一些信息

      • 比如:要是我们想知道当前被迭代的元素序号,并模拟Python中的enumerate函数做的事情,则可以使用loop变量的index属性,例如:

      • {% for post in posts%}
        {{loop.index}}, {{post.title}}
        {% endfor %}
        会输出这样的结果
        1, Post title2, Second Post
      • cycle函数会在每次循环的时候,返回其参数中的下一个元素,可以拿上面的例子来说明:
      • {% for post in posts%}
        {{loop.cycle('odd','even')}} {{post.title}}
        {% endfor %}
        会输出这样的结果:
        
        odd Post Title
        even Second Post

    传入函数

    • -django,自动执行
    • -flask,不自动执行

    自定义函数

    @app.template_global()
    def sb(a,b):
        return a+b
    
    @app.template_filter() # 适合if 判断
    def db(a,b,c):
        return a+b+c
    全局定义函数
    html
    {{ 1|db(2,3) }}
        {% if 1|db(2,3) %}
            <div>666</div>
        {% else %}
            <div>999</div>
        {% endif %}
    
    @app.template_filter()   # 适合if 判断
    def db(a,b,c):
        return a+b+c
    template_filter()
    用装饰器来实现自定义过滤器。装饰器传入的参数是自定义的过滤器名称。
    
    @app.template_filter('lireverse')
    def do_listreverse(li):
        # 通过原列表创建一个新列表
        temp_li = list(li)    # 将新列表进行返转
        temp_li.reverse()    return temp_li
    
    在 html 中使用该自定义过滤器
    
    <br/> my_array 原内容:{{ my_array }}<br/> my_array 反转:{{ my_array | lireverse }}
    运行结果
    
    my_array 原内容:[3, 4, 2, 1, 7, 9] 
    my_array 反转:[9, 7, 1, 2, 4, 3]
    例:添加列表反转的过滤器

    模板继承

    layout.html
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Title</title>
        </head>
        <body>
            {% block content %}{% endblock %}
        </body>
        </html>
        
    tpl.html
        {% extends 'layout.html' %}
        {% block content %}
            {% include 'form.html' %}
    
            {% macro ccccc(name,type='text', value='') %}
                <h1>宏</h1>
                <input type="{{ type }}" name="{{ name }}" value="{{ value }}">
                <input type="submit" name="提交">
            {% endmacro %}
    
            {{ ccccc('n1') }}
            {{ ccccc('n2') }}
    
        {% endblock %}
    模板继承extends
    <include 'from.html'>
    
    form.html
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Title</title>
        </head>
        <body>
        <div>sasasasa sasasasasasa</div>
        </body>
    </html>
    include

    {% macro ccccc(name,type='text', value='') %}
        <h1>宏</h1>
        <input type="{{ type }}" name="{{ name }}" value="{{ value }}">
        <input type="submit" name="提交">
    {% endmacro %}
    
    {{ ccccc('n1') }}
    {{ ccccc('n2') }}

    安全

       前端:{{ txt|safe }}
       后端:Markup(txt)

       七、session

      原理:加密后放置在用户浏览器的cookie中

      流程:

    请求到来:flask读取cookie中的session对应的值:eyJrMiI6NDU2LCJ1c2VyIjoiZmVpIn0,将该值解密并反序列化成字典,放入内存以便视图函数使用。
    视图函数:
        @app.route('/sess')
        def sess():
            print(session.__class__)
            session['k1'] = 123
            session['k2'] = 456
            del session['k1']
            return 'Session'
    
    请求结束,flask会读取内存中字典的值,进行序列化+加密,写入用户cookie中。
    session执行流程

      实现原理(源码)

    def wsgi_app(self, environ, start_response):
                """
                1.获取environ并对其进行再次封装
                2.从environ中获取名称为session的cookie,解密,反序列化
                3.两个东西放到‘某个神奇'的地方
                """
                # ctx = RequestContext(self, environ)  #self是app对象,evviron是原始数据对象
                # ctx.request = Request(environ)
                # ctx.session = None
                ctx = self.request_context(environ)
                error = None
                try:
                    try:
                        ctx.push()
                        # 4. 执行视图函数
                        # 5.'某个神奇’获取session,加密,序列化,写入cookie
                        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
                    """
    6.'某个神奇'位置清空
    """
    ctx.auto_pop(error)
    源码

       闪现(flash): 在session存储一个数据,读取时通过pop将数据删除,形成一种数据只能取一次的效果

    from flask import Flask,flash,get_flashed_messages
    
    @app.route('/page1')
    def page1():
        flash('临时数据存储','error')
        flash('sasasasa','error')
        flash('sasasas','info')
        return 'Session'
    
    @app.route('/page2')
    def page2():
        print(get_flashed_messages(category_filter=['error']))
        return 'Session'
    闪现示例

      八、中间件

          一般不常用,因为它的执行顺序很靠前,无序获取request对象,与请求相关信息难以获得

    • call方法什么时候触发?

        -用户发起请求时,才执行

    • -任务:在执行call方法之前,做一个操作,call方法执行之后,做一个操作
    class Middleware(object):
        def __init__(self,old):
            self.old = old
    
        def __call__(self, *args, **kwargs):
            print('')
            ret = self.old(*args,**kwargs)
            print('')
    return ret
    
    if __name__ == '__main__':
        app.wsgi_app = Middleware(app.wsgi_app)
        app.run()
    中间件示例

      九、特殊的装饰器

        技术点:before_request和after_request的实现原理

     六大装饰器

    before_first_request
    
    before_request 视图函数之前,原理是将视图函数放入到一个列表中,循环,如果有返回值停止循环,后面的函数也将不会执行
    
    after_request  视图函数之后,原理是将视图函数放入到一个列表中reverse,循环执行
    
    template_global
    
    template_filter
    
    errorhandler
        @app.errorhandler(404)
        def not_found(arg):
            print(arg)
            return '404 没找到'
    # -*- coding: utf-8 -*-
    
    """
    @Datetime: 2018/12/21
    @Author: Zhang Yafei
    """
    from flask import Flask
    
    app = Flask(__name__)
    
    
    @app.before_first_request
    def x():
        print('before_first')
    
    
    @app.before_request
    def x1():
        print('before:x1')
    
    
    @app.before_request
    def xx1():
        print('before:xx1')
    
    
    @app.after_request
    def x2(response):
        print('after:x2')
        return response
    
    
    @app.after_request
    def xx2(response):
        print('after:xx2')
        return response
    
    
    @app.route('/index')
    def index():
        print('index')
        return 'index'
    
    
    @app.route('/order')
    def order():
        print('order')
        return 'order'
    
    
    @app.errorhandler(404)
    def not_found(arg):
        print(arg)
        return '404 没找到'
    
    
    if __name__ == '__main__':
        app.run()
    装饰器测试

      十、蓝图

     (1) 目标:目录结构的划分
     (2) 自定义模板、静态文件

    admin = Blueprint(
      'admin',
      __name__,
      template_folder='templates',
      static_folder='static'
    )

    (3)  给某一类url添加前缀变量 app.register_blueprint(admin, url_prefix='/admin')
    (4)  给一类url添加before_request

    @app.before_request
    def x1():
        print('app.before_request')

      十一、多app

    from flask import Flask
    from werkzeug.wsgi import DispatcherMiddleware
    from werkzeug.serving import run_simple
    
    app1 = Flask("app1")
    app1.config['DB'] = 123
    
    app2 = Flask("app2")
    app1.config['DB'] = 456
    
    
    @app1.route('/web')
    def web():
        print('web')
        return '12213'
    
    
    @app1.route('/news')
    def news():
        print('news')
        return '12213'
    
    
    @app2.route('/admin')
    def admin():
        print('admin')
        return '12213'
    
    
    @app2.route('/article')
    def article():
        print('article')
        return '12213'
    
    
    """
    /web
    /new
    /app2/admin
    /app2/article
    """
    app = DispatcherMiddleware(app1, {
        '/app2': app2,
    })
    
    
    if __name__ == '__main__':
        run_simple(hostname='127.0.0.1', port=5000, application=app)
    mul_app.py
    from multi_app import app1
    from multi_app import app2
    
    
    with app1.app_context():
        pass # 为app1创建数据库
        with app2.app_context():
            pass  # 为app2创建数据库
    离线脚本

      什么是响应式布局?                                                               

    技术点:@media

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" />
    </head>
    <body>
        <div class="row" style="background-color: #28a4c9">
          <div class="col-lg-6">.col-lg-6</div>
          <div class="col-lg-6">.col-lg-6</div>
        </div>
        <div class="row" style="background-color: #67b168">
            <div class="col-md-6">.col-md-6</div>
            <div class="col-md-6">.col-md-6</div>
        </div>
        <div class="row" style="background-color: red">
            <div class="col-sm-6">.col-sm-6</div>
            <div class="col-sm-6">.col-sm-6</div>
        </div>
        <div class="row" style="background-color: gold">
            <div class="col-xs-6">.col-xs-6</div>
            <div class="col-xs-6">.col-xs-6</div>
        </div>
    </body>
    </html>
    响应式布局.html
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <style>
            body{
                margin: 0;
            }
            .pg{
                width: 100%;
                background-color: rebeccapurple;
            }
            @media (min- 666px) {
                .pg{
                    background-color: green;
                }
            }
            @media (min- 888px) {
                .pg{
                    background-color: red;
                }
            }
        </style>
    </head>
    <body>
        <div>
            <div class="pg">asdfasdf</div>
        </div>
    </body>
    </html>
    本质

    注:

    1. 上下文管理的实现?
        当请求到来的时候,
            flask会把请求相关和session相关信息封装到一个ctx = RequestContext对象中,
            会把app和g封装到app_ctx = APPContext对象中去,
            通过localstack对象将ctx、app_ctx对象封装到local对象中
    
        获取数据(执行视图函数的时候)
            通过导入一个模块,用模块.的方式获取我们想要的数据
            实现细节:
                通过localproxy对象+偏函数,调用localstack去local中获取对应的响应ctx、app_ctx中封装值
                问题:为什么要把ctx = request/session  app_ctx = app/g
                    因为离线脚本需要使用app_ctx
        请求结束
            调用localstk的pop方法,将ctx和app_ctx移除    
    2. 为什么把上下文管理分成:
        - 应用上下文:request/session
        - 请求上下文: app/g 
        离线脚本应用
                
    3. Local的作用?
        类似于threading.local的作用,但是是他的升级版(greentlet.get_current())
        __storage__ = {
            1231: {},
            1231: {}
        }
    4. LocalStack作用?
        将Local中__storage__的值维护成一下结构:
        __storage__ = {
            1231: {stack:[],},
            1231: {stack:[],}
        }
    5. 为什么要维护成一个栈?
    
    6. 为什么导入request,就可以使用?
        每次执行request.xx方法时,会触发LocalProxy对象的__getattr__等方法,由方法每次动态的使用
        LocalStack去Local中获取数据
    flask进阶问题预览
  • 相关阅读:
    RedisTemplate实现事物问题剖析和解决
    PO BO VO DTO POJO DAO概念及其作用(附转换图)
    Java 应用程序设计规范
    Java web url 规范
    Java 中 Map与JavaBean实体类之间的相互转化
    使用Java 8中的Stream
    [转]http://lua-users.org/wiki/LpegTutorial
    [转]LUA元表
    LPEG
    [转]LUA 学习笔记
  • 原文地址:https://www.cnblogs.com/zhangyafei/p/10204463.html
Copyright © 2020-2023  润新知