我正在使用Django REST Framework构建API,
Viewset
s HyperlinkedModelSerializer
URLPathVersioning
由于它是大型的,已建立的且是封闭源代码的,因此我试图将我认为与我的应用程序无关的许多方面切掉。但是,如果您能想到我想念的任何东西,我会提供更多信息。
我已将HyperlinkedModelSerializer
配置为支持自定义命名的Viewset。
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名称已成功匹配(因此,显示的尝试匹配),但kwarg不匹配。但是,据我所知,按照上述resolve()
的要求,我完全按照Django的期望提供了kwarg。
答案 0 :(得分:0)
传递给reverse
的viewname
参数可以包含URL namespaces,而不是路径分量。
由于文件路径(可能是) api / questions / urls.py ,因此"view_name"
应该是:
"view_name": "api:questions:item-detail",