I am trying to add reverse()
to my automated test api calls instead of hard-coding the urls. I have a router in my urls.py
that is directed to a ModelViewSet
and ModelSerializer
.
Here is my urls.py
router = DefaultRouter()
router.register('votes', VoteViewSet, basename='votes')
app_name = 'api'
urlpatterns = [
path('', include(router.urls)),
]
Here is my views.py
class VoteViewSet(viewsets.ModelViewSet):
queryset = Vote.objects.all()
serializer_class = VoteSerializer
permission_classes = [IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]
Here is my 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']
The following test.py fails
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)
This returns the following error
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>[^/.]+)/$']
When I use the hard-coded url, the test passes
response = self.client.post('/api/votes/', entry)