FLASK


1 Flask介紹和快速使用

#介紹
Flask是一個基於Python開發並且依賴jinja2模板(DTL)和Werkzeug(wsgiref) WSGI服務的一個微型框架,對於Werkzeug本質是Socket服務端,其用於接收http請求並對請求進行預處理,然后觸發Flask框架,開發人員基於Flask框架提供的功能對請求進行相應的處理,並返回給用戶,如果要返回給用戶復雜的內容時,需要借助jinja2模板來實現對模板的處理,即:將模板和數據進行渲染,將渲染后的字符串返回給用戶瀏覽器
# flask項目的所有代碼都可以寫在一個py文件中(一般不用),可擴展性高
# 通過集成很多第三方插件實現跟django一樣的功能

# wsgiref
from flask import Flask

app = Flask(__name__)


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


if __name__ == '__main__':
    app.run()  # 把flask項目跑起來

2 登錄,顯示用戶信息案例

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

# flask中的請求對象是全局的request
# render_template 就是django的render

app = Flask(__name__)
# 使用session,一定要設置秘鑰,等同於django配置文件中的密碼
app.secret_key = 'adfae^^4384045532@@#$#'
app.debug = True  # 啟用調試模式,修改了代碼不用重啟,自動重啟
USERS = {
    1: {'name': '張三', 'age': 18, 'gender': '男', 'text': "道路千萬條"},
    2: {'name': '李四', 'age': 28, 'gender': '男', 'text': "安全第一條"},
    3: {'name': '王五', 'age': 18, 'gender': '女', 'text': "行車不規范"},
}


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        # request.form 等同於request.POST
        name = request.form.get('user')
        password = request.form.get('pwd')
        if name == 'lqz' and password == '123':
            # 登錄成功,重定向到首頁
            # 把登錄信息放到session中
            session['name'] = name
            return redirect('/')
        else:
            return jsonify({'code': 101, 'msg': '用戶名或密碼錯誤'})


@app.route('/', methods=['GET'])
def index():
    if session.get('name'):
        return render_template('index.html', user_dict=USERS)
    else:
        # 沒有登錄,重定向到登陸頁面
        return redirect('/login')


# @app.route('/detail/<int:pk>', methods=['GET'])
# def detail(pk):
@app.route('/detail', methods=['GET'],endpoint='detail')
def detail():
    print(request.query_string)  # b'pk=1&name=lqz&age=19'
    pk = int(request.args.get('pk'))
    print(pk)
    user_detail = USERS.get(pk)
    return render_template('detail.html', info=user_detail)


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

### 總結
'''
0 當次請求的對象,request對象是全局的,直接導入使用即可
1 前端post提交的數據,從flask中的:request.form中取
2 轉換器的使用/detail/<int:pk>
3 模板語法兼容django的dtl,可以直接使用函數加括號,並且可以傳參數
4 session也是全局對象,用戶登錄信息,放到session中
5 新手四件套
    '字符串'---》HttpResonse('字符串')
    redirect('/')---》redirect('/')
    render_template()--->render()
    jsonify          ---->JsonResponse()
5 前端get請求提交的數據,從request.args
6 request.query_string是get請求 ? 后面的內容,bytes格式
7 @app.route('/detail', methods=['GET'],endpoint='detail')  endpoint等同於django路由中的name別名
'''

2.2 detail.html

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

</head>
<body>
 <h1>詳細信息 {{info.name}}</h1>
    <div>
        {{info.text}}
    </div>
</body>
</html>

2.3 index.html

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

</head>
<body>
    <h1>用戶列表</h1>
    <table>
        {% for k,v in user_dict.items() %}
        <tr>
            <td>{{k}}</td>
            <td>{{v.name}}</td>
            <td>{{v['name']}}</td>
            <td>{{v.get('name')}}</td>
            <td><a href="/detail/{{k}}">查看詳細1</a></td>
            <td><a href="/detail?pk={{k}}&name=lqz&age=19">查看詳細2</a></td>
        </tr>
        {% endfor %}
    </table>
</body>
</html>

2.4 login.html

<!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="text" name="pwd">
    <input type="submit" value="登錄">{{error}}
</form>
</body>
</html>

