routers.py

ルータ

リソースルーティングを使用すると、特定のリソースコントローラーのすべての共通ルートをすばやく宣言できます。 index ... のように個別のルートを宣言する代わりに、リソースルートは1行のコードでそれらを宣言します。

Ruby on Railsドキュメント

Railsなどの一部のWebフレームワークは、アプリケーションのURLを、受信リクエストを処理するロジックにどのようにマッピングするかを自動的に決定する機能を提供します。

REST frameworkは、Djangoに自動URLルーティングのサポートを追加し、ビューロジックを一連のURLに接続するためのシンプルで迅速かつ一貫性のある方法を提供します。

使用方法

SimpleRouterを使用する簡単なURLconfの例を次に示します。

from rest_framework import routers

router = routers.SimpleRouter()
router.register(r'users', UserViewSet)
router.register(r'accounts', AccountViewSet)
urlpatterns = router.urls

register()メソッドには2つの必須引数があります

  • prefix - このルートセットに使用するURLプレフィックス。
  • viewset - ビューセットクラス。

必要に応じて、追加の引数を指定することもできます

  • basename - 作成されるURL名に使用するベース。設定されていない場合、basenameは、viewsetのqueryset属性に基づいて自動的に生成されます(存在する場合)。 viewsetにqueryset属性が含まれていない場合は、viewsetを登録するときにbasenameを設定する必要があることに注意してください。

上記の例では、次のURLパターンが生成されます

  • URLパターン:^users/$ 名前:'user-list'
  • URLパターン:^users/{pk}/$ 名前:'user-detail'
  • URLパターン:^accounts/$ 名前:'account-list'
  • URLパターン:^accounts/{pk}/$ 名前:'account-detail'

basename引数は、ビュー名パターンの最初の部分を指定するために使用されます。上記の例では、それはuserまたはaccount部分です。

通常、basename引数を指定する必要はありませんが、カスタムget_querysetメソッドを定義したviewsetがある場合、viewsetに.queryset属性が設定されていない可能性があります。そのviewsetを登録しようとすると、次のようなエラーが表示されます

'basename' argument not specified, and could not automatically determine the name from the viewset, as it does not have a '.queryset' attribute.

これは、モデル名から自動的に決定できなかったため、viewsetを登録するときにbasename引数を明示的に設定する必要があることを意味します。


ルータでincludeを使用する

ルータインスタンスの.urls属性は、単なる標準のURLパターンのリストです。これらのURLを含める方法には、さまざまなスタイルがあります。

たとえば、既存のビューのリストにrouter.urlsを追加できます...

router = routers.SimpleRouter()
router.register(r'users', UserViewSet)
router.register(r'accounts', AccountViewSet)

urlpatterns = [
    path('forgot-password/', ForgotPasswordFormView.as_view()),
]

urlpatterns += router.urls

または、Djangoのinclude関数を使用することもできます...

urlpatterns = [
    path('forgot-password', ForgotPasswordFormView.as_view()),
    path('', include(router.urls)),
]

アプリケーション名前空間でincludeを使用できます

urlpatterns = [
    path('forgot-password/', ForgotPasswordFormView.as_view()),
    path('api/', include((router.urls, 'app_name'))),
]

または、アプリケーションとインスタンスの両方の名前空間

urlpatterns = [
    path('forgot-password/', ForgotPasswordFormView.as_view()),
    path('api/', include((router.urls, 'app_name'), namespace='instance_name')),
]

詳細については、DjangoのURL名前空間のドキュメントinclude APIリファレンスを参照してください。


:ハイパーリンクされたシリアライザで名前空間を使用する場合、シリアライザのview_nameパラメータが名前空間を正しく反映していることを確認する必要もあります。上記の例では、ユーザー詳細ビューにハイパーリンクされたシリアライザフィールドに、view_name='app_name:user-detail'などのパラメータを含める必要があります。

自動view_name生成では、%(model_name)-detailのようなパターンが使用されます。モデル名が実際に競合しない限り、ハイパーリンクされたシリアライザを使用する場合は、Django REST Frameworkビューに名前空間を付けない方がよいでしょう。


追加アクションのルーティング

ビューセットは、メソッドを@actionデコレータで装飾することにより、ルーティングのための追加アクションをマークできます。これらの追加アクションは、生成されたルートに含まれます。たとえば、UserViewSetクラスのset_passwordメソッドが与えられた場合

from myapp.permissions import IsAdminOrIsSelf
from rest_framework.decorators import action

class UserViewSet(ModelViewSet):
    ...

    @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf])
    def set_password(self, request, pk=None):
        ...

次のルートが生成されます

  • URLパターン:^users/{pk}/set_password/$
  • URL名:'user-set-password'

デフォルトでは、URLパターンはメソッド名に基づいており、URL名はViewSet.basenameとハイフンで区切られたメソッド名の組み合わせです。これらの値のいずれかにデフォルトを使用しない場合は、代わりに@actionデコレータにurl_pathおよびurl_name引数を指定できます。

たとえば、カスタムアクションのURLを^users/{pk}/change-password/$に変更する場合は、次のように記述できます

from myapp.permissions import IsAdminOrIsSelf
from rest_framework.decorators import action

class UserViewSet(ModelViewSet):
    ...

    @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf],
            url_path='change-password', url_name='change_password')
    def set_password(self, request, pk=None):
        ...

上記の例では、次のURLパターンが生成されるようになりました

  • URLパス:^users/{pk}/change-password/$
  • URL名:'user-change_password'

APIガイド

SimpleRouter

このルータには、listcreateretrieveupdatepartial_update、およびdestroyアクションの標準セットのルートが含まれています。ビューセットは、@actionデコレータを使用して、ルーティングされる追加のメソッドをマークすることもできます。

URLスタイルHTTPメソッドアクションURL名
{prefix}/GETlist{basename}-list
POSTcreate
{prefix}/{url_path}/GET、または `methods` 引数で指定されたメソッド`@action(detail=False)` デコレータ付きメソッド{basename}-{url_name}
{prefix}/{lookup}/GETGETretrieve
{basename}-detailPUT
updatePATCH
partial_updateDELETE
destroyGET、または `methods` 引数で指定されたメソッド{prefix}/{lookup}/{url_path}/{basename}-{url_name}

{basename}-{url_name}

router = SimpleRouter(trailing_slash=False)

SimpleRouterによって作成されたURLには、デフォルトで末尾にスラッシュが追加されます。この動作は、ルータをインスタンス化するときにtrailing_slash引数をFalseに設定することで変更できます。例えば

末尾のスラッシュはDjangoでは慣例ですが、Railsなどの一部の他のフレームワークではデフォルトでは使用されません。使用するスタイルの選択は、主に好みの問題ですが、一部のJavaScriptフレームワークは特定のルーティングスタイルを期待する場合があります。

router = SimpleRouter(use_regex_path=False)

デフォルトでは、SimpleRouter によって作成された URL は正規表現を使用します。この動作は、ルータをインスタンス化するときに `use_regex_path` 引数を `False` に設定することで変更できます。この場合、パスコンバータ が使用されます。例えば

use_regex_path=False は Django 2.x 以降でのみ機能します。この機能は 2.0.0 で導入されたためです。リリースノート を参照してください。

class MyModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    lookup_field = 'my_model_id'
    lookup_value_regex = '[0-9a-f]{32}'

class MyPathModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    lookup_field = 'my_model_uuid'
    lookup_value_converter = 'uuid'

DefaultRouter

ルータは、スラッシュとピリオド文字以外の文字を含むルックアップ値に一致します。より制限的な(または寛大な)ルックアップパターンにするには、ビューセットに `lookup_value_regex` 属性を設定するか、パスコンバータを使用する場合は `lookup_value_converter` を設定します。たとえば、ルックアップを有効な UUID に制限できます

URLスタイルHTTPメソッドアクションURL名
このルータは上記のSimpleRouterに似ていますが、さらに、すべてのリストビューへのハイパーリンクを含むレスポンスを返すデフォルトのAPIルートビューが含まれています。また、オプションの.jsonスタイルのフォーマットサフィックスのルートも生成します。GET[.format]自動生成されたルートビュー
api-rootGETlist{basename}-list
POSTcreate
上記と同じGET、または `methods` 引数で指定されたメソッド`@action(detail=False)` デコレータ付きメソッド{basename}-{url_name}
上記と同じGETGETretrieve
{basename}-detailPUT
updatePATCH
partial_updateDELETE
上記と同じGET、または `methods` 引数で指定されたメソッド{prefix}/{lookup}/{url_path}/{basename}-{url_name}

上記と同じ

router = DefaultRouter(trailing_slash=False)

カスタムルータ

SimpleRouterと同様に、URLルートの末尾のスラッシュは、ルータをインスタンス化するときにtrailing_slash引数をFalseに設定することで削除できます。

カスタムルータを実装することは、それほど頻繁に行う必要のあることではありませんが、APIのURLの構造方法に関する特定の要件がある場合に役立ちます。これを行うことで、URL構造を再利用可能な方法でカプセル化できるため、新しいビューごとにURLパターンを明示的に記述する必要がなくなります。

カスタムルータを実装する最も簡単な方法は、既存のルータクラスの1つをサブクラス化することです。 .routes属性は、各ビューセットにマッピングされるURLパターンのテンプレートに使用されます。 .routes属性は、Routeという名前のタプルのリストです。

