Flaskについてまとめ7 アプリケーションコンテキスト リクエストコンテキスト


https://qiita.com/TaichiEndoh/items/d0a716f089387b656e58

前回記事では静的ファイルの利用をしました

### アプリケーションコンテキスト と リクエストコンテキスト

Flaskには

・アプリケーションコンテキスト

・リクエストコンテキスト

という二種類のコンテキストがあります


コンテキストとは、プログラムの実行に必要な各種情報のことですが

### アプリケーションコンテキスト

アプリケーションコンテキストとは
リクエストの間　アプリレベルのデータを利用できるようにするものです

アプリケーションレベルのデータには　current_app　と　g があります

current_app　=　アクティブアプリ（実行中のアプリ）のインスタンス
g　=  リスエストの間だけ利用できるグローバルなテンポラリ（一次）領域　
　　　　リクエストごとにリセットされる

https://msiz07-flask-docs-ja.readthedocs.io/ja/latest/appcontext.html

これまで、app = Flask(__name__)で取得したappにアクセスすれば

アプリのインスタンスにアクセスできました

pythonのインスタンスは
以下のソースコードなどがイメージしやすいかと思います
```
class Circle:
  def __init__(self, radius):
    # インスタンス変数に値を代入
    self.radius = radius
 
  # 関数を定義
  def area(self):
    return radius * radius * 3.14

radius = 10
# インスタンスの生成
circle = Circle(radius)

print("円の半径:", radius)
print("円の面積:", circle.area())
```
https://techacademy.jp/magazine/23289

Flask アプリがスケールすると
app = Flask(__name__)で取得したインスタンスが
お互いが参照しあい　

ループが生じる循環参照　が発生しやすくなり　
Flask側でエラーが発生します

Flaskはこの問題を　
Flaskアプリのインスタンスであるappを直接参照するのではなく
current_app にアクセスすることで解消します

#### current_app
current_appは、アプリケーションコンテキストからpushされると
スタックへ積まれて　どこからでもアクセス出来るようになります

https://runebook.dev/ja/docs/flask/appcontext/index

スタック　は　一時的にデータを貯めるデータ構造

スタックに　
データを積むことをpush　
データを取り出すことをpop
 
といいます


current_appは、アプリケーションコンテキストから
push（データを積む）と
スタック（一時的にデータを貯めるデータ構造）へ積まれて　
どこからでもアクセス出来るようになります

アプリケーションコンテキストの確認　app.pyを調整
（通常書かないコード　内部の仕組み理解のため使用）
```
# flask クラスをimport
from statistics import mean

from flask import Flask, current_app, g, render_template, url_for

# Flask クラスをインスタンス化する
app = Flask(__name__)

# URLと実行する関数をマッピングする
@app.route("/")
def index():
    return "Hello, Flask book like CEML"


# ルーティング
@app.route("/hello/<name>", methods=["GET"], endpoint="hello-endpoint")

# endpoint を指定しない場合は　関数名がendpoint 名となる
# 許可するHTTPメソッドをGETとPOSTにする場合
# methods=["GET","POST"]となる


def hello(name):
    # python3.6から導入されたf-stringで文字列を定義
    return f"Hello world,{name}!!!!!"


def hello_world():
    return "Hello world"


# show_name エンドポイントを作成する
@app.route("/name/<name>")
def show_name(name):
    # 変数をテンプレートエンジンに渡す
    return render_template("index.html", name=name)


with app.test_request_context():
    # /
    print(url_for("index"))
    # /hello/world
    print(url_for("hello-endpoint", name="world"))
    # /name/ichiro?page=ichiro
    print(url_for("show_name", name="ichiro", page="1"))


# ここで呼び出すとエラーになる
# print(current_app)

# アプリケーションコンテキストを取得してスタックへpushする
ctx = app.app_context()
ctx.push()

# current_appにアクセスが可能となる
print(current_app.name)
# >> app.minimalapp.app

# グローバルなテンポラリ領域に値を設定する
g.connetction = "connection"
print(g.connetction)

```

### g
gはリクエストの間だけ利用できる
グローバルなテンポラリ領域ですが
gも同様にアプリケーションコンテキストが

スタックへ積まれると利用可能になります

gの代表的な利用例はデータベースのコネクション（接続）
などで、サンプルではg.connetction　に　"connection"
という文字列を格納して出力しています
```
# グローバルなテンポラリ領域に値を設定する
g.connetction = "connection"
print(g.connetction)
```
このgに設定した値は同一リクエスト間であれば

どこからでもアクセスすることが可能です

### リクエストコンテキスト
リクエストコンテキストは　リクエストの間
リクエストレベルのデータを利用できるようにするものです

リクエストレベルのデータにはrequest と　sesson　があります

リスエストコンテキストを手動で取得してpushするには
Flaskについてまとめ5　で記載した
test_request_context関数を使います


https://qiita.com/TaichiEndoh/items/f2ddbd49cf0417ebc1e9


test_request_context関数
```
with app.test_request_context():
    # /
    print(url_for("index"))
    # /hello/world
    print(url_for("hello-endpoint", name="world"))
    # /name/ichiro?page=ichiro
    print(url_for("show_name", name="ichiro", page="1"))
```

app.pyファイルを調整
```
# flask クラスをimport
from statistics import mean

from flask import Flask, current_app, g, render_template, request, url_for

# Flask クラスをインスタンス化する
app = Flask(__name__)

# URLと実行する関数をマッピングする
@app.route("/")
def index():
    return "Hello, Flask book like CEML"


# ルーティング
@app.route("/hello/<name>", methods=["GET"], endpoint="hello-endpoint")

# endpoint を指定しない場合は　関数名がendpoint 名となる
# 許可するHTTPメソッドをGETとPOSTにする場合
# methods=["GET","POST"]となる


def hello(name):
    # python3.6から導入されたf-stringで文字列を定義
    return f"Hello world,{name}!!!!!"


def hello_world():
    return "Hello world"


# show_name エンドポイントを作成する
@app.route("/name/<name>")
def show_name(name):
    # 変数をテンプレートエンジンに渡す
    return render_template("index.html", name=name)


with app.test_request_context():
    # /
    print(url_for("index"))
    # /hello/world
    print(url_for("hello-endpoint", name="world"))
    # /name/ichiro?page=ichiro
    print(url_for("show_name", name="ichiro", page="1"))


# ここで呼び出すとエラーになる
# print(current_app)

# アプリケーションコンテキストを取得してスタックへpushする
ctx = app.app_context()
ctx.push()

# current_appにアクセスが可能となる
print(current_app.name)
# >> app.minimalapp.app

# グローバルなテンポラリ領域に値を設定する
g.connetction = "connection"
print(g.connetction)


with app.test_request_context("/users?updated=true"):
    # trueが出力される
    print(request.args.get("updated"))

```