0

I'm trying to send a couple of files to my backend:

class AccountsImporterTestCase(APITestCase):

    def test(self):
        data = [open('accounts/importer/accounts.csv'), open('accounts/importer/apartments.csv')]
        response = self.client.post('/api/v1/accounts/import/', data, format='multipart')
        self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED)

But I'm getting an error:

Error
Traceback (most recent call last):
  File "/vagrant/conjuntos/accounts/tests/cases.py", line 128, in test
    response = self.client.post('/api/v1/accounts/import/', data, format='multipart')
  File "/vagrant/venv/lib/python3.4/site-packages/rest_framework/test.py", line 168, in post
    path, data=data, format=format, content_type=content_type, **extra)
  File "/vagrant/venv/lib/python3.4/site-packages/rest_framework/test.py", line 89, in post
    data, content_type = self._encode_data(data, format, content_type)
  File "/vagrant/venv/lib/python3.4/site-packages/rest_framework/test.py", line 64, in _encode_data
    ret = renderer.render(data)
  File "/vagrant/venv/lib/python3.4/site-packages/rest_framework/renderers.py", line 757, in render
    return encode_multipart(self.BOUNDARY, data)
  File "/vagrant/venv/lib/python3.4/site-packages/django/test/client.py", line 156, in encode_multipart
    for (key, value) in data.items():
AttributeError: 'list' object has no attribute 'items'

I know I'm not preparing the data correctly but is it possible to do it?, how?. Thanks!

Update: Trying @Kevin Brown solution

def test(self):
    data = QueryDict('', mutable=True)
    data.setlist('files', [open('accounts/importer/accounts.csv'), open('accounts/importer/apartments.csv')])
    response = self.client.post('/api/v1/accounts/import/', data, format='multipart')
    self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED)

Got the following:

Error
Traceback (most recent call last):
  File "/vagrant/conjuntos/accounts/tests/cases.py", line 130, in test
    response = self.client.post('/api/v1/accounts/import/', data, format='multipart')
  File "/vagrant/venv/lib/python3.4/site-packages/rest_framework/test.py", line 168, in post
    path, data=data, format=format, content_type=content_type, **extra)
  File "/vagrant/venv/lib/python3.4/site-packages/rest_framework/test.py", line 89, in post
    data, content_type = self._encode_data(data, format, content_type)
  File "/vagrant/venv/lib/python3.4/site-packages/rest_framework/test.py", line 64, in _encode_data
    ret = renderer.render(data)
  File "/vagrant/venv/lib/python3.4/site-packages/rest_framework/renderers.py", line 757, in render
    return encode_multipart(self.BOUNDARY, data)
  File "/vagrant/venv/lib/python3.4/site-packages/django/test/client.py", line 182, in encode_multipart
    return b'\r\n'.join(lines)
TypeError: sequence item 4: expected bytes, bytearray, or an object with the buffer interface, str found
chachan
  • 2,382
  • 1
  • 26
  • 41

2 Answers2

2

You are sending a list of files to the view, but you aren't sending them correctly. When you send data to a view, whether it is a Django view or DRF view, you are supposed to send it as a list of key-value pairs.

{
  "key": "value",
  "file": open("/path/to/file", "rb"),
}

To answer your question...

is it possible to do it?

It does not appear to be possible to upload multiple files using the same key (in tests), but it is possible to spread them out across multiple keys to achieve the same goal. Alternatively, you could set up your views to only handle a single file, and have multiple tests covering different test cases (apartments.csv, accounts.csv, etc.).

Your exception is being triggered because you are passing a single list instead of a dictionary, and Django cannot parse them correctly.

You might have some luck by directly forming the request dictionary using a QueryDict which is the internal representation of form data used by Django.

data = QueryDict(mutable=True)
data.setlist("files", [
  open('accounts/importer/accounts.csv', 'rb'),
  open('accounts/importer/apartments.csv', 'rb')
])

As this would more closely represent the data sent in through the browser. This has not been tested, but it's the way to send multiple non-file-values in one key.

Jieter
  • 4,101
  • 1
  • 19
  • 31
Kevin Brown-Silva
  • 40,873
  • 40
  • 203
  • 237
0

Check if this solves your problem as from the errors its clearly says that the data should not be a list

data = {"account_csv": open('accounts/importer/accounts.csv'), "apartments_csv": open('accounts/importer/apartments.csv')}

This link you might find useful Uploading multiple files in a single request using python requests module

Community
  • 1
  • 1
MaNKuR
  • 2,578
  • 1
  • 19
  • 31
  • Great guess, but your answer is skirting around the actual issue: Any data sent using form data must be a set of key-value pairs. – Kevin Brown-Silva May 16 '15 at 19:20
  • Agree @Kevin. just trying to give OP some sort of hints to get out of this issue. – MaNKuR May 16 '15 at 19:26
  • Sorry @MaNKuR, tried that too but I got something like `TypeError: sequence item 4: expected bytes, bytearray, or an object with the buffer interface, str found` – chachan May 16 '15 at 21:08