3 Flask配置文件

# flask.config.Config


方式一:直接寫app.config['DEBUG']    一般不用
方式二:放到py文件中                  一般不用
方式三:通過環境變量                  一般不用
app.config.from_envvar("環境變量名稱")
app.config.from_json("json文件名稱")
JSON文件名稱,必須是json格式,因為內部會執行json.loads
app.config.from_mapping({'DEBUG': True})

方式四: 通過類的方式                 用的多
app.config.from_object('settings.DevelopmentConfig')

0 函數加裝飾器的執行順序

# flask的路由基於裝飾器----》在視圖函數上再加裝飾器---》加多個裝飾器的執行順序---》登錄認證裝飾器---》加載router下,先做路由匹配,匹配成功執行被auth包裹的視圖函數

1 路由系統

# flask的路由是基於裝飾器的---》但是它的本質是app.add_url_rule方法--->類似於django 的paht(url)

1.1 轉換器

app.add_url_rule(rule='/index/<string:name>',endpoint='index',view_func=index,methods=['GET'])

# 跟django 2.x后的轉換器一樣
DEFAULT_CONVERTERS = {
    'default':          UnicodeConverter,
    'string':           UnicodeConverter,
    'any':              AnyConverter,
    'path':             PathConverter,
    'int':              IntegerConverter,
    'float':            FloatConverter,
    'uuid':             UUIDConverter,
}

1.2 路由系統的本質

"""
# route的源代碼
1. decorator = app.route('/',methods=['GET','POST'],endpoint='n1')
    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
2. @decorator
   decorator(index)----》本質---》self.add_url_rule(rule, endpoint, f, **options)
   
   
3 路由的本質可以寫成
app.add_url_rule(rule='/',endpoint='index',view_func=index,methods=['GET'])


4 (了解)endpoint如果不傳,是None,也有個默認值---》函數的名字
	-如果使用了裝飾器,一定要指定endpoint
"""

1.3 app.add_url_rule參數

@app.route和app.add_url_rule參數:
#1   rule, URL規則
#2  view_func, 視圖函數內存地址
#3  defaults = None, 默認值, 當URL中無參數,函數需要參數時,使用defaults = {'k': 'v'}   為函數提供參數
#4 endpoint = None, 名稱,用於反向生成URL,即: url_for('名稱')
#5 methods = None, 允許的請求方式,如:["GET", "POST"]
#6 對URL最后的 / 符號是否嚴格要求
	strict_slashes = None
    '''
        @app.route('/index', strict_slashes=False)
        #訪問http://www.xx.com/index/ 或http://www.xx.com/index均可
        @app.route('/index', strict_slashes=True)
        #僅訪問http://www.xx.com/index
    '''
#7 重定向到指定地址
redirect_to = None, 
    '''
        @app.route('/index/<int:nid>', redirect_to='/home/<nid>')
    '''

1.4 支持正則(不用)

