DRF upload extracted zip files - django

I'm going to upload some files through my DRF API. This API receives a .zip file that has some .xlsx files in. I extract its content in the API view. Then I send this data to the serializer, But I get this error:
[
{
"file_name": [
"The submitted data was not a file. Check the encoding type on the form."
]
}
]
This is how I'm extracting my .zip file before passing data to the serializer:
def _get_serializer_initial_data(self):
with ZipFile(self.request.FILES.get('file_name')) as input_zip:
return [
{
'file_name': input_zip.open(_input_excel)
}
for _input_excel in input_zip.infolist()
]
Then I send data to the serializer this way:
initial_data = self._get_serializer_initial_data()
serializer = self.get_serializer(data=initial_data, many=True)
I checked the extracted files type in the serializer class and it's ZipExtFile, which seems to be unacceptable by DRF.
Any help would be appreciated.

Related

Django rest framework bulk post object

I want insert bulk json via django rest_framework however, I get a error message ;
typed following line ;
{
"non_field_errors": [
"Invalid data. Expected a dictionary, but got list."
]
}
my serializer.py file include following code block;
class DrfSerializerV2(serializers.ModelSerializer):
def __init__(self,*args,**kwargs):
many=kwargs.pop('many',True)
super(DrfSerializerV2,self).__init__(many=True,*args,**kwargs)
class Meta:
model = DrfData
fields=['id','name','duration','rating','typ']
and my views.py file ;
class ActionViewSet(viewsets.ModelViewSet):
queryset = DrfData.objects.filter(typ="action")
serializer_class = serializers.DrfSerializerV2
finally send json file via post;
[
{"name":"m1","duration":120,"rating":4.7},
{"name":"m2","duration":120,"rating":4.5}
]
however, I can't handle this error message.
if I send sperataly, there is no problem.
but, I can't resolve this error for bulk insert

In django serializer response, how to get rid of absolute path (media_url prefix) for FileField?

I have used a models.FileField() in my model and I have created a simple ModelSerializer and CreateAPIView for it. As I am using AWS S3, when posting a file, I receive Json response that includes MEDIA_URL as prefix of filename i.e.:
{
"id": 32,
"file": "https://bucket.digitaloceanspaces.com/products/capture_a50407758a6e.png"
}
What i would like to achieve instead is to either keep only a file name:
{
"id": 32,
"file": "capture_a50407758a6e.png"
}
or to be able to change the prefix to some other:
{
"id": 32,
"file": "https://google.com/capture_a50407758a6e.png"
}
The change should only be visible in Json response and and the database entry. The S3 details should be still sourced as they are from settings.py.
Hope it makes sense.
Let me know if anyone has any idea as I'm only able to find solution for providing full/absolute path.
Thanks
I think what you're looking for is the serializer method to_representation.
In your case, this might look like:
import os
class MySerializer(serializers.ModelSerializer):
# serializer contents
def to_representation(self, instance):
ret = super().to_representation(instance)
ret['file'] = os.path.basename(ret['file'])
return ret
That should make the serializer return all normal values besides the basename of the file!

Django Rest Framework api test upload text file application/x-www-form-urlencoded

