Flask基礎

   開篇引入

1.django和flask的相同點和不一樣點?css

共同點:都是基於wsgi的
不一樣點:
(1) django是一個大而全的框架,提供了方便內置的框架,orm,admin,分頁,form,model_form,緩存,信號等不少方便的組件,
咱們只要在配置文件中一修改就可使用。
(2) flask是一個短小精悍,可擴展性很是強,flask適合開發小型的網站,也能夠開發大型網站,由於它給咱們提供有許多第三方組件,咱們就能夠結合這些第三方組件集成一個像django同樣擁有不少功能的web框架。可定製性很是強。
(3) flask和django最大的不一樣點:request/session
flask是直接導入的,request在全局起做用。
django是依附於request參數,經過參數傳導。
兩個框架沒有優劣之分,具體應用看實際需求。

2.什麼是wsgi?html

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框架。python

特點:上下文管理機制
wsgi:web service getway interface web服務網管接口
依賴wsgi:werkzurgmysql

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、配置文件

1.使用:全部配置都在app.config中web

  • 方式一: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',能夠找到類並獲取其中大寫的靜態字段。sql

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,
})
默認配置文件

  2、路由系統

重點:基於裝飾器實現數據庫

技術點:-functools.wraps(func):保留原函數的原信息django

裝飾器(帶參數)

  • 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

  3、視圖

 視圖: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

  4、請求相關

  技術點:面向對象的封裝

  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請求

   5、響應相關

技術點:面向對象的封裝

響應體: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'))

應用場景:比較多的函數中須要添加額外的功能
版本三

  6、模板渲染

基本數據類型:能夠執行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)

   7、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'
閃現示例

  8、中間件

      通常不經常使用,由於它的執行順序很靠前,無序獲取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()
中間件示例

  9、特殊的裝飾器

    技術點: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()
裝飾器測試

  10、藍圖

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

  11、多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-width: 666px) {
            .pg{
                background-color: green;
            }
        }
        @media (min-width: 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進階問題預覽
相關文章
相關標籤/搜索