I'm writing tests for my Django Rest Framework API.
I'm stuck on testing 'delete'.
My test for 'create' works fine.
Here's my test code:
import json
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from users.models import CustomUser
from lists.models import List, Item
class ListAPITest(APITestCase):
@classmethod
def setUp(self):
self.data = {'name': 'Test list', 'description':'A description', 'item': [
{'name': 'Item 1 Name', 'description': 'Item 1 description', 'order': 1},
{'name': 'Item 2 Name', 'description': 'Item 2 description', 'order': 2},
{'name': 'Item 3 Name', 'description': 'Item 3 description', 'order': 3},
{'name': 'Item 4 Name', 'description': 'Item 4 description', 'order': 4},
{'name': 'Item 5 Name', 'description': 'Item 5 description', 'order': 5},
{'name': 'Item 6 Name', 'description': 'Item 6 description', 'order': 6},
{'name': 'Item 7 Name', 'description': 'Item 7 description', 'order': 7},
{'name': 'Item 8 Name', 'description': 'Item 8 description', 'order': 8},
{'name': 'Item 9 Name', 'description': 'Item 9 description', 'order': 9},
{'name': 'Item 10 Name', 'description': 'Item 10 description', 'order': 10}
]}
# 'lists' is the app_name set in endpoints.py
# 'Lists' is the base_name set for the list route in endpoints.py
# '-list' seems to be something baked into the api
self.url = reverse('lists:Lists-list')
def test_create_list_authenticated(self):
"""
Ensure we can create a new list object.
"""
user = CustomUser.objects.create(email='person@example.com', username='Test user', email_verified=True)
self.client.force_authenticate(user=user)
response = self.client.post(self.url, self.data, format='json')
list_id = json.loads(response.content)['id']
# the request should succeed
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
# there should now be 1 List in the database
self.assertEqual(List.objects.count(), 1)
def test_delete_list_by_owner(self):
"""
delete list should succeed if user created list
"""
user = CustomUser.objects.create(email='person@example.com', username='Test user', email_verified=True)
new_list = List.objects.create(name='Test list', description='A description', created_by=user, created_by_username=user.username)
self.client.force_authenticate(user=user)
response = self.client.delete(self.url + '/' + str(new_list.id))
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
Instead of the expected status 204, I'm seeing:
AssertionError: 405 != 204
405 is method not allowed.
Here's my model definition:
class List(models.Model):
"""Models for lists
"""
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created_by = models.ForeignKey(USER, on_delete=models.CASCADE, related_name='list_created_by_id')
created_by_username = models.CharField(max_length=255) # this shold be OK given that the list will be deleted if the created_by_id user is deleted
created_at = models.DateTimeField(auto_now_add=True)
parent_item = models.ForeignKey('Item', on_delete=models.SET_NULL, null=True, related_name='parent_item')
modified_by = models.ForeignKey(USER, on_delete=models.SET_NULL, null=True,
related_name='list_modified_by')
modified_at = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=255)
description = models.CharField(max_length=5000, blank=True, default='')
is_public = models.BooleanField(default=False)
def __str__(self):
return self.name
Here's my viewset:
class ListViewSet(FlexFieldsModelViewSet):
"""
ViewSet for lists.
"""
permission_classes = [IsOwnerOrReadOnly, HasVerifiedEmail]
model = List
serializer_class = ListSerializer
permit_list_expands = ['item']
pagination_class = LimitOffsetPagination
def get_queryset(self):
# unauthenticated user can only view public lists
queryset = List.objects.filter(is_public=True)
# authenticated user can view public lists and lists the user created
# listset in query parameters can be additional filter
if self.request.user.is_authenticated:
listset = self.request.query_params.get('listset', None)
if listset == 'my-lists':
queryset = List.objects.filter(created_by=self.request.user)
elif listset == 'public-lists':
queryset = List.objects.filter(is_public=True)
else:
queryset = List.objects.filter(
Q(created_by=self.request.user) |
Q(is_public=True)
)
# allow filter by URL parameter created_by
created_by = self.request.query_params.get('created_by', None)
if created_by is not None:
queryset = queryset.filter(created_by=created_by)
# return only lists that have no parent item
toplevel = self.request.query_params.get('toplevel')
if toplevel is not None:
queryset = queryset.filter(parent_item=None)
return queryset.order_by('name')
I have read the docs but I haven't been able to find how to set up the delete request.
I have also tried this:
kwargs = {'pk': new_list.id}
response = self.client.delete(self.url, **kwargs)
This gives me an error:
AssertionError: Expected view ListViewSet to be called with a URL keyword argument named "pk". Fix your URL conf, or set the `.lookup_field` attribute on the view correctly.
Delete in my app works fine via the API in my React front end.
I know it's confusing that my object is called List...but it's hard to think of another name because that's what it is!
Thank you for for any ideas what I'm missing here!