簡體   English   中英

如何在 django restframework 中反轉 ViewSet 的自定義操作的 URL

[英]how to reverse the URL of a ViewSet's custom action in django restframework

我為 ViewSet 定義了一個自定義操作

from rest_framework import viewsets

class UserViewSet(viewsets.ModelViewSet):
    @action(methods=['get'], detail=False, permission_classes=[permissions.AllowAny]) 
    def gender(self, request):
        ....

並且viewset以常規方式注冊到url

from django.conf.urls import url, include                                          

from rest_framework import routers                                                 
from api import views                                                              


router = routers.DefaultRouter()                                                   
router.register(r'users', views.UserViewSet, base_name='myuser')                   

urlpatterns = [                                                                    
    url(r'^', include(router.urls)),                                               
]   

URL /api/users/gender/有效。 但我不知道如何在單元測試中使用reverse來獲得它。 (我當然可以對這個 URL 進行硬編碼,但是從代碼中得到它會很好)

根據django 文檔,以下代碼應該可以工作

reverse('admin:app_list', kwargs={'app_label': 'auth'})
# '/admin/auth/'

但是我嘗試了以下方法,但它們不起作用

reverse('myuser-list', kwargs={'app_label':'gender'})
# errors out
reverse('myuser-list', args=('gender',))
# '/api/users.gender'

django-restframework 文檔中,有一個名為reverse_action的 function 。 但是,我的嘗試沒有奏效

from api.views import UserViewSet
a = UserViewSet()
a.reverse_action('gender') # error out
from django.http import HttpRequest
req = HttpRequest()
req.method = 'GET'
a.reverse_action('gender', request=req)  # still error out

扭轉該動作的 URL 的正確方法是什么?

您可以使用reverse僅添加到視圖集的基本名稱操作:

reverse('myuser-gender') 

請參閱文檔的相關部分

您可以在啟動時使用get_urls()方法打印給定router所有可逆 url 名稱

在將最后一項注冊到router = DefaultRouter()變量后的 urls.py 中,您可以添加:

#router = routers.DefaultRouter()
#router.register(r'users', UserViewSet)
#...
import pprint
pprint.pprint(router.get_urls())

下次您的應用程序初始化時,它將打印到標准輸出,例如

[<RegexURLPattern user-list ^users/$>,
 <RegexURLPattern user-list ^users\.(?P<format>[a-z0-9]+)/?$>,
 <RegexURLPattern user-detail ^users/(?P<pk>[^/.]+)/$>,
 <RegexURLPattern user-detail ^users/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$>,         
#...
]

其中 'user-list' 和 'user-detail' 是可以給reverse(...)

基於DRF 文檔

from rest_framework import routers

router = routers.DefaultRouter()

view = UserViewSet()
view.basename = router.get_default_basename(UserViewSet)
view.request = None

或者您可以根據需要設置請求。

view.request = req

最后,您可以獲得一個反向操作 URL 並使用它。

url = view.reverse_action('gender', args=[])

如果你想特別使用UserViewset().reverse_action() ,你可以通過為你的 ViewSet 分配一個basenamerequest = None來做到這一點:

from rest_framework import viewsets

class UserViewSet(viewsets.ModelViewSet):
    basename = 'user'
    request = None

    @action(methods=['get'], detail=False, permission_classes=[permissions.AllowAny]) 
    def gender(self, request):
        ....

在 urls.py 中:

router.register('user', UserViewSet, basename=UserViewSet.basename)

然后調用url = UserViewset().reverse_action('gender')url = UserViewset().reverse_action(UserViewSet().gender.url_name)將返回正確的 url。

編輯:上述方法僅在僅調用reverse_action()一次時有效,因為ViewSetMixin.as_view()方法在實例化時覆蓋basename 這可以通過添加的定制子類來解決GenericViewSet (或ModelViewSet如果優選的),如下所示:

from django.utils.decorators import classonlymethod
from rest_framework.viewsets import GenericViewSet

class ReversibleViewSet(GenericViewSet):
    basename = None
    request = None

    @classonlymethod
    def as_view(cls, actions=None, **initkwargs):
        basename = cls.basename
        view = super().as_view(actions, **initkwargs)
        cls.basename = basename
        return view

並為特定的 ViewSet 子類化此類,根據需要設置 basename。

答案是reverse('myuser-gender')

筆記! 但請記住,DRF 會將動作名稱中的_替換為- 這意味着如果動作名稱是my_pretty_action反向你應該使用reverse(app-my-pretty-action)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM