naritoブログ

このブログはDjangoとBootstrap4で作成されました
ソースコード

Djangoで、requestをグローバル変数として扱う

プログラミング関連 Django Python 約23日前
2017年12月30日0:02
Djangoのビューにはリクエストオブジェクトが渡されます。しかし、ビューにしか渡されないので、受け渡しはビューを介して行う必要があります。(一応、テンプレートにも暗黙のうちに渡されます)
ビューから渡せるのであればまだいいのですが、それだと厳しいケースがあります。
例えば、テンプレートフィルタ内でリクエストオブジェクトを触りたい場合はどうでしょうか。テンプレートタグと違い、引数を渡すことはできません...

そこで、そもそもrequestをグローバルな変数として利用できるようにするのが今回の目的です。
このような方法はあまり推奨されませんが、念の為覚えておくとちょっとお得です。

プロジェクト名を「project」
アプリケーション名を「app」
とします。

まず、ミドルウェアを作成します。ミドルウェアの段階で、requestオブジェクトをどこかに保存するのです。
app内に、middleware.pyを作成し、以下のような中身にします。
from threading import local

_global = local()


def global_request(get_response):
    def middleware(request):
        _global.request = request
        response = get_response(request)
        return response
    return middleware




settings.pyを開き、ミドルウェアに作った↑のを読み込ませます。
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'app.middleware.global_request',  # 追加!
]



後は、使いたい場所からアクセスするだけです。
試しに、テンプレートフィルタからrequestにアクセスするならば以下のようになります。
from django import template
from app.middleware import _global  # このimportが必要

register = template.Library()


@register.filter
def test(value):
    request = _global.request  # ここでrequestを取得
    # requestを使った処理...
    # ...

    return value




middlewareモジュールの_global属性のimportをし、request属性を使うだけです。
from app.middleware import _global
...
...
request = _global.request



middleware.pyの解説をしましょう。
threading.local()ですが、これは各スレッド毎に固有の値を管理することができます。この各スレッドは各ユーザーに対応しており、他のユーザーのrequestを誤って使うことはまずありません。(Webサーバー側の実装にもよるかも)
Djangoにはタイムゾーンがありますが、各ユーザーのタイムゾーン情報もthreading.local()を使って管理しています。(django.utils.timezone.py)
from threading import local

_global = local()



ミドルウェアを作成するには、クラスを定義するか関数を定義するかの2つがあります。今回はシンプルな関数を使い、_globalのrequest属性にrequestを保存しておきました。
def global_request(get_response):
    def middleware(request):
        _global.request = request
        response = get_response(request)
        return response
    return middleware