#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匹配正則表達式
    """
    def __init__(self, map, regex):
        super(RegexConverter, self).__init__(map)
        self.regex = regex

    def to_python(self, value):
        """
        路由匹配時,匹配成功后傳遞給視圖函數中參數的值
        """
        return int(value)

    def to_url(self, value):
        """
        使用url_for反向生成URL時,傳遞的參數經過該方法處理,返回的值用於生成URL中的參數
        """
        val = super(RegexConverter, self).to_url(value)
        return val
# 添加到flask中
app.url_map.converters['regex'] = RegexConverter
@app.route('/index/<regex("\d+"):nid>')
def index(nid):
    print(url_for('index', nid='888'))
    return 'Index'

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

1.5 CBV源碼分析(跟djagno沒有區別)

from flask import Flask, url_for
from flask.views import View, MethodView

app = Flask(__name__)

app.debug = True  # 熱更新---->不停機更新


@app.route(rule='/', methods=['GET'], endpoint='index')
def index():
    return 'hello flask'


# 1 寫一個類,繼承MethodView
class LoginView(MethodView):
    def get(self):
        return 'get --login'

    def post(self):
        return 'post---login'


# 2 配置路由,LoginView.as_view()但是必須帶一個參數,參數是這個地址的別名

app.add_url_rule('/login', view_func=LoginView.as_view('login'))

# add_url_rule--->等同於django的pathpaht('/',index,name='index')
if __name__ == '__main__':
    app.run()

2 模板語法

# 完全兼容DTL,比DTL強大一些,可直接執行函數,還可以傳參數
#1.Markup等價django的mark_safe ,
# 2.extends,include一模一樣


3 請求與響應

# request對象屬性和方法
	# request.method  提交的方法
    # request.args  get請求提及的數據
    # request.form   post請求提交的數據
    # request.values  post和get提交的數據總和(一般不用)
    # request.cookies  客戶端所帶的cookie
    # request.headers  請求頭
    # request.path     不帶域名,請求路徑 http://127.0.0.1:5000/test_if?name=lqz&age=10----》/test_if
    # request.full_path  不帶域名,帶參數的請求路徑--->/test_if?name=lqz&age=10
    # 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']
# response對象
	######新手四件套
	# return "字符串"
    # return render_template('html模板路徑',**{})
    # return redirect('/index.html')
    # return jsonify({'k1':'v1'})
    
	# 向cookie中寫值
    # response = make_response(render_template('index.html'))
    # response是flask.wrappers.Response類型
    # response.delete_cookie('key')
    # response.set_cookie('key', 'value')
    # response.headers['X-Something'] = 'A value'
    # return response

4 session

# 通過全局的session
	-設置值:session['key']=value
    -取值:session['key']
    -刪除值:del session['key']
    -不用管不同的用戶,統一都用全局session取,不會亂
    
    
# 一部分源碼解讀(等同於django中的中間件django.contrib.sessions.middleware.SessionMiddleware)
	-這樣設置session['key']=value---》在請求走的時候把這個數據,加密,放到cookie中給了前端
    -前端帶着cookie再一次請求---》請求來的時候,反解出數據,再放到session中,這樣你才能使用session['key']
    
    
    -SecureCookieSessionInterface---》負責干上面那倆事
    	--save_session:走的時候
    	-open_session:帶着cookie來的時候
        

5 閃現

# flash 翻譯過來的,flash函數
# 作用: 把一些數據放在某個位置,下次要用的時候,直接取出來,取一次就沒了,下次請求再取就沒了---》可以跨請求

# 放
flash('一坨大便')
# 分類放
flash('一個包子',category='bz')
flash('一個大便',category='db')
flash('又一個大便',category='db')
# 取
res = get_flashed_messages()
#分類取
res = get_flashed_messages(category_filter=['db'])


# 應用場景:在上一次的請求中有些數據,需要帶到下次請求中看,就可以使用閃現

# 其實是放在session中等同於  session[ss]='sdsd'



# django也有類似的東西----》message這個app,就是它

6 請求擴展

# 類似於django的中間件,在請求來之前,和走之后執行一些方法

6.1 before_request

類比django中間件中的process_request,在請求收到之前綁定一個函數做一些事情

#基於它做用戶登錄認證
@app.before_request
def process_request(*args,**kwargs):
    if request.path == '/login':
        return None
    user = session.get('user_info')
    if user:
        return None
    return redirect('/login')

6.2 after_request

類比django中間件中的process_response,每一個請求之后綁定一個函數,如果請求沒有異常

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

6.3 before_first_request

第一次請求時,跟瀏覽器無關

@app.before_first_request
def first():
    pass

6.4 teardown_request

每一個請求之后綁定一個函數,即使遇到了異常

@app.teardown_request 
def ter(e):
    pass

6.5 errorhandler

路徑不存在時404,服務器內部錯誤500

@app.errorhandler(404)
def error_404(arg):
    return "404錯誤了"

6.6 template_global

標簽

@app.template_global()
def sb(a1, a2):
    return a1 + a2
#{{sb(1,2)}}

6.7 template_filter

過濾器

@app.template_filter()
def db(a1, a2, a3):
    return a1 + a2 + a3
#{{ 1|db(2,3)}}
總結:

1 重點掌握before_request和after_request,
2 注意有多個的情況,執行順序
3 before_request請求攔截后(也就是有return值),response所有都執行

7 藍圖

# blueprint翻譯過來的---》藍圖---》划分目錄
# 不使用藍圖划分目錄,也可以,但是會出現循環導入
# 使用藍圖可以避免

# 使用藍圖:
	1 創建藍圖對象blog = Blueprint('blog', __name__),可以使用自己的靜態文件和template
    2 以后路由都使用藍圖對象來注冊
    3 把所有藍圖在app中注冊一下,可以指定前綴
    app.register_blueprint(blog)

1 fLask-session

# 原生的session把數據加密后放到了cookie中
# 數據庫中,redis,文件中。。。。
# pip install flask-session
# 使用了第三方以后,用法跟之前一樣,只是在項目啟動是,執行一些代碼
# 使用的兩種方式
# 方式一:
conn = redis.Redis()
app.session_interface = RedisSessionInterface(conn, 'lqz')
# 我自己寫的類有save_session和open_session---》操作redis存儲和獲取

# 方式二:(通用方案:flask集成第三方的通用方案),本質一樣,看源碼
from flask_session import Session
# 配置文件
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_KEY_PREFIX'] = 'lqz'
app.config['SESSION_REDIS'] = redis.Redis()
Session(app)  # 源碼在這---》內部就是做了 app.session_interface=RedisSessionInterface(app.config['SESSION_REDIS'])



# RedisSessionInterface源碼分析
	
from flask import Flask, session
from flask_session import RedisSessionInterface
import redis

app = Flask(__name__)

# 方式一:
# conn = redis.Redis()
# app.session_interface = RedisSessionInterface(conn, 'lqz')
# 我自己寫的類有save_session和open_session---》操作redis存儲和獲取

# 方式二:(通用方案:flask集成第三方的通用方案),本質一樣,看源碼
from flask_session import Session
# 配置文件
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_KEY_PREFIX'] = 'lqz'
app.config['SESSION_REDIS'] = redis.Redis()
Session(app)  # 源碼在這---》內部就是做了 app.session_interface=RedisSessionInterface(app.config['SESSION_REDIS'])
@app.route('/')
def index():
    session['name'] = 'lqz'
    return 'hello'


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

2 數據庫連接池

# 為什么要有數據庫連接池
	-隨着並發量的越來越大,mysql的連接數也會增大---》我們創建出連接池后,每次從池中獲取連接使用,能夠避免並發量過大,導致的數據庫崩掉的危險
    
    
# pip install dbutils

# 創建池對象
from dbutils.pooled_db import PooledDB

# 獲得一個數據庫連接池對象()
POOL = PooledDB(
    creator=pymysql,  # 使用鏈接數據庫的模塊
    maxconnections=6,  # 連接池允許的最大連接數,0和None表示不限制連接數
    mincached=2,  # 初始化時,鏈接池中至少創建的空閑的鏈接,0表示不創建
    maxcached=5,  # 鏈接池中最多閑置的鏈接,0和None不限制
    maxshared=3,
    # 鏈接池中最多共享的鏈接數量,0和None表示全部共享。PS: 無用,因為pymysql和MySQLdb等模塊的 threadsafety都為1,所有值無論設置為多少,_maxcached永遠為0,所以永遠是所有鏈接都共享。
    blocking=True,  # 連接池中如果沒有可用連接后,是否阻塞等待。True,等待;False,不等待然后報錯
    maxusage=None,  # 一個鏈接最多被重復使用的次數,None表示無限制
    setsession=[],  # 開始會話前執行的命令列表。如:["set datestyle to ...", "set time zone ..."]
    ping=0,
    # ping MySQL服務端,檢查是否服務可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
    host='127.0.0.1',
    port=3306,
    user='root',
    password='111',
    database='cnblogs',
    charset='utf8'
)


# 從池中拿出連接
   conn = POOL.connection()
    cursor = conn.cursor()
    cursor.execute('select * from article')
    print(cursor.fetchall())

# 了解
	-django和flask都是同步框架
    	-來一個請求,開啟線程執行視圖函數
    -異步框架:sanic,fastapi,只要被async裝飾的函數,就是協程函數
    	@app.route("/")
        async def test(request):
            await cousor.excute()
            return json({"hello": "world"})
        
        
        
# 協程:單線程下實現並發   


# 一旦用了異步,所有框架都必須使用異步
	-pymysql,redis是同步框架
    -不能使用在異步web框架中
    -需要使用專門的異步模塊
    -aiomysql,aioredis,python到目前為止,沒有一個比較好的異步orm框架
    
    -django 3.0以后支持異步
    	-但是django的orm不支持異步
        
   -python的orm框架
	-django的orm:同步
    -sqlalchemy:同步
    -peewee:同步和異步
    
    
# aioredis和aiomyql的使用

# 為什么一旦用了異步框架,后面全要用異步

信號(重要)

# 多線程並發安全的問題:多個線程同時操作同一個數據,出現錯亂
# 使用同步鎖--》修改數據之前先拿到---》開始修改---》釋放鎖--》別人再拿

# 線程鎖----》分布式鎖
# 悲觀鎖和樂觀鎖
# 同步鎖(互斥鎖),遞歸鎖(可重入鎖),Event事件,Semaphore(信號量)多把鎖



############## 內置信號(flask有一些)
request_started = _signals.signal('request-started')                # 請求到來前執行
request_finished = _signals.signal('request-finished')              # 請求結束后執行
 
before_render_template = _signals.signal('before-render-template')  # 模板渲染前執行
template_rendered = _signals.signal('template-rendered')            # 模板渲染后執行
 
got_request_exception = _signals.signal('got-request-exception')    # 請求執行出現異常時執行
 
request_tearing_down = _signals.signal('request-tearing-down')      # 請求執行完畢后自動執行(無論成功與否)
appcontext_tearing_down = _signals.signal('appcontext-tearing-down')# 應用上下文執行完畢后自動執行(無論成功與否)
 
appcontext_pushed = _signals.signal('appcontext-pushed')            # 應用上下文push時執行
appcontext_popped = _signals.signal('appcontext-popped')            # 應用上下文pop時執行
message_flashed = _signals.signal('message-flashed')                # 調用flask在其中添加數據時,自動觸發



# 自定義信號(我們自定義的)
# 內置信號使用
# 內置信號執行使用步驟:
# 模板渲染后信號執行
# 功能:home頁面被渲染,記錄一條日志
# 第一步:寫一個函數
def template_test(*args,**kwargs):
    print(args)
    print(kwargs)
    print('模板渲染完了')
# 第二步:綁定上內置信號:template_rendered
template_rendered.connect(template_test)
# 第三不:觸發信號執行(不需要咱們操作)
### 自定義信號的使用###### 自定義信號步驟
# 第0步:自定義一個信號xxxxx = _signals.signal('xxxxx')
# 第一步:寫一個函數def xxx_test(*args, **kwargs):    print(args)    print(kwargs)    print('xxx信號觸發了')
# 第二步:綁定上自定義的信號信號:xxxxxxxxxx.connect(xxx_test)
# 第三步:觸發信號執行(手動觸發,在某個位置觸發,比如視圖函數中)@app.route('/')def index():    xxxxx.send()  # 觸發信號    return 'hello'

3.3 django 的信號

###### 自定義信號步驟
# 第0步:自定義一個信號
xxxxx = _signals.signal('xxxxx')
# 第一步:寫一個函數
def xxx_test(*args, **kwargs):
    print(args)
    print(kwargs)
    print('xxx信號觸發了')


# 第二步:綁定上自定義的信號信號:xxxxx
xxxxx.connect(xxx_test)


# 第三步:觸發信號執行(手動觸發,在某個位置觸發,比如視圖函數中)


@app.route('/')
def index():
    xxxxx.send()  # 觸發信號
    return 'hello'

5 flask-script

# 實現類似於這樣的命令:python manage.py runserver
# 自定義命令:


# django 自定義命令
	python manage.py initdb xx.xsl article  #只要執行這個命令,就向數據庫的article表中寫入xx.xsl的數據
    
    
    
 # pip3 install flask-script
####基本使用
from flask import Flask


app = Flask(__name__)
# 第一步導入
from flask_script import Manager
# 第二步包裝
manager = Manager(app)


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


if __name__ == '__main__':
    # app.run()
    # 第三步使用
    manager.run()

5.2 自定義命令

######### 自定義命令
@manager.command
def custom(arg):
    """
    自定義命令
    python manage.py custom 123
    :param arg:
    :return:
    """
    print(arg)


@manager.option('-n', '--name', dest='name')
@manager.option('-u', '--url', dest='url')
def cmd(name, url):
    """
    自定義命令(-n也可以寫成--name)
    執行: python manage.py  cmd -n lqz -u http://www.oldboyedu.com
    執行: python manage.py  cmd --name lqz --url http://www.oldboyedu.com
    :param name:
    :param url:
    :return:
    """
    print(name, url)

6 請求上下文源碼分析

請求上下文執行流程(ctx):
		-0 flask項目一啟動,有6個全局變量
			-_request_ctx_stack:LocalStack對象----》封裝了local
			-_app_ctx_stack :LocalStack對象
			-request : LocalProxy對象
			-session : LocalProxy對象
		-1 請求來了 app.__call__()---->內部執行:self.wsgi_app(environ, start_response)
		-2 wsgi_app()
			-2.1 執行:ctx = self.request_context(environ):返回一個RequestContext對象,並且封裝了request(當次請求的request對象),session
			-2.2 執行: ctx.push():RequestContext對象的push方法
				-2.2.1 push方法中中間位置有:_request_ctx_stack.push(self),self是ctx對象
				-2.2.2 去_request_ctx_stack對象的類中找push方法(LocalStack中找push方法)
				-2.2.3 push方法源碼:
				    def push(self, obj):
						#通過反射找self._local,在init實例化的時候生成的:self._local = Local()
						#Local()flask封裝的支持線程和協程的local對象
						# 一開始取不到stack,返回None
						rv = getattr(self._local, "stack", None)
						if rv is None:
							#走到這,self._local.stack=[],rv=self._local.stack
							self._local.stack = rv = []
						# 把ctx放到了列表中
						#self._local={'線程id1':{'stack':[ctx,]},'線程id2':{'stack':[ctx,]},'線程id3':{'stack':[ctx,]}}
						rv.append(obj)
						return rv
		-3 如果在視圖函數中使用request對象,比如:print(request)
			-3.1 會調用request對象的__str__方法,request類是:LocalProxy
			-3.2 LocalProxy中的__str__方法:lambda x: str(x._get_current_object())
				-3.2.1 內部執行self._get_current_object()
				-3.2.2 _get_current_object()方法的源碼如下:
				    def _get_current_object(self):
						if not hasattr(self.__local, "__release_local__"):
							#self.__local()  在init的時候,實例化的,在init中:object.__setattr__(self, "_LocalProxy__local", local)
							# 用了隱藏屬性
							#self.__local 實例化該類的時候傳入的local(偏函數的內存地址:partial(_lookup_req_object, "request"))
							#加括號返回,就會執行偏函數,也就是執行_lookup_req_object,不需要傳參數了
							#這個地方的返回值就是request對象(當此請求的request,沒有亂)
							return self.__local()
						try:
							return getattr(self.__local, self.__name__)
						except AttributeError:
							raise RuntimeError("no object bound to %s" % self.__name__)
				-3.2.3 _lookup_req_object函數源碼如下:
					def _lookup_req_object(name):
						#name是'request'字符串
						#top方法是把第二步中放入的ctx取出來,因為都在一個線程內,當前取到的就是當次請求的ctx對象
						top = _request_ctx_stack.top
						if top is None:
							raise RuntimeError(_request_ctx_err_msg)
						#通過反射,去ctx中把request對象返回
						return getattr(top, name)
				-3.2.4 所以:print(request) 實質上是在打印當此請求的request對象的__str__
		-4 如果在視圖函數中使用request對象,比如:print(request.method):實質上是取到當次請求的reuquest對象的method屬性
		
		-5 最終,請求結束執行: ctx.auto_pop(error),把ctx移除掉
 # 總結:ctx對象中放了:當次請求的request對象,app對象,flashes和session


# python的Local對象(java threadlocal),多個線程可以同時讀寫這個變量,並且不會有並發安全的問題---》不同線程操作的是自己的數據---》大字典--》key值為線程id號

l=local()
# 線程1 
l.name='lqz'

# 線程2
l.name='egon'


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM