🐙

[DRF]エンドポイントに含まれる複合ユニークキーを利用して単一レコードを取得する

2023/02/11に公開

書くこと

  • DRFのViewに標準搭載されているlookup_fieldに似た機能として、lookup_fieldsを実現するmixinの実装
    • lookup_fieldは単一キーによるレコード取得(<--標準搭載)
    • lookup_fieldsでは複数キーによるレコード取得(<--実現する!)
  • mixinを利用したViewファイルの記述例

利用する技術

  • Django REST Framework
  • rest_framework.generics

想定シチュエーション

複合ユニークキーが含まれていて、この複合ユニークキーを使ったレコード取得をしたい。

urlpatterns = [
    # user_idとpkによる複合ユニークキー
    path('user/<user_id>/post/<pk>/', PostDetailView.as_view())

    # example:
    # 'user/1/post/1/', PostDetailView.as_view())
    # => User<id:1>の最初の投稿を取得する
]

方法論

MultipleFieldLookupMixinを定義する

公式から例として提供されている

common/multiple_field_lookup_mixin.py/

class MultipleFieldLookupMixin:
    def get_object(self):
        queryset = self.get_queryset()
        queryset = self.filter_queryset(queryset)
        filter = {}
        for field in self.lookup_fields:
            if self.kwargs.get(field):
                filter[field] = self.kwargs[field]
        obj = get_object_or_404(queryset, **filter)
        self.check_object_permissions(self.request, obj)
        return obj

Viewを定義する

view/post_details.py/

from ..common.multiple_field_lookup_mixin import MultipleFieldLookupMixin

# 単一レコードへの操作系Viewで利用する
# Retrieve, Update, Destroy
from rest_framework.generics import RetrieveUpdateDestroyAPIView


class PostDetailView(MultipleFieldLookupMixin, RetrieveUpdateDestroyAPIView):
    queryset = Post.objects.all()
    # (省略)serializer_class =  SomethingSerializer
    lookup_fields = ('user_id', 'pk')

    # get_objectで書く手間を省ける
    # def get_object(self):
    #     user_id = self.kwargs.get("user_id")
    #     pk = self.kwargs.get("pk")
    #     etc....

付録: エンドポイント含まれるユニークキーで単一レコードを取得する

想定シチュエーション

ユニークキーが含まれていて、このユニークキーを使ったレコード取得をしたい。

urlpatterns = [
    # user_idとpkによる複合ユニークキー
    path('post/<post_id>/', PostDetailView.as_view())

Viewを定義する

view / post_details.py /

# 単一レコードへの操作系Viewで利用する
# Retrieve, Update, Destroy
from rest_framework.generics import RetrieveUpdateDestroyAPIView


class PostDetailView(RetrieveUpdateDestroyAPIView):
    queryset = Post.objects.all()
    # (省略)serializer_class =  SomethingSerializer

    # lookup_fieldはDRFが標準提供している
    lookup_field = 'post_id'

    # lookup_field値は'pk'なので今回の例では、以下の`url_patterns`にすることで省略可
    # path('post/<pk>/', PostDetailView.as_view())

参考

Discussion