Django クラスベースビューのメソッド内でURLのパラメータを使う

Djangoのクラスベースビューのメソッド内でURLのパラメータを取る。

listviewのメソッド、get_queryset(self)内でURLのパラメータを取得したい。

結論

結論から述べると、メソッド内で、self.kwargsを使えばいい。

# 結論
def get_queryset(self):
    # self.kwargs.get()を使う。第二引数はデフォルト値。
    year = self.kwargs.get('year', 2000)
    month = self.kwargs.get('month', 10)
    day = self.kwargs.get('day', 10)

    return self.create_result(year, month, day)

以下、経緯。

経緯

urls.pyは次のようになっている。

# urls.py
urlpatterns = [
    path(
        'path/to/<int:year>/<int:month>/<int:day>/',
        views.SomeListView.as_view(),
        name='list',
    ),
    ...
]

views.pyは次のようになっている。

class SomeListView(ListView):
    model = models.SomeModel
    context_object_name = 'some_models'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        return context

    def get_queryset(self):
        return self.model.objects.all()

SomeListView内のメソッドで、URLのyear,month,dayを使いたい。 get_queryset内でフィルタに使いたい。

公式docsを見ても、情報は見つからない。探し辛くもある。 なので、ソースを眺めたところ、全てのビューの基底クラスのdjango.views.generic.base.Viewで 参考になりそうなものがあった。

as_viewクラスメソッドにある。このメソッドは、urls.pyで登録するやつ。view関数を生成してくれる。

その生成する関数が以下のようになっていた。

def as_view(...):
    ...
    def view(request, *args, **kwargs):
        self = cls(**initkwargs)
        if hasattr(self, 'get') and not hasattr(self, 'head'):
            self.head = self.get
        self.request = request
        self.args = args
        self.kwargs = kwargs
        return self.dispatch(request, *args, **kwargs)
    ...
    return view

このview関数インスタンスが関数ベースビューで定義してた関数と同等のものになる。 関数ベースビューの時は、パラメータはrequest引数の後に書いていた。 なので、*args**kwargsが怪しい。 そして、それらはselfのフィールドに格納されてる。 そのフィールドにアクセスすれば良さそう。試してみる。

# 実験用コード1
def get_queryset(self):
    print(self.kwargs)
    print(self.args)

    # stub
    return self.model.objects.all()

これでページにアクセスすれば、出力されるはず。 localhost:8000/path/to/2019/1/1/にアクセスすると、次のように出力された。

# 実験用コード1の出力
{'year': 2019, 'month': 1, 'day': 1}
()

self.kwargsを見れば良いことがわかる。取り出しを見てみる。

# 実験用コード2
def get_queryset(self):
    # urls.pyの内容からして、デフォルトは使われないはず。
    default_year = 2000
    print(self.kwargs.get('year', default_year))

    # stub
    return self.model.objects.all()

localhost:8000/path/to/2019/1/1/にアクセスした時、出力は次のようになる。

# 実験用コード2の出力
2019

OK。デフォルトは指定しなくても問題ない。 また、get_context_dataなど、フローチャート(公式docs用語)内のどのメソッドからも参照できるはず。

あとがき

もう少し公式docs見やすくなってほしい。せめて検索が検索になってほしい。

タイトルとURLをコピーしました