Routeという名前のタプルへの引数は次のとおりです

  • url:ルーティングされるURLを表す文字列。次のフォーマット文字列を含めることができます
  • {prefix} - このルートセットに使用するURLプレフィックス。
  • {lookup} - 単一のインスタンスとの照合に使用されるルックアップフィールド。

{trailing_slash} - trailing_slash引数に応じて '/' または空の文字列。

mapping:HTTPメソッド名からビューメソッドへのマッピング

  • {basename} - 作成されるURL名に使用するベース。

initkwargs: ビューをインスタンス化する際に渡される追加の引数の辞書。detailbasenamesuffix 引数は、ビューセットのイントロスペクション用に予約されており、ブラウザブルAPIによってビュー名とパンくずリストのリンクを生成するためにも使用されることに注意してください。

動的ルートのカスタマイズ

@action デコレーターのルーティング方法をカスタマイズすることもできます。.routes リストに DynamicRoute 名前付きタプルを含め、リストベースおよび詳細ベースのルートに適した detail 引数を設定します。detail に加えて、DynamicRoute の引数は以下のとおりです。

url: ルーティングされるURLを表す文字列。Route と同じ形式の文字列を含めることができ、さらに {url_path} 形式の文字列も受け入れます。

name: reverse 呼び出しで使用されるURLの名前。次の形式の文字列を含めることができます。

  • {basename} - 作成されるURL名に使用するベース。
  • {url_name} - @action に提供された url_name

initkwargs: ビューをインスタンス化する際に渡される追加の引数の辞書。

次の例では、list および retrieve アクションへのみルーティングされ、末尾のスラッシュ規則は使用されません。

from rest_framework.routers import Route, DynamicRoute, SimpleRouter

class CustomReadOnlyRouter(SimpleRouter):
    """
    A router for read-only APIs, which doesn't use trailing slashes.
    """
    routes = [
        Route(
            url=r'^{prefix}$',
            mapping={'get': 'list'},
            name='{basename}-list',
            detail=False,
            initkwargs={'suffix': 'List'}
        ),
        Route(
            url=r'^{prefix}/{lookup}$',
            mapping={'get': 'retrieve'},
            name='{basename}-detail',
            detail=True,
            initkwargs={'suffix': 'Detail'}
        ),
        DynamicRoute(
            url=r'^{prefix}/{lookup}/{url_path}$',
            name='{basename}-{url_name}',
            detail=True,
            initkwargs={}
        )
    ]

簡単なビューセットに対して CustomReadOnlyRouter が生成するルートを見てみましょう。

views.py:

class UserViewSet(viewsets.ReadOnlyModelViewSet):
    """
    A viewset that provides the standard actions
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer
    lookup_field = 'username'

    @action(detail=True)
    def group_names(self, request, pk=None):
        """
        Returns a list of all the group names that the given
        user belongs to.
        """
        user = self.get_object()
        groups = user.groups.all()
        return Response([group.name for group in groups])

urls.py:

router = CustomReadOnlyRouter()
router.register('users', UserViewSet)
urlpatterns = router.urls

以下のマッピングが生成されます...

URLHTTPメソッドアクションURL名
/usersGETlistuser-list
/users/{username}GETGETuser-detail
/users/{username}/group_namesGETgroup_namesuser-group-names

.routes 属性の設定に関する別の例については、SimpleRouter クラスのソースコードを参照してください。

高度なカスタムルータ

完全にカスタムの動作を提供する場合は、BaseRouter をオーバーライドし、get_urls(self) メソッドをオーバーライドできます。このメソッドは、登録されたビューセットを検査し、URLパターンのリストを返す必要があります。登録されたプレフィックス、ビューセット、および basename のタプルは、self.registry 属性にアクセスすることで検査できます。

get_default_basename(self, viewset) メソッドをオーバーライドするか、ビューセットをルーターに登録する際に常に basename 引数を明示的に設定することもできます。

サードパーティパッケージ

以下のサードパーティパッケージも利用可能です。

DRFネストされたルータ

drf-nested-routers パッケージ は、ネストされたリソースを扱うためのルーターとリレーションシップフィールドを提供します。

ModelRouter (wq.db.rest)

wq.db パッケージ は、DefaultRouterregister_model() API で拡張した高度な ModelRouter クラス(およびシングルトンインスタンス)を提供します。 Django の admin.site.register と同様に、rest.router.register_model に必要な引数はモデルクラスだけです。URLプレフィックス、シリアライザー、およびビューセットの適切なデフォルトは、モデルとグローバル設定から推測されます。

from wq.db import rest
from myapp.models import MyModel

rest.router.register_model(MyModel)

DRF-extensions

DRF-extensions パッケージ は、ネストされたビューセットカスタマイズ可能なエンドポイント名 を持つ コレクションレベルのコントローラー を作成するための ルーター を提供します。