download a file after rendering a template in django - 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

Related

Export all list_display fields to csv in django admin

I'm using the recipe at https://books.agiliq.com/projects/django-admin-cookbook/en/latest/export.html
class ExportCsvMixin:
def export_as_csv(self, request, queryset):
meta = self.model._meta
field_names = [field.name for field in meta.fields]
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename={}.csv'.format(meta)
writer = csv.writer(response)
writer.writerow(field_names)
for obj in queryset:
row = writer.writerow([getattr(obj, field) for field in field_names])
return response
export_as_csv.short_description = "Export Selected"
which works great except it doesn't export all the columns I see in the admin panel. I have added some extra columns like this
class MeetingAdmin(admin.ModelAdmin, ExportCsvMixin):
actions = ["export_as_csv"]
list_display = (
"__str__",
"expert_network",
)
def expert_network(self, obj):
if obj.expert:
return obj.expert.network.name
else:
return "-"
is it possible to improve the ExportCsvMixin to also export the callable list_display fields?
I'm currently trying to loop over the list_display but I don't know how to use callables
class ExportCsvMixin:
def export_as_csv(self, request, queryset):
meta = self.model._meta
# field_names = [field.name for field in meta.fields]
field_names = list(self.list_display)
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = "attachment; filename={}.csv".format(meta)
writer = csv.writer(response)
writer.writerow(field_names)
for obj in queryset:
result = []
for field in field_names:
attr = getattr(obj, field)
if callable(attr):
result.append(attr())
else:
result.append(attr)
row = writer.writerow(result)
return response
export_as_csv.short_description = "Export Selected"
came up with this
class ExportCsvMixin:
def export_as_csv(self, request, queryset):
meta = self.model._meta
# field_names = [field.name for field in meta.fields]
field_names = list(self.list_display)
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = "attachment; filename={}.csv".format(meta)
writer = csv.writer(response)
writer.writerow(field_names)
for obj in queryset:
result = []
for field in field_names:
attr = getattr(obj, field, None)
if attr and callable(attr):
result.append(attr())
elif attr:
result.append(attr)
else:
attr = getattr(self, field, None)
if attr:
result.append(attr(obj))
else:
result.append(attr)
row = writer.writerow(result)
return response
export_as_csv.short_description = "Export Selected"
basically we look for the attribute or callable on the object, if it's not there then we look at the modelAdmin and invoke the callable with the object as the arg.

DRF not supporting multiple renderer_classes

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()

How to pre fill the values in form fields which are already in database and deliver the partial form or taking the remaining inputs

i want to make a form in which the database is queryed and prefill the fields which are already been present to that model instance id or pk.
models.py
class Uploads(models.Model):
name = models.CharField(max_length=20,blank=True)
age = models.IntegerField(blank=True)
gstin = models.CharField(max_length=15,blank=False,default='xxxxxxxxxx00000')
PAN = models.CharField(max_length=10,default="xxxxxxxxxx",blank=False)
doc_pdf = models.FileField(upload_to='static/files',blank=True)
image = models.ImageField(upload_to='static/images',blank=True)
def __str__(self):
return self.name
forms.py [ do i have to use super().clean() in clean method?, bcoz without using super() is also working , whats the use of super() ]
class Uploadform(forms.ModelForm):
class Meta:
model = Uploads
fields ='__all__'
def clean_gstin(self):
gstin = self.cleaned_data.get('gstin')
print(gstin)
if len(gstin) != 15:
raise forms.ValidationError('Input correct length of GSTIN')
if gstin[:2].isdigit() == False:
raise forms.ValidationError('put the 1st two digits as number')
return gstin
def clean_PAN(self):
PAN = self.cleaned_data.get('PAN')
if len(PAN) != 10:
raise forms.ValidationError('input correct length')
return PAN
def clean(self):
gst = self.cleaned_data.get('gstin')
pan = self.cleaned_data.get('PAN')
print(pan)
print(gst)
if not pan in gst:
raise forms.ValidationError('worng GSTIN')
views.py
def uploadview_django(request , id):
x= Uploads.objects.all.filter(pk = id)
if x:
gst = x.gstin
pan = x.pan
if request.method == 'POST':
form = Uploadform(request.POST , request.FILES)
if form.is_valid():
form.save()
return redirect('index')
else:
form = Uploadform(gstin = gst , PAN = pan)
return render(request,'form.html',{'form':form})
after suggestion done this:
def uploadview_django(request):
try:
x = Uploads.objects.all().filter(pk = 12)
except Uploads.DoesNotExists:
x= None
if request.method == 'POST':
form = Uploadform(request.POST , request.FILES , instance=x)
if form.is_valid():
form.save()
return redirect('index')
else:
form = Uploadform(instance=x)
return render(request,'form.html',{'form':form})
showing error:
Traceback:
File "C:\Users\J A X\Anaconda3\envs\madeenv\lib\site-packages\django\core\handlers\exception.py" in inner
34. response = get_response(request)
File "C:\Users\J A X\Anaconda3\envs\madeenv\lib\site-packages\django\core\handlers\base.py" in _get_response
126. response = self.process_exception_by_middleware(e, request)
File "C:\Users\J A X\Anaconda3\envs\madeenv\lib\site-packages\django\core\handlers\base.py" in _get_response
124. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\J A X\Desktop\Django\practice\Appone\views.py" in uploadview_django
27. form = Uploadform(instance=x)
File "C:\Users\J A X\Anaconda3\envs\madeenv\lib\site-packages\django\forms\models.py" in init
292. object_data = model_to_dict(instance, opts.fields, opts.exclude)
File "C:\Users\J A X\Anaconda3\envs\madeenv\lib\site-packages\django\forms\models.py" in model_to_dict
82. opts = instance._meta
Exception Type: AttributeError at /form_django
Exception Value: 'QuerySet' object has no attribute '_meta'
First you must query using get x= Uploads.objects.all.get(pk = id) , make sure to put this in a try except block, like this
try:
x= Uploads.objects.get(pk = id)
except:
x=None
Simple just pass the instance in the form's post and get methods, like this form = Uploadform(request.POST , request.FILES, instance=x) and form = Uploadform(instance=x).
Now it should fill the fields values for which instance is given.
For an example from the docs look at this
You have to pass the instance while initialising the form like below
x = Uploads.objects.all.filter(pk=id)
# .... your code....
if request.method == 'POST':
form = Uploadform(instance=x, request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('index')
else:
form = Uploadform(instance=x)
# .... your code....

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))