如何在 django rest 框架中反转 ViewSet 自定义操作的 URL
Posted
技术标签:
【中文标题】如何在 django rest 框架中反转 ViewSet 自定义操作的 URL【英文标题】:how to reverse the URL of a ViewSet's custom action in django restframework 【发布时间】:2018-12-21 09:17:11 【问题描述】:我已经为 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)),
]
网址/api/users/gender/
有效。但我不知道如何在单元测试中使用reverse
来获取它。 (我当然可以对这个 URL 进行硬编码,但从代码中获取它会很好)
根据django documentation,下面的代码应该可以工作
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 documentation中,有一个函数叫做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 的正确方法是什么?
【问题讨论】:
【参考方案1】:您可以使用 reverse
只需添加到视图集的基本名称操作:
reverse('myuser-gender')
请参阅related part 的文档。
【讨论】:
您可能需要添加应用名称:reverse('api:myuser-gender')
@A.J. docs 中的示例:view.reverse_action('set-password', args=['1'])
我可以补充一下,如果函数名称是gender_something,您将需要使用'gender-something'
如果您的操作由两个用“_”分隔的单词组成,那么在reverse
中您需要放置一个“-”
就像@MatthewHegarty 指出的那样,您可能还需要添加应用程序命名空间。您可以通过先运行python manage.py show_urls
来节省时间,如果您正在调试,可以从那里找到 URL 名称。【参考方案2】:
基于DRF Doc。
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=[])
【讨论】:
【参考方案3】:您可以在启动时使用get_urls()
方法打印给定router
的所有可逆url 名称
在urls.py中最后一项注册到router = DefaultRouter()
变量后,可以添加:
#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(...)
的名称
【讨论】:
为什么users/pk后面有个点,不应该是'/'吗?如果您想专门使用UserViewset().reverse_action()
,您可以通过将basename
和request = 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):
....
在 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。
【讨论】:
以上是关于如何在 django rest 框架中反转 ViewSet 自定义操作的 URL的主要内容,如果未能解决你的问题,请参考以下文章