web-dev-qa-db-ja.com

Django RESTフレームワーク-複数のルックアップフィールド?

私は多かれ少なかれこのように見えるモデルを持っています:

class Starship(models.Model):
    id = models.UUIDField(default=uuid4, editable=False, primary_key=True)
    name = models.CharField(max_length=128)
    hull_no = models.CharField(max_length=12, unique=True)

目立たないStarshipDetailSerialiserStarshipListSerialiserがあり(最終的には異なるフィールドを表示したいのですが、今のところ同じです)、どちらもserializers.ModelSerializer。元のHyperlinkedIdentityFieldと非常によく似た自作クラスを使用して、(UU)IDを参照するHyperlinkedIdentityFieldがありますが、UUIDを正規化して処理する機能があります。

class StarshipListSerializer(HyperlinkedModelSerializer):
uri = UUIDHyperlinkedIdentityField(view_name='starships:starship-detail', format='html')

    class Meta:
         model = Starship
         fields = ('uri', 'name', 'hull_no')

最後に、リストビュー(ListAPIView)と次のような詳細ビューがあります。

class StarshipDetail(APIView):
    """
    Retrieves a single starship by UUID primary key.
    """

    def get_object(self, pk):
        try:
            return Starship.objects.get(pk=pk)
        except Starship.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        vessel = self.get_object(pk)
        serializer = StarshipDetailSerialiser(vessel, context={'request': request})
        return Response(serializer.data)

詳細ビューのURLスキーマは現在、UUIDに基づいてビューを呼び出しています。

...
url(r'vessels/id/(?P<pk>[0-9A-Fa-f\-]+)/$', StarshipDetail.as_view(), name='starship-detail'),
...

ユーザーがUUIDだけでなく、船体番号でも同じ船をナビゲートして見つけることができるようにしたいと思います。 vessels/id/abcde1345...and so on.../およびvessels/hull/H1025/は同じエンティティに解決できます。そして理想的には、IDまたは船体番号から詳細ビューに到達したかどうかに関係なく、リストをわずかに変更して使用されるシリアライザーは、IDをIDベースのリンクにハイパーリンクし、船体をハイパーリンクできる必要があります。船体番号ベースのリンク(vessels/hull/H1025/)。これは可能ですか?もしそうなら、私はそれについてどうしますか?

11
Chris vCB

1.新しいルートを追加します

# in urls.py

urlpatterns = [
    ...,
    url(r'vessels/id/(?P<pk>[0-9A-Fa-f\-]+)/$', StarshipDetail.as_view(), name='starship-detail-pk'),
    url(r'vessels/hull/(?P<hull_no>[0-9A-Za-z]+)/$', StarshipDetail.as_view(), name='starship-detail-hull'),
]

必要に応じて、hull_noの正規表現を微調整します。各ルートにstarship-detail-pkstarship-detail-hullという異なる名前を付けたことに注意してください。

2.シリアライザーに船体フィールドを追加します

# in serializers.py

class StarshipListSerialiser(HyperlinkedModelSerializer):
    uri = UUIDHyperlinkedIdentityField(view_name='starship-detail-pk', format='html')
    hull_no = UUIDHyperlinkedIdentityField(view_name='starship-detail-hull', format='html', lookup_field='hull_no')

    class Meta:
         model = Starship
         fields = ('uri', 'name', 'hull_no')

3.ビューを変更して、船体に基づいてオブジェクトも解決できるようにします

# in serializers.py

from Django.shortcuts import get_object_or_404

from rest_framework.views import APIView, Response

from starwars.serializers import StarshipDetailSerialiser
from starwars.models import Starship


class StarshipDetail(APIView):

    def get(self, request, pk=None, hull_no=None, format=None):
        lookup = {'hull_no': hull_no} if pk is None else {'pk': pk}
        vessel = get_object_or_404(Starship, **lookup)
        serializer = StarshipDetailSerialiser(vessel, context={'request': request})
        return Response(serializer.data)

詳細ビューを使用するには、これで十分です。

drf screencap

最後に、このような2つの異なるURLで同じリソースを利用できるのはRESTfulではないことに注意してください。おそらく、別の設計上の決定として、リソースの「1つの真のルート」を定義し、他のロケーターから正規URLへの「便利な」リダイレクトを追加することを検討することをお勧めします。

11
wim