python 如何在django restframework中反转ViewSet的自定义操作的URL

tpxzln5u  于 2023-02-07  发布在  Python
关注(0)|答案(6)|浏览(139)

我已经为ViewSet定义了一个自定义操作

from rest_framework import viewsets

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

视图集以常规方式注册到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的函数。

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的正确方法是什么?

twh00eeo

twh00eeo1#

您可以使用reverse just add to viewset的basename操作:

reverse('myuser-gender')

参见文档的相关部分。

pgvzfuti

pgvzfuti2#

可以使用get_urls()方法在启动时打印给定router的所有可逆url名称
在www.example.com中urls.py将最后一项注册到router = DefaultRouter()变量后,可以添加:

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

下次初始化应用程序时,它将打印到stdout,如下所示

[<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(...)的名称

eeq64g8w

eeq64g8w3#

基于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=[])
von4xj4u

von4xj4u4#

如果要专门使用UserViewset().reverse_action(),可以通过将basenamerequest = None指定给ViewSet来实现:

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):
        ....

以及www.example.com网站上urls.py:

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

然后调用url = UserViewset().reverse_action('gender')url = UserViewset().reverse_action(UserViewSet().gender.url_name)将返回正确的url。

    • Edit:**上述方法只在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

并且为特定的视图集子类化这个类,根据需要设置basename。

zc0qhyus

zc0qhyus5#

答案是reverse('myuser-gender')

**注意!**但请记住DRF会将操作名中的_替换为-。这意味着如果操作名是my_pretty_action,则应反过来使用reverse(app-my-pretty-action)

anhgbhbe

anhgbhbe6#

根据您的代码,您希望获得您使用的用户列表

reverse("myuser-list")

细节

reverse("myuser-detail",kwargs={"id":1})

在这里,您可能会注意到,我使用list或detail时将其添加到了相反的位置,因此,如果您添加appname,您可以直接输入它,这是因为我们使用的是viewset

相关问题