Django: URLパターンとビュー関数の関係を解き明かす!resolver_match属性の秘密


http.HttpRequest.resolver_match は、Django のリクエストオブジェクト HttpRequest に含まれる属性であり、現在のリクエストがどのURLパターンにマッチしたかを表す情報を提供します。これは、URL解決プロセスが完了した後にのみ利用可能になり、ビュー関数やミドルウェア関数内で使用できます。

属性

ResolverMatch オブジェクトには、以下の属性が含まれます。

  • resolver: URL解決器オブジェクト
  • url_name: マッチしたURLパターンの名前
  • app_name: マッチしたURLパターンのアプリケーション名
  • namespace: マッチしたURLパターンの名前空間
  • kwargs: ビュー関数に渡されるキーワード引数
  • args: ビュー関数に渡される引数
  • func: マッチしたビュー関数

以下の例は、http.HttpRequest.resolver_match 属性を使用して、現在のURLパターンとビュー関数に関する情報を取得する方法を示します。

from django.http import HttpRequest

def my_view(request, pk):
    # ...

request = HttpRequest()
request.path_info = '/articles/123/'

resolver_match = request.resolver_match

print(resolver_match.func)  # my_view
print(resolver_match.args)  # (123,)
print(resolver_match.kwargs)  # {}
print(resolver_match.namespace)  # 'articles'
print(resolver_match.app_name)  # 'blog'
print(resolver_match.url_name)  # 'article_detail'
print(resolver_match.resolver)  # <django.urls.resolvers.URLResolver object at 0x12345678>

用途

http.HttpRequest.resolver_match 属性は、以下の目的で使用できます。

  • 現在のURLパターンに基づいてアクセス制御を行う
  • 現在のURLパターンに関連する情報をテンプレートに渡す
  • 現在のURLパターンに基づいて動的なコンテンツを生成する
  • ミドルウェア関数内で http.HttpRequest.resolver_match 属性を使用する場合は、process_view() 関数を使用する必要があります。
  • http.HttpRequest.resolver_match 属性は、URL解決プロセスが完了した後にのみ利用可能になります。


from django.http import HttpRequest

def my_view(request, pk):
    # ...

request = HttpRequest()
request.path_info = '/articles/123/'

resolver_match = request.resolver_match

print(resolver_match.func)  # my_view
print(resolver_match.args)  # (123,)
print(resolver_match.kwargs)  # {}
print(resolver_match.namespace)  # 'articles'
print(resolver_match.app_name)  # 'blog'
print(resolver_match.url_name)  # 'article_detail'
print(resolver_match.resolver)  # <django.urls.resolvers.URLResolver object at 0x12345678>

例2:現在のURLパターンに基づいて動的なコンテンツを生成する

from django.http import HttpRequest

def my_view(request, pk):
    article = Article.objects.get(pk=pk)

    context = {
        'article': article,
    }

    return render(request, 'articles/detail.html', context)

request = HttpRequest()
request.path_info = '/articles/123/'

resolver_match = request.resolver_match

article_id = resolver_match.args[0]
article = Article.objects.get(pk=article_id)

context = {
    'article': article,
}

return render(request, 'articles/detail.html', context)

例3:現在のURLパターンに関連する情報をテンプレートに渡す

from django.http import HttpRequest
from django.shortcuts import render

def my_view(request, pk):
    article = Article.objects.get(pk=pk)

    context = {
        'article': article,
        'namespace': resolver_match.namespace,
        'app_name': resolver_match.app_name,
        'url_name': resolver_match.url_name,
    }

    return render(request, 'articles/detail.html', context)

request = HttpRequest()
request.path_info = '/articles/123/'

resolver_match = request.resolver_match

context = {
    'article': Article.objects.get(pk=resolver_match.args[0]),
    'namespace': resolver_match.namespace,
    'app_name': resolver_match.app_name,
    'url_name': resolver_match.url_name,
}

return render(request, 'articles/detail.html', context)

例4:現在のURLパターンに基づいてアクセス制御を行う

from django.http import HttpRequest
from django.shortcuts import redirect

def my_view(request, pk):
    if not request.user.is_authenticated:
        return redirect('/login/')

    article = Article.objects.get(pk=pk)

    context = {
        'article': article,
    }

    return render(request, 'articles/detail.html', context)

request = HttpRequest()
request.path_info = '/articles/123/'

resolver_match = request.resolver_match

if not request.user.is_authenticated:
    return redirect('/login/')

article = Article.objects.get(pk=resolver_match.args[0])

context = {
    'article': article,
}

return render(request, 'articles/detail.html', context)


代替方法

以下の方法で http.HttpRequest.resolver_match 属性の代替として使用できます。

  • request.path_inforeverse() 関数を使用する
from django.core.urlresolvers import reverse

def my_view(request, pk):
    # ...

url_name = reverse('articles:article_detail', args=[pk])
namespace = url_name.split(':')[0]
app_name = url_name.split(':')[1]

# ...
  • request.META ヘッダーを使用する
def my_view(request, pk):
    # ...

url_name = request.META['HTTP_X_URL_PATH_INFO']
namespace, app_name, view_name = url_name.split('/')

# ...
  • カスタムミドルウェアを作成する
from django.http import HttpRequest
from django.core.urlresolvers import resolve

class MyMiddleware:
    def process_request(self, request):
        resolved_url = resolve(request.path_info)
        request.resolver_match = ResolverMatch(
            func=resolved_url.func,
            args=resolved_url.args,
            kwargs=resolved_url.kwargs,
            namespace=resolved_url.namespace,
            app_name=resolved_url.app_name,
            url_name=resolved_url.url_name,
            resolver=resolved_url.resolver,
        )

        return None

注意事項

上記の方法にはそれぞれ、以下のような注意事項があります。

  • カスタムミドルウェアを作成する

    • この方法は、最も柔軟性がありますが、最も複雑でもあります。
    • ミドルウェアの書き方がわからない場合は、使用しない方がよいでしょう。
  • request.META ヘッダーを使用する

    • この方法は、すべてのフレームワークで動作するとは限りません。
    • セキュリティ上の懸念事項があります。
    • この方法は、URLパターンが単純な場合にのみ有効です。
    • リバースルックアップは、キャッシュされていない場合、パフォーマンスが低下する可能性があります。