DRF not supporting multiple renderer_classes - django

I'm using Django 2.x and DRF
I have a view which returns the binary data in different formats like pdf, eps, etc.
To return binary response, I have set the renderer_classes property and my view is
class DownloadQRCode(APIView):
renderer_classes = (PdfFileRenderer, EPSRenderer,)
def post(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
name = serializer.validated_data.get('name')
data = serializer.validated_data.get('data')
mimetype = None
if data and name:
imgarr = name.rsplit('.', 1)
if len(imgarr) == 2:
name, format = imgarr
if format == 'pdf':
data = str(unquote(data, encoding='utf-8'))
data, mimetype = generate_data_cairo(data, format)
if format == 'eps':
data = str(unquote(data, encoding='utf-8'))
data, mimetype = svg_2_eps(data)
if data and mimetype:
response = Response(data=data, content_type=mimetype)
response['Content-Disposition'] = 'attachment; filename=%s' % "-".join(name.split())
return response
else:
return Response(status=status.HTTP_400_BAD_REQUEST, data='Either data or mime type was missing')
else:
return Response(status=status.HTTP_400_BAD_REQUEST, data='filename does not contain formatInfo')
else:
return Response(status=status.HTTP_400_BAD_REQUEST)
I have two renderer_classes
PdfFileRenderer
class PdfFileRenderer(BaseRenderer):
media_type = 'application/octet-stream'
format = None
charset = None
render_style = 'binary'
def render(self, data, media_type=None, renderer_context=None):
return data
and EPSRenderer
class EPSRenderer(BaseRenderer):
media_type = 'image/eps'
format = None
charset = 'utf-8'
render_style = 'binary'
def render(self, data, accepted_media_type=None, renderer_context=None):
return data
Using any one of them is working fine for that file type. But I want to use both so that appropriate renderer can be used depending on the file requested.
But, this is giving error and only first in the set is working.

Agree with #Nafees
Got it resolved by overriding get_renderer() method.
def get_renderer(self):
imgarr = name.rsplit('.', 1)
name, format = imgarr
if format == 'pdf':
return [BinaryRenderer()]
if format == 'eps':
return [EPSRenderer()]
return super().get_renderer()

Related

Decoding problem with files and serializers

In my API using Django Rest Framework I have a function based view for part of a sign up process for using Stripe. The view succefully sends everything to stripe and updates my sites database with the proper information, but when trying to return the serializer data I get this error:
'utf-8' codec can't decode byte 0xff in position 0: invalid start byte
Assuming it has to do with the files needed for personal identification
Here is the view:
#api_view(['POST'])
def StripePersonalOnboard(request):
serializer = StripePersonalSerializer(data=request.data)
profile = get_object_or_404(Profile, user=request.user)
if serializer.is_valid():
phone_number = serializer.validated_data.get('phone_number')
ss_number = serializer.validated_data.get('ss_number')
id_front = serializer.validated_data.get('id_front')
id_back = serializer.validated_data.get('id_back')
try:
stripe.api_key = settings.STRIPE_TEST_SEC_KEY
id_front_file = stripe.File.create(
purpose='identity_document',
file=id_front
)
id_back_file = stripe.File.create(
purpose='identity_document',
file=id_back
)
stripe.Account.modify(
profile.stripe_acc_id,
individual= {
'phone': phone_number,
'id_number': ss_number,
'verification': {
'document': {
'back': id_back_file,
'front': id_front_file
}
}
}
)
profile.stripe_id_front_tkn = id_front_file.id
profile.stripe_id_back_tkn = id_back_file.id
profile.save(update_fields=['stripe_id_front_tkn', 'stripe_id_back_tkn'])
return Response(serializer.validated_data)
except stripe.error.InvalidRequestError as e:
#invalid parameters were provided
return Response(e.error.message)
else:
return Response(serializer.errors)

download a file after rendering a template in django

I am trying to download a csv file and rendering a template at the same time after redirecting from a view but it only downloads the file ,doesn't render the template.
I think it can be handled by middleware but finding an easier solution to it.
class ProductUploadView(FormView):
template_name ='upload.html'
form_class = csvform
success_url = 'response/'
failed_rows = []
def post(self,request,*args,**kwargs):
form = self.form_class(request.POST,request.FILES)
file = request.FILES['csvfile']
csvfile = csv.reader(utf_8_encoder(file), dialect='excel', delimiter=',')
csvfile.next()
for row in csvfile:
if row[1] in ['PRES','GEN','GEN'] and row[2] in ['CAPS','TAB','SYP','GEL','SUSP','INJ']:
try:
p, created = Product.objects.get_or_create(brand_name=row[0], category=row[1], defaults={'form': row[2], 'mrp': row[3], 'ptr': row[4]})
except ValidationError:
self.failed_rows.append(row)
pass
else:
self.failed_rows.append(row)
return HttpResponseRedirect(self.success_url)
class UploadedView(ProductUploadView):
template_name = 'response.html'
rows = ProductUploadView.failed_rows
def download_view(self,request):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
writer = csv.writer(response)
writer.writerows(self.rows)
return response
def get(self,request):
response = TemplateResponse(request,self.template_name,{'num':len(self.rows),'failed_rows':self.rows})
response.add_post_render_callback(download_view)
return response

Tasty pie multipart post integer field become array

I uploading lie with additional fields to DJANGO testy pie and get validation error
If I post file field and char field - char field become from 'test' to '[test]'
If I post also integer field for example 57 it becomes ['57']
I check fron - it's ok
In back request.POST have converted data... Field in model described as small integer field
class SpkPhotoResource(ModelResource):
img = fields.FileField(attribute="filename", null=True, blank=True)
class Meta:
queryset = SpkPhoto.objects.all()
validation = CleanedDataFormValidation(form_class=forms.SpkPhotoForm)
...
def dehydrate_img(self, bundle):
return repr(bundle.obj.filename.name)
def deserialize(self, request, data, format=None):
if format is None:
format = request.META.get('CONTENT_TYPE','application/json')
if format == 'application/x-www-form-urlencoded':
return request.POST
elif format.startswith('multipart'):
data = request.POST.copy()
data.update(request.FILES)
return data
return super(SpkPhotoResource, self).deserialize(request, data, format)
//model:
class SpkPhoto(models.Model):
....
filename = models.ImageField(db_column='FILENAME', max_length=256, upload_to=getFilePath, null=False, blank=False)
site_order = models.SmallIntegerField(db_column='SITE_ORDER', blank=True, null=True)
class Meta:
...
def save(self, *args, **kwargs):
p = str('media/OFFICE/' + str(self.spk_propertyid.office.id))
self.filename.storage = FileSystemStorage(location = p )
if not self.spk_photoid:
self.spk_photoid = get12charid_spk_photoid()
super(SpkPhoto, self).save(*args, **kwargs)
def delete(self, *args, **kwargs):
p = str('media/OFFICE/' + str(self.spk_propertyid.office.id))
self.filename.storage = FileSystemStorage(location = p )
self.filename.delete()
super(SpkPhoto, self).delete(*args, **kwargs)
FormData multipart -Ajax-post- DJANGO-tastie-deserialize data in querydict is as array so just remake returned data from deserialize make it ok
def deserialize(self, request, data, format=None):
if format is None:
format = request.META.get('CONTENT_TYPE','application/json')
if format == 'application/x-www-form-urlencoded':
return request.POST
elif format.startswith('multipart'):
data = request.POST.copy()
data.update(request.FILES)
data2 = {}
for name, value in data.items():
data2[name] = value
return data2
return super(SpkPhotoResource, self).deserialize(request, data, format)

Django: How can I download images from Base64ImageField?

I can use json format to upload images with Base64ImageField (by POST), but now I want to download images in the same way (by POST). When I POST a image to "driver_photo", for example:
"R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
,the image would save as "driver_photo":"driver_photos/2016/05/20/d2821170-662.gif".
My question is how could I open the file , encode it for convenient transmition by Base64ImageField and POST them.
Below is my serializer.py
class PassengerDataSerializer(serializers.ModelSerializer):
passenger_photo = Base64ImageField(
max_length=None, use_url=True,
)
class Meta:
model = PassengerData
fields = ("passenger_name","passenger_phone", "passenger_photo")
class TaxiDriverDataSerializer(serializers.ModelSerializer):
PassengerDatas = PassengerDataSerializer( many=True )
driver_photo = Base64ImageField(
max_length=None, use_url=True,
)
class Meta:
model = TaxiDriverData
fields = ("pk","fingerprint","driver_name","driver_photo","PassengerDatas")
def create(self, validated_data):
taxiDriverData = TaxiDriverData.objects.create(fingerprint = validated_data['fingerprint'],
driver_name = validated_data['driver_name'],
driver_photo = validated_data['driver_photo'],
)
taxiDriverData.save()
Passenger_Datas = validated_data.pop('PassengerDatas')
for passenger in Passenger_Datas:
passengerdata = PassengerData.objects.create(passenger_name = passenger.get('passenger_name'),
passenger_phone = passenger.get('passenger_phone'),
passenger_photo = passenger.get('passenger_photo'), )
passengerdata.taxi_driver_data_id = taxiDriverData
passengerdata.save()
return taxiDriverData
def update(self, instance, validated_data):
instance.fingerprint = validated_data['fingerprint']
instance.driver_name = validated_data['driver_name']
instance.driver_photo = validated_data['driver_photo']
instance.save()
return instance
views.py
#api_view(['GET','POST'])
def taxi_driver_list(request, format=None):
if request.method=="GET":
TaxiDriverDatas =list(TaxiDriverData.objects.all())
serializer = TaxiDriverDataSerializer(TaxiDriverDatas,many=True)
return Response(serializer.data)
elif request.method == 'POST':
#print (request.body)
serializer = TaxiDriverDataSerializer(data=request.data)
#, files=request.FILES
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
#api_view(['GET', 'PUT', 'DELETE'])
def taxi_driver_detial(request,pk, format=None):
try:
Taxi_Driver_Data = TaxiDriverData.objects.get(pk=pk)
except TaxiDriverData.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == "GET":
serializer = TaxiDriverDataSerializer(Taxi_Driver_Data)
return Response(serializer.data)
elif request.method == "PUT":
serializer = TaxiDriverDataSerializer(Taxi_Driver_Data,data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors,status=status.HTTP_400_BAD_REQUEST)
elif request.method == "DELETE":
Taxi_Driver_Data.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
models.py
class TaxiDriverData(models.Model):
fingerprint = models.CharField(max_length=30, default="")
driver_name = models.CharField(max_length=30, default="")
driver_photo = models.ImageField(upload_to ="driver_photos/%Y/%m/%d/", null=True ,blank=True)
class PassengerData(models.Model):
taxi_driver_data_id = models.ForeignKey(TaxiDriverData, related_name='PassengerDatas', null=True)
passenger_name = models.CharField(max_length=100, default="")
passenger_phone = models.CharField(validators=[phone_regex], blank=True, max_length=16)
passenger_photo = models.ImageField(upload_to ="passenger_photos/%Y/%m/%d/", null=True ,blank=True )
Thanks for help.
It is highly recommended to use POST instead of GET to send/receive Base64 images, since Base64 images are converted to String with length of string varying upto 15000 characters. If you are using the GET method, you are limited to a maximum of 2,048 characters.
Save the image
import base64
imgdata = base64.b64decode(imgstring)
filename = 'some_image.jpg' # I assume you have a way of picking unique filenames
with open(filename, 'wb') as f:
f.write(imgdata)
Edit1
You could do something like this to save the image
import base64
import datetime
def save_image(request):
b64_image = request.POST['driver_photo'] #key that is being used to send the data
imgdata = base64.b64decode(b64_image)
var = datetime.datetime.now().strftime("%d%m%Y%H%M%S") #This will give unique values everytime. Values are based on current datetime
filename = "PATH_TO_SAVE_FILE" + var +'.jpg'
with open(filename, 'wb') as f:
f.write(imgdata)
Edit 2
POST Request in Android
HttpClient httpclient = new DefaultHttpClient();
String responseStr="";
String URL=Constants.API_URL;#URL where request needs to be sent
HttpPost httppost = new HttpPost(URL);
try {
// Add your data
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("id", pick_up_id));
nameValuePairs.add(new BasicNameValuePair("driver_photo", strPhoto));#image in form of Base64 String which you need to send
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
// Execute HTTP Post Request
HttpResponse response = httpclient.execute(httppost);
int responseCode = response.getStatusLine().getStatusCode();
switch(responseCode) {
case 200:
HttpEntity entity = response.getEntity();
if(entity != null) {
String responseBody = EntityUtils.toString(entity);
responseStr=responseBody;
}
break;
}
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
} catch (IOException e) {
// TODO Auto-generated catch block
}
System.out.println("this is response "+responseStr);

How do i pass a form data as context?

I have this PDFview class now i want to use it in any form template so i can see preview of that form data before sending data.
class PDFView(View):
def get(self, request, **kwargs):
template = get_template("pim/pdfview.html")
ctx = {}
ctx["test"] = Employee.objects.all()
context = Context(ctx)
html = template.render(context)
result = StringIO.StringIO()
pdf = pisa.pisaDocument(StringIO.StringIO(html.encode("ISO-8859-1")), result)
if not pdf.err:
response = http.HttpResponse(result.getvalue(), mimetype='application/pdf')
response['Content-Disposition'] = 'attachment; filename="leavereport.pdf"'
return response
return http.HttpResponse('We had some errors<pre>%s</pre>' % cgi.escape(html))