为啥我的 Django REST Framework 视图集 URL 没有解析?
Posted
技术标签:
【中文标题】为啥我的 Django REST Framework 视图集 URL 没有解析?【英文标题】:Why isn't my Django REST Framework viewset URL resolving?为什么我的 Django REST Framework 视图集 URL 没有解析? 【发布时间】:2020-04-06 13:53:25 【问题描述】:我正在使用 Django REST 框架构建 API,使用:
Viewset
s
非标准命名空间
HyperlinkedModelSerializer
URLPathVersioning
我正在尝试删除许多我认为与我的应用程序无关的方面,因为它是大型、成熟和封闭源代码的。但是,如果您能想到我遗漏的任何内容,我会提供更多信息。
我已配置我的 HyperlinkedModelSerializer
以支持我的自定义命名空间视图集。
class ItemSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
fields = ["id", "url", "description", "source_id", "location", "test"]
model = models.Item
extra_kwargs =
"url":
"view_name": "api:v1:questions:item-detail",
"lookup_field": "pk",
我的Viewset
:
class ItemViewSet(APIMixin, viewsets.ReadOnlyModelViewSet):
queryset = models.Item.objects.all()
serializer_class = serializers.ItemSerializer
# This is largely inconsequential, except for maybe the versioning class.
class APIMixin(SerializerExtensionsAPIViewMixin):
authentication_classes = [authentication.SessionAuthentication]
filter_backends = [
django_filters.DjangoFilterBackend,
filters.OrderingFilter,
]
ordering_fields = []
pagination_class = pagination.DefaultPageNumberPagination
parser_classes = [
parsers.JSONParser,
parsers.FormParser,
parsers.MultiPartParser,
]
permission_classes = [
permissions.IsAuthenticated,
permissions.DjangoObjectPermissions,
]
versioning_class = versioning.URLPathVersioning
urls.py
文件:
from rest_framework.routers import DefaultRouter
from django.conf.urls import url, include
from . import viewsets
router = DefaultRouter()
router.register(r"^test", viewsets.TestViewSet)
urlpatterns = [url(r"^", include(router.urls, namespace="questions"))]
项目中一系列最小的其他 urls.py
文件包含这些文件,这些文件只是应用了命名空间。不包括在内,因为这似乎不是问题,因为我可以在下面证明命名空间按预期工作。
从视图集(列表或详细信息)加载视图会引发以下问题:
Traceback:
File "/usr/local/lib/python3.5/dist-packages/rest_framework/reverse.py" in reverse
41. url = scheme.reverse(viewname, args, kwargs, request, format, **extra)
File "/usr/local/lib/python3.5/dist-packages/rest_framework/versioning.py" in reverse
88. viewname, args, kwargs, request, format, **extra
File "/usr/local/lib/python3.5/dist-packages/rest_framework/versioning.py" in reverse
25. return _reverse(viewname, args, kwargs, request, format, **extra)
File "/usr/local/lib/python3.5/dist-packages/rest_framework/reverse.py" in _reverse
60. url = django_reverse(viewname, args=args, kwargs=kwargs, **extra)
File "/usr/local/lib/python3.5/dist-packages/django/urls/base.py" in reverse
91. return force_text(iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs)))
File "/usr/local/lib/python3.5/dist-packages/django/urls/resolvers.py" in _reverse_with_prefix
497. raise NoReverseMatch(msg)
During handling of the above exception (Reverse for 'item-detail' with keyword arguments ''pk': UUID('380fda25-196d-41ef-93c6-5216d54561a2'), 'version': 'v1'' not found. 2 pattern(s) tried: ['api/(?P<version>v1)/questions/^item/(?P<pk>[^/.]+)\\.(?P<format>[a-z0-9]+)/?$', 'api/(?P<version>v1)/questions/^item/(?P<pk>[^/.]+)/$']), another exception occurred:
File "/usr/local/lib/python3.5/dist-packages/rest_framework/relations.py" in to_representation
393. url = self.get_url(value, self.view_name, request, format)
File "/usr/local/lib/python3.5/dist-packages/rest_framework/relations.py" in get_url
331. return self.reverse(view_name, kwargs=kwargs, request=request, format=format)
File "/usr/local/lib/python3.5/dist-packages/rest_framework/reverse.py" in reverse
45. url = _reverse(viewname, args, kwargs, request, format, **extra)
File "/usr/local/lib/python3.5/dist-packages/rest_framework/reverse.py" in _reverse
60. url = django_reverse(viewname, args=args, kwargs=kwargs, **extra)
File "/usr/local/lib/python3.5/dist-packages/django/urls/base.py" in reverse
91. return force_text(iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs)))
File "/usr/local/lib/python3.5/dist-packages/django/urls/resolvers.py" in _reverse_with_prefix
497. raise NoReverseMatch(msg)
During handling of the above exception (Reverse for 'item-detail' with keyword arguments ''pk': UUID('380fda25-196d-41ef-93c6-5216d54561a2'), 'version': 'v1'' not found. 2 pattern(s) tried: ['api/(?P<version>v1)/questions/^item/(?P<pk>[^/.]+)\\.(?P<format>[a-z0-9]+)/?$', 'api/(?P<version>v1)/questions/^item/(?P<pk>[^/.]+)/$']), another exception occurred:
File "/usr/local/lib/python3.5/dist-packages/django/core/handlers/exception.py" in inner
41. response = get_response(request)
File "/usr/local/lib/python3.5/dist-packages/django/core/handlers/base.py" in _get_response
187. response = self.process_exception_by_middleware(e, request)
File "/usr/local/lib/python3.5/dist-packages/django/core/handlers/base.py" in _get_response
185. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/usr/local/lib/python3.5/dist-packages/django/views/decorators/csrf.py" in wrapped_view
58. return view_func(*args, **kwargs)
File "/usr/local/lib/python3.5/dist-packages/rest_framework/viewsets.py" in view
114. return self.dispatch(request, *args, **kwargs)
File "/usr/local/lib/python3.5/dist-packages/django/utils/decorators.py" in _wrapper
67. return bound_func(*args, **kwargs)
File "/usr/local/lib/python3.5/dist-packages/django/views/decorators/cache.py" in _wrapped_view_func
57. response = view_func(request, *args, **kwargs)
File "/usr/local/lib/python3.5/dist-packages/django/utils/decorators.py" in bound_func
63. return func.__get__(self, type(self))(*args2, **kwargs2)
File "/opt/project/project/api/v1/views.py" in dispatch
57. return super(APIMixin, self).dispatch(*args, **kwargs)
File "/usr/local/lib/python3.5/dist-packages/rest_framework/views.py" in dispatch
505. response = self.handle_exception(exc)
File "/usr/local/lib/python3.5/dist-packages/rest_framework/views.py" in handle_exception
465. self.raise_uncaught_exception(exc)
File "/usr/local/lib/python3.5/dist-packages/rest_framework/views.py" in raise_uncaught_exception
476. raise exc
File "/usr/local/lib/python3.5/dist-packages/rest_framework/views.py" in dispatch
502. response = handler(request, *args, **kwargs)
File "/usr/local/lib/python3.5/dist-packages/rest_framework/mixins.py" in list
43. return self.get_paginated_response(serializer.data)
File "/usr/local/lib/python3.5/dist-packages/rest_framework/serializers.py" in data
757. ret = super().data
File "/usr/local/lib/python3.5/dist-packages/rest_framework/serializers.py" in data
261. self._data = self.to_representation(self.instance)
File "/usr/local/lib/python3.5/dist-packages/rest_framework/serializers.py" in to_representation
675. self.child.to_representation(item) for item in iterable
File "/usr/local/lib/python3.5/dist-packages/rest_framework/serializers.py" in <listcomp>
675. self.child.to_representation(item) for item in iterable
File "/usr/local/lib/python3.5/dist-packages/rest_framework/serializers.py" in to_representation
526. ret[field.field_name] = field.to_representation(attribute)
File "/usr/local/lib/python3.5/dist-packages/rest_framework/relations.py" in to_representation
408. raise ImproperlyConfigured(msg % self.view_name)
Exception Type: ImproperlyConfigured at /api/v1/questions/item/
Exception Value: Could not resolve URL for hyperlinked relationship using view name "api:v1:questions:item-detail". You may have failed to include the related model in your API, or incorrectly configured the `lookup_field` attribute on this field.
虽然这就是问题本身的表现方式,但在故障排除过程中,我尝试移除所有中间人并将问题提炼成最纯粹的形式。
在 Django shell 中,向解析器提供(虚构的)URL 成功。我可以(重要地)看到:
url_name
namespaces
kwargs
In [26]: resolve('/api/v1/questions/item/25/')
Out[26]: ResolverMatch(func=project.questions.api.v1.viewsets.ItemViewSet, args=(), kwargs='version': 'v1', 'pk': '25', url_name=item-detail, app_names=[], namespaces=['api', 'v1', 'questions'])
现在,如果我使用从中收集到的信息来反转给定的 URL 名称:
In [24]: reverse('api:v1:questions:item-detail', kwargs='version': 'v1', 'pk': '25')
---------------------------------------------------------------------------
NoReverseMatch Traceback (most recent call last)
<ipython-input-24-922da70ed04c> in <module>()
----> 1 reverse('api:v1:questions:item-detail', kwargs='version': 'v1', 'pk': '25')
/usr/local/lib/python3.5/dist-packages/django/urls/base.py in reverse(viewname, urlconf, args, kwargs, current_app)
89 resolver = get_ns_resolver(ns_pattern, resolver)
90
---> 91 return force_text(iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs)))
92
93
/usr/local/lib/python3.5/dist-packages/django/urls/resolvers.py in _reverse_with_prefix(self, lookup_view, _prefix, *args, **kwargs)
495 "a valid view function or pattern name." % 'view': lookup_view_s
496 )
--> 497 raise NoReverseMatch(msg)
498
499
NoReverseMatch: Reverse for 'item-detail' with keyword arguments ''version': 'v1', 'pk': '25'' not found. 2 pattern(s) tried: ['api/(?P<version>v1)/questions/^item/(?P<pk>[^/.]+)\\.(?P<format>[a-z0-9]+)/?$', 'api/(?P<version>v1)/questions/^item/(?P<pk>[^/.]+)/$']
(我使用的是 Django 的标准 reverse
,而不是 DRF 的 reverse
,因此没有与版本控制相关的事情妨碍)。
我的理解是,此异常文本表明 URL 名称已成功匹配(因此显示的尝试匹配),但 kwargs 不匹配。但是,据我所知,我提供的 kwargs 完全符合 Django 的预期,正如上面的 resolve()
ing 所示。
【问题讨论】:
urls.py的路径是什么? 你使用的是哪个版本的 Django? 【参考方案1】:传递给reverse
的viewname
参数可以包含URL namespaces,而不是路径组件。
既然文件路径(大概)是api/questions/urls.py,那么"view_name"
应该是:
"view_name": "api:questions:item-detail",
【讨论】:
以上是关于为啥我的 Django REST Framework 视图集 URL 没有解析?的主要内容,如果未能解决你的问题,请参考以下文章
尽管有 AllowAny 权限,django-rest-framework 在 POST、PUT、DELETE 上返回 403 响应
为啥我的 Django REST Framework 视图集 URL 没有解析?
django.test.client 上的 Django rest 框架导入错误
无法使用 Django Rest 框架发送压缩的 gzip 数据