LoginSignup
7
6

More than 1 year has passed since last update.

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

Posted at

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

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

Flaskには

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

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

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

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

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

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

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

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

これまで、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())

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

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

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

current_app

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

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

スタックに 
データを積むことを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関数を使います

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"))

7
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6