django.urls.exceptions.NoReverseMatch:使用关键字参数反转 'votes-detail'
django.urls.exceptions.NoReverseMatch: Reverse for 'votes-detail' with keyword arguments
我正在尝试将 reverse()
添加到我的自动化测试 api 调用中,而不是对 url 进行硬编码。我的 urls.py
中有一个路由器指向 ModelViewSet
和 ModelSerializer
.
这是我的 urls.py
router = DefaultRouter()
router.register('votes', VoteViewSet, basename='votes')
app_name = 'api'
urlpatterns = [
path('', include(router.urls)),
]
这是我的views.py
class VoteViewSet(viewsets.ModelViewSet):
queryset = Vote.objects.all()
serializer_class = VoteSerializer
permission_classes = [IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]
这是我的 serializers.py
class VoteSerializer(serializers.ModelSerializer):
entry = serializers.SlugField()
def create(self, validated_data):
user = self.context['request'].user
entry_slug = validated_data['entry']
entry_queryset = Entry.objects.filter(slug=entry_slug)
entry = entry_queryset[0]
response = {
'user': user,
'entry': entry_queryset[0]
}
if entry_queryset.filter(author=user):
raise PermissionDenied("Cannot upvote your own entry")
elif Vote.objects.filter(entry=entry, user=user):
raise PermissionDenied("Already voted")
return super().create(response)
class Meta:
model = Vote
fields = '__all__'
read_only_fields = ['user']
以下test.py失败
class EntryViewSetTestCase(APITestCase):
@classmethod
def setUpTestData(cls):
user = get_user_model()
cls.user_data = {
'email': 'user@localhost.com',
'password': 'hgEYqf$nQ8x5Pms'
}
cls.user = user.objects.create_user(
username='user',
email='user@localhost.com',
password='hgEYqf$nQ8x5Pms'
)
cls.other_user_data = {
'email': 'otheruser@localhost.com',
'password': 'hgEYqf$nQ8x5Pms'
}
cls.other_user = user.objects.create_user(
username='otheruser',
email='otheruser@localhost.com',
password='hgEYqf$nQ8x5Pms'
)
cls.entry_data = {
'title': 'title',
'description': 'description',
}
cls.entry = Entry(
title='title',
description='description',
author=cls.user
)
cls.entry.save()
def test_vote_entry_success(self):
token = self.client.post(reverse('token_obtain_pair'), self.other_user_data)
self.client.credentials(
HTTP_AUTHORIZATION='Bearer ' + token.data['access'])
entry = {'entry': self.entry.slug}
response = self.client.post(reverse('api:votes-detail', kwargs=entry))
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
这个returns下面的错误
django.urls.exceptions.NoReverseMatch: Reverse for 'votes-detail' with keyword arguments '{'entry': 'd901e2-title'}' not found. 2 pattern(s) tried: ['api/votes/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$', 'api/votes/(?P<pk>[^/.]+)/$']
当我使用硬编码url时,测试通过
response = self.client.post('/api/votes/', entry)
你试过了吗reverse('api:votes-detail', kwargs=entry)
?只有在使用 namespace=
参数注册路由器时才需要 api:
。
无论如何,我强烈建议使用 django-extension 的 show_urls
。看看this SO thread.
至少对于自动化测试,看起来使用 *-list
而不是 *-detail
是解决方案。感谢 Tommy 推荐 django-extensions 来解决这个问题。
response = self.client.post(reverse('api:votes-list'), entry)
我正在尝试将 reverse()
添加到我的自动化测试 api 调用中,而不是对 url 进行硬编码。我的 urls.py
中有一个路由器指向 ModelViewSet
和 ModelSerializer
.
这是我的 urls.py
router = DefaultRouter()
router.register('votes', VoteViewSet, basename='votes')
app_name = 'api'
urlpatterns = [
path('', include(router.urls)),
]
这是我的views.py
class VoteViewSet(viewsets.ModelViewSet):
queryset = Vote.objects.all()
serializer_class = VoteSerializer
permission_classes = [IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]
这是我的 serializers.py
class VoteSerializer(serializers.ModelSerializer):
entry = serializers.SlugField()
def create(self, validated_data):
user = self.context['request'].user
entry_slug = validated_data['entry']
entry_queryset = Entry.objects.filter(slug=entry_slug)
entry = entry_queryset[0]
response = {
'user': user,
'entry': entry_queryset[0]
}
if entry_queryset.filter(author=user):
raise PermissionDenied("Cannot upvote your own entry")
elif Vote.objects.filter(entry=entry, user=user):
raise PermissionDenied("Already voted")
return super().create(response)
class Meta:
model = Vote
fields = '__all__'
read_only_fields = ['user']
以下test.py失败
class EntryViewSetTestCase(APITestCase):
@classmethod
def setUpTestData(cls):
user = get_user_model()
cls.user_data = {
'email': 'user@localhost.com',
'password': 'hgEYqf$nQ8x5Pms'
}
cls.user = user.objects.create_user(
username='user',
email='user@localhost.com',
password='hgEYqf$nQ8x5Pms'
)
cls.other_user_data = {
'email': 'otheruser@localhost.com',
'password': 'hgEYqf$nQ8x5Pms'
}
cls.other_user = user.objects.create_user(
username='otheruser',
email='otheruser@localhost.com',
password='hgEYqf$nQ8x5Pms'
)
cls.entry_data = {
'title': 'title',
'description': 'description',
}
cls.entry = Entry(
title='title',
description='description',
author=cls.user
)
cls.entry.save()
def test_vote_entry_success(self):
token = self.client.post(reverse('token_obtain_pair'), self.other_user_data)
self.client.credentials(
HTTP_AUTHORIZATION='Bearer ' + token.data['access'])
entry = {'entry': self.entry.slug}
response = self.client.post(reverse('api:votes-detail', kwargs=entry))
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
这个returns下面的错误
django.urls.exceptions.NoReverseMatch: Reverse for 'votes-detail' with keyword arguments '{'entry': 'd901e2-title'}' not found. 2 pattern(s) tried: ['api/votes/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$', 'api/votes/(?P<pk>[^/.]+)/$']
当我使用硬编码url时,测试通过
response = self.client.post('/api/votes/', entry)
你试过了吗reverse('api:votes-detail', kwargs=entry)
?只有在使用 namespace=
参数注册路由器时才需要 api:
。
无论如何,我强烈建议使用 django-extension 的 show_urls
。看看this SO thread.
至少对于自动化测试,看起来使用 *-list
而不是 *-detail
是解决方案。感谢 Tommy 推荐 django-extensions 来解决这个问题。
response = self.client.post(reverse('api:votes-list'), entry)