I'm trying to test my Django REST API for file uploading.
The catch is that my server tries to validate the file so that the file has a recognised file type. Currently only text-based filetypes are allowed, like: docs, pdf, txt.
I've tried using a temporary file and now I just tried to read a file from the disk then gave up.
Whenever I use a temporary file the server responds with:
{
"cover_letter": [
"The submitted data was not a file. Check the encoding type on the form."
],
"manuscript": [
"The submitted data was not a file. Check the encoding type on the form."
]
}
This is my test:
def test_user_can_submit_a_paper(self):
"""
Ensure that an user is able to upload a paper.
This test is not working.. yet
"""
tmp_file = open("__init__.py", "w")
data = {
"title": "paper",
"authors": "me",
"description": "ma detailed description",
"manuscript": base64.b64encode(tmp_file.read()).decode(),
"cover_letter": base64.b64encode(tmp_file.read()).decode()
}
tmp_file.close()
response = self.client.post(self.papers_submitted, data=urlencode(MultiValueDict(data)), content_type='application/x-www-form-urlencoded',
HTTP_AUTHORIZATION=self.authorization_header)
print(response.data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
del data["cover_letter"]
response = self.client.post(self.papers_submitted, data=urlencode(MultiValueDict(data)), content_type='application/x-www-form-urlencoded',
HTTP_AUTHORIZATION=self.authorization_header)
print(response.data)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
I have successfully tested this endpoint via postman but I don't know how to do it in Python.
If I understand what you are asking, you'd like to test your DRF endpoint with a file upload, using python. You should use RequestFactory, which simulates a request -- it will be more like using Postman.
This answer Django: simulate HTTP requests in shell has an example of using RequestFactory, and there are multiple answers here django RequestFactory file upload for specific solutions on how to include uploaded files with RequestFactory.
I've managed to come up with a solution thanks to Mark's answer.
Here's the code if anyone is interested:
def test_user_can_submit_a_paper(self):
from api.journal import PaperListSubmitted
from django.test.client import RequestFactory
from django.core.files import temp as tempfile
"""
Ensure that an user is able to upload a paper.
"""
request_factory = RequestFactory()
manuscript = tempfile.NamedTemporaryFile(suffix=".txt")
cover_letter = tempfile.NamedTemporaryFile(suffix=".txt")
manuscript.write(b"This is my stupid paper that required me to research writing this test for over 5h")
cover_letter.write(b"This is my stupid paper that required me to research writing this test for over 5h")
manuscript.seek(0)
cover_letter.seek(0)
post_data = {
"title": "My post title",
"description": "this is my paper description",
"authors": "no authors",
"manuscript": manuscript,
"cover_letter": cover_letter
}
request = request_factory.post(self.papers_submitted, HTTP_AUTHORIZATION=self.authorization_header,
data=post_data)
response = PaperListSubmitted.as_view()(request)
self.assertEqual(response.status_code, status.HTTP_200_OK)

Django Rest Framework Serializer POST data not receiving arrays

I'm using the DRF test api for testing my serializers, for instance I made my data like this:
image_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../media/', 'product_images/', 'myimage.png')
with open(image_path) as image:
encoded_image = base64.b64decode(image.read())
data = {
u'small_name': u'Product Test Case Small Name',
u'large_name': u'Product Test Case Large Name',
u'description': u'Product Test Case Description',
u'images': [
{u'image': encoded_image}
],
u'variants': [
{u'value': u'123456789'}
]
}
response = self.client.post(url, data, 'multipart')
However when I receive the data on the respective serializer the variants array and the images array are empty.
What could be the issue here?
Thanks to Linovia and Rahul Gupta I was able to resolve the image and arrays problem. I changed my serializer to receive an Base64ImageField using the one provided by django-extra-fields. Rahul also pointed a silly error on my code.
I'll leave some code to help someone with the same problem.
serializers.py file
from drf_extra_fields.fields import Base64ImageField
class ProductImageSerializer(serializers.ModelSerializer):
source = Base64ImageField(source='image', required=False, )
test.py file
image_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../media/', 'product_images/', 'myimage.png')
with open(image_path) as image:
encoded_image = base64.b64encode(image.read())
data = {
u'small_name': u'Product Test Case Small Name',
u'large_name': u'Product Test Case Large Name',
u'description': u'Product Test Case Description',
u'images': [
{u'source': encoded_image}
],
u'variants': [
{u'value': u'123456789'}
]
}
response = self.client.post(url, data, format='json')
You can not have both the image upload and nested data.
Either you want to upload the image and use multipart encoding (HTML forms) which means your arrays will not work (unless it's a list of foreign key or similar). This could work with JSon content type but then you'll loose the ability to upload image. Your option there might be to encode with base64 the image and upload it but you'll have to customize a few things on the serializer.
Try to encode the image using base64.b64encode() instead of the base64.b64decode() method.
encoded_image = base64.b64encode(image.read())
Also, send json encoded data with content type set to application/json.
self.client.post(url, json.dumps(data), content_type='application/json')
You have to set the format of clientto JSON:
response = self.client.post(url, data, format='json')

Send file from backbone Model to django

I want to upload files from Backbone to Django File upload system.
First of all I've follow the https://stackoverflow.com/a/10916733/1590377 explanation. I've do a FileModel and with the above indication I have a model with this information:
attributes: Object
data: "data:image/png;base64,iVBORw ..."
file: "image2012-06-12 13:36:45.png"
now I save the model to the URL where I have the upload view in django like this:
def upload_file_64(request):
if request.method == 'POST':
file = cStringIO.StringIO(base64.b64decode(request.POST['data']))
#method to save the file
response_data={"result":"ok"}
return HttpResponse(simplejson.dumps(response_data), mimetype='application/json')
else:
response_data={"success": "No a post request"}
return HttpResponse(simplejson.dumps(response_data), mimetype='application/json')
but the response that the django sistem give me is:
"MultiValueDictKeyError at /api/upload64/↵'Key \'data\' not found in <QueryDict: {u\'base64,iVBORw0KG....
The POST http request is:
POST:
base64,iVBORw0KG ..."} = u''
{"file":"Captura de pantalla de 2012-06-12 13:36:45.png","data":"data:image/png = u''
How I can fix this so that I can upload a file to django. I use a multi-part method to upload files from another platforms how android but with backbone I can't upload a file.
Can someone help me eith this problem?
Thanks!!
I've coded another solution. I've used a jquery upload pluging to upload the file, and get the response.
The plugin is : http://lagoscript.org/jquery/upload/demo?locale=en and the code that I used in my backbone view is:
events : {
'change #file1' : 'upload'
},
upload : function(){
$('#file1').upload('http://192.168.0.195/api/upload/', function(res) {
console.log(res)
//now I use the res to create a model :)
}, 'html');
},