59👍
When testing file uploads, you should pass the stream object into the request, not the data.
This was pointed out in the comments by @arocks
Pass { ‘image’: file} instead
But that didn’t full explain why it was needed (and also didn’t match the question). For this specific question, you should be doing
from PIL import Image
class TestFileUpload(APITestCase):
def test_file_is_accepted(self):
self.client.force_authenticate(self.user)
image = Image.new('RGB', (100, 100))
tmp_file = tempfile.NamedTemporaryFile(suffix='.jpg')
image.save(tmp_file)
tmp_file.seek(0)
response = self.client.post('my_url', {'image': tmp_file}, format='multipart')
self.assertEqual(status.HTTP_201_CREATED, response.status_code)
This will match a standard Django request, where the file is passed in as a stream object, and Django REST Framework handles it. When you just pass in the file data, Django and Django REST Framework interpret it as a string, which causes issues because it is expecting a stream.
And for those coming here looking to another common error, why file uploads just won’t work but normal form data will: make sure to set format="multipart"
when creating the request.
This also gives a similar issue, and was pointed out by @RobinElvin in the comments
It was because I was missing format=’multipart’
22👍
Python 3 users: make sure you open
the file in mode='rb'
(read,binary). Otherwise, when Django calls read
on the file the utf-8
codec will immediately start choking. The file should be decoded as binary not utf-8, ascii or any other encoding.
# This won't work in Python 3
with open(tmp_file.name) as fp:
response = self.client.post('my_url',
{'image': fp},
format='multipart')
# Set the mode to binary and read so it can be decoded as binary
with open(tmp_file.name, 'rb') as fp:
response = self.client.post('my_url',
{'image': fp},
format='multipart')
- [Django]-How to write a unit test for a django view?
- [Django]-How can I call a custom Django manage.py command directly from a test driver?
- [Django]-Django: Reference to an outer query may only be used in a subquery
14👍
You can use Django built-in SimpleUploadedFile
:
from django.core.files.uploadedfile import SimpleUploadedFile
class TestFileUpload(APITestCase):
...
def test_file_is_accepted(self):
...
tmp_file = SimpleUploadedFile(
"file.jpg", "file_content", content_type="image/jpg")
response = self.client.post(
'my_url', {'image': tmp_file}, format='multipart')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
- [Django]-How do I add a placeholder on a CharField in Django?
- [Django]-How to implement followers/following in Django
- [Django]-Django SUM Query?
6👍
It’s not so simple to understand how to do it if you want to use the PATCH method, but I found the solution in this question.
from django.test.client import BOUNDARY, MULTIPART_CONTENT, encode_multipart
with open(tmp_file.name, 'rb') as fp:
response = self.client.patch(
'my_url',
encode_multipart(BOUNDARY, {'image': fp}),
content_type=MULTIPART_CONTENT
)
- [Django]-How would you create a 'manual' django migration?
- [Django]-Django serializer Imagefield to get full URL
- [Django]-Django – Login with Email
2👍
For those in Windows, the answer is a bit different. I had to do the following:
resp = None
with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as tmp_file:
image = Image.new('RGB', (100, 100), "#ddd")
image.save(tmp_file, format="JPEG")
tmp_file.close()
# create status update
with open(tmp_file.name, 'rb') as photo:
resp = self.client.post('/api/articles/', {'title': 'title',
'content': 'content',
'photo': photo,
}, format='multipart')
os.remove(tmp_file.name)
The difference, as pointed in this answer (https://stackoverflow.com/a/23212515/72350), the file cannot be used after it was closed in Windows. Under Linux, @Meistro’s answer should work.
- [Django]-Django REST Framework (DRF): Set current user id as field value
- [Django]-Django-allauth social account connect to existing account on login
- [Django]-How to receive json data using HTTP POST request in Django 1.6?