Eerik Sven Puudist
Eerik Sven Puudist

Reputation: 2348

Django REST Framework: error when testing POST request with list

I have a test view:

@api_view(['POST'])
@permission_classes([AllowAny])
@authentication_classes([])
def test_view(request):
    return Response(request.data)

It is registered in urls.py:

urlpatterns = [
    path('api/test/', test_view)
]

When I try to POST [{"a": 1}, {"a": 2}] manually through the REST Frameworks UI, everything works fine.

However, if I write a test for it, an unexpected error occurs. Here is the test

from rest_framework.test import APITestCase

class ViewTest(APITestCase):
    def test_it(self):
        response = self.client.post('/api/test/', [{"a": 1}, {"a": 2}])
        print(response)

which produces the following error:

Traceback (most recent call last):
  File "C:\development\HeedView\backend\clients\api\tests.py", line 13, in test_it
    response = self.client.post('/api/test/', [{"a":1}, {"a": 2}])
  File "C:\development\HeedView\venv\lib\site-packages\rest_framework\test.py", line 300, in post
    path, data=data, format=format, content_type=content_type, **extra)
  File "C:\development\HeedView\venv\lib\site-packages\rest_framework\test.py", line 212, in post
    data, content_type = self._encode_data(data, format, content_type)
  File "C:\development\HeedView\venv\lib\site-packages\rest_framework\test.py", line 184, in _encode_data
    ret = renderer.render(data)
  File "C:\development\HeedView\venv\lib\site-packages\rest_framework\renderers.py", line 921, in render
    return encode_multipart(self.BOUNDARY, data)
  File "C:\development\HeedView\venv\lib\site-packages\django\test\client.py", line 194, in encode_multipart
    for (key, value) in data.items():
AttributeError: 'list' object has no attribute 'items'

How to explain this behavior?

Upvotes: 2

Views: 792

Answers (2)

krubo
krubo

Reputation: 6406

To pass json data, you need to add format='json'. This test works:

from rest_framework.test import APITestCase

class ViewTest(APITestCase):
    def test_it(self):
        response = self.client.post('/api/test/', [{"a": "1"}, {"a": 2}], format='json')
        print(response.json())

Alternatively, your original test works if you set the default format in settings.py:

REST_FRAMEWORK = {
    ...
    'TEST_REQUEST_DEFAULT_FORMAT': 'json'
}

If the format isn't specified:

The Rest Framework APIClient extends Django's existing Client class. The documentation for Client.post explains that:

The key-value pairs in the data dictionary are used to submit POST data. For example:

... client.post('/login/', {'name': 'fred', 'passwd': 'secret'})

... [tests] this POST data:

name=fred&passwd=secret

Your original test returns an error because if the format isn't specified, then Django expects a dictionary, not a list.

Upvotes: 4

Eerik Sven Puudist
Eerik Sven Puudist

Reputation: 2348

Here is a test that will work:

from rest_framework.test import APITestCase

class ViewTest(APITestCase):
    def test_it(self):
        response = self.client.post('/api/test/', json.dumps([{"a": "1"}, {"a": 2}]),
                                    content_type='application/json')
        print(response.json())

However, I still do not understand, why I have to use json.dumps (i.e. give data as a string) and explicitly specify the content type. This has never happened with request data of dict type.

Upvotes: 1

Related Questions