attaching a csv file django - django

Hi i have generated a csv and it saves correctly in my media dir .
This is how I generate the csv file :
def export_to_csv_file(data, titles, file_name):
csv_file_address = os.path.join(settings.MEDIA_ROOT, file_name)
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = "attachment; filename={}".format(file_name)
response.write("\ufeff".encode("utf8"))
with open(csv_file_address, "w+") as file:
writer = csv.DictWriter(file, fieldnames=titles, extrasaction="ignore")
headers = {title: _(title).replace("_", " ").title() for title in titles}
writer.writerow(headers)
for row in data:
writer.writerow(row)
return response, file
and this is how I use this function :
data_file = export_to_csv_file(
serializer.data,
list(self.get_serializer().Meta.headers),
file_name,
)
email_html_message = render_to_string(
"myhtml.html",
{"host": settings.SITE_URL, "file_url": data_file[1].name},
)
email = EmailMessage(
email_title,
email_html_message,
settings.EMAIL_FROM,
["mont#gmail.com"],
)
with open(data_file[1].name, "r") as file:
email.attach("attach_file", file.read(), "text/csv")
email.send()
when I pdb the file I got this:
<_io.TextIOWrapper name='D:\\02_work\\Golf\\media\\spender_liste_1669015631.216596.csv' mode='r' encoding='cp1252'>
what should I do to send this csv file as an attached file?
Also I have tested the
with open(data_file[1].name, "w+") as file:
email.attach("attach_file", file.read(), "text/csv")
email.send()
but this time get
IndexError: list index out of range
in the email.send() line

Related

Generate multiple PDFs and zip them for download, all in a single view

I am using xhtml2pdf to generate PDFs in my Django View. The idea is to loop over all the instances that are there in the query, then for each instance create a PDF, then add all the generated PDFs to one zip File for download. The xtml2pdf logic is working okay but the looping logic is what gives me headache.
So this is my function so far:
def bulk_cover_letter(request, ward_id, school_cat_id, cheque_number):
school_type = SchoolType.objects.get(id=school_cat_id)
schools_in_school_type = Applicant.objects.filter(
school_type=school_type, ward_id=ward_id, award_status='awarded'
).order_by().values_list('school_name', flat=True).distinct()
for school in schools_in_school_type:
beneficiaries = Applicant.objects.filter(school_type=school_type, ward_id=ward_id, award_status='awarded', school_name=school)
total_amount_to_beneficiaries = Applicant.objects.filter(school_type=school_type, ward_id=ward_id, award_status='awarded', school_name=school).aggregate(total=Sum('school_type__amount_allocated'))
context = {
'school_name' : school,
'beneficiaries' : beneficiaries,
'total_amount_to_beneficiaries' : total_amount_to_beneficiaries,
'title' : school + ' Disbursement Details',
'cheque_number': cheque_number
}
response = HttpResponse('<title>Cover Letter</title>', content_type='application/pdf')
filename = "%s.pdf" %(cheque_number)
content = "inline; filename=%s" %(filename)
response['Content-Disposition'] = content
template = get_template('cover_letter.html')
html = template.render(context)
result = io.BytesIO()
pdf = pisa.CreatePDF(
html, dest=response, link_callback=link_callback)
if not pdf.error:
# At this point I can generate a single PDF.
# But no idea on what to do next.
# The zipping logic should follow here after looping all the instances - (schools)
From that Point I have no idea on what to do next. Any help will be highly appreciated.
Try this:
Utils.py
def render_to_pdf(template_src, context_dict={}):
template = get_template(template_src)
html = template.render(context_dict)
buffer = BytesIO()
p = pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")), buffer)
pdf = buffer.getvalue()
buffer.close()
if not p.err:
return pdf#HttpResponse(result.getvalue(), content_type='application/pdf')
return None
def generate_zip(files):
mem_zip = BytesIO()
with zipfile.ZipFile(mem_zip, mode="w",compression=zipfile.ZIP_DEFLATED) as zf:
for f in files:
zf.writestr(f[0], f[1])
return mem_zip.getvalue()
Views.py
def generate_attendance_pdf(modeladmin, request, queryset):
template_path = 'student/pdf_template.html'
files = []
for q in queryset:
context = {
'firstname': q.firstname,
'lastname': q.lastname,
'p_firstname': q.bceID.firstname
}
pdf = render_to_pdf(template_path, context)
files.append((q.firstname + ".pdf", pdf))
full_zip_in_memory = generate_zip(files)
response = HttpResponse(full_zip_in_memory, content_type='application/force-download')
response['Content-Disposition'] = 'attachment; filename="{}"'.format('attendnace.zip')
return response
Obviously, you have to modify the context/names to what you need.
Credit to -> Neil Grogan https://www.neilgrogan.com/py-bin-zip/
If you need to generate several PDF files and send them as a response in a zip file then you can store the reports in memory and set it as dest when you call pisa.CreatePDF. Then have a list of reports in memory, zip them, and send as a Django response specifying another content type.
For example:
reports = tempfile.TemporaryDirectory()
report_files = {}
for school in schools_in_school_type:
# ... same code that renerates `html`
mem_fp = BytesIO()
pisa.CreatePDF(html, dest=mem_fp)
report_files[filename] = mem_fp
mem_zip = BytesIO()
with zipfile.ZipFile(mem_zip, mode="w") as zf:
for filename, content in report_files.items():
zf.write(filename, content)
response = HttpResponse(mem_zip, content_type='application/force-download')
response['Content-Disposition'] = 'attachment; filename="{}"'.format('cover_letters.zip')
This still generates an error of [Errno 2] No such file or directory: 'cheque_number.pdf'.

Django download file set correct name

Right now I use the following function for downloading files:
def download_xlsx(request):
user = request.user
file_name = request.GET['file_name']
file_path='main_app/static/xlsx/' + str(user.id) + '/' + file_name
if os.path.exists(file_path):
with open(file_path, 'rb') as fh:
response = HttpResponse(fh.read(), content_type="application/vnd.ms-excel")
response['Content-Disposition'] = "inline; filename=%s" % file_name
return response
Using the following url http://127.0.0.1:8000/download_xlsx?file_name=test.xlsx
I downloaded file named 'download_xlsx'
I tried to write something like this:
response['Content-Disposition'] = "inline; filename='+ file_name
But didn't help.
How do I rename my file to file_name var ?
UPD: it seems to be the problem with non-english file_names. But still don't know how do deal with it
Works like this(the problem was in encoding)
response['Content-Disposition'] = "inline; filename=%s" % file_name.encode('utf-8')

Return Zip file with HttpResponse using StringIO, Django, Python

I'm trying to return a zip file with HttpResponse, using StringIO() because i'm not storing in DB or Harddrive.
My issue is that my response is returning 200 when i request the file, but the OS never ask me if i want to save the file, or the file is never saved. i think that the browser is reciving the file because i have seen on the Network Activity (inspect panel) and it says than a 6.4 MB file type zip is returned.
I'm taking a .step file (text file) from a DB's url, extracting the content, zipping and returning, that's all.
this my code:
def function(request, url_file = None):
#retrieving info
name_file = url_file.split('/')[-1]
file_content = urllib2.urlopen(url_file).read()
stream_content = StringIO(file_content)
upload_name = name_file.split('.')[0]
# Create a new stream and write to it
write_stream = StringIO()
zip_file = ZipFile(write_stream, "w")
try:
zip_file.writestr(name_file, stream_content.getvalue().encode('utf-8'))
except:
zip_file.writestr(name_file, stream_content.getvalue().encode('utf-8', 'ignore'))
zip_file.close()
response = HttpResponse(write_stream.getvalue(), mimetype="application/x-zip-compressed")
response['Content-Disposition'] = 'attachment; filename=%s.zip' % upload_name
response['Content-Language'] = 'en'
response['Content-Length'] = write_stream.tell()
return response

How to convert content of InMemoryUploadedFile to string

Does anyone know how to convert content of uploaded file (InMemoryUploadedFile) in Django2 to string?
I want to know how to write the following convert2string():
uploaded_file = request.FILES['file']
my_xml = convert2string(uploaded_file) # TODO write method(convert to xml string)
obj = MyObject()
parser = MyContentHandler(obj)
xml.sax.parseString(my_xml, parser) # or xml.sax.parse(convertType(uploaded_file), parser)
Try str(uploaded_file.read()) to convert InMemoryUploadedFile to str
uploaded_file = request.FILES['file']
print(type(uploaded_file)) # <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
print(type(uploaded_file.read())) # <class 'bytes'>
print(type(str(uploaded_file.read()))) # <class 'str'>
UPDATE-1
Assuming you are uploading a text file (.txt,.json etc) as below,
my text line 1
my text line 2
my text line 3
then your view be like,
def my_view(request):
uploaded_file = request.FILES['file']
str_text = ''
for line in uploaded_file:
str_text = str_text + line.decode() # "str_text" will be of `str` type
# do something
return something
file_in_memory -> InMemoryUploadedFile
file_in_memory.read().decode() -> txt output
If you want to read file data as a list, then you can try out the following code:
uploaded_file = request.FILES['file']
file_data = uploaded_file.read().decode().splitlines() # get each line data as list item

returning zip for download from view in django

I try to download a zip file in my Django application.
How should I return it from the view?
I tried the code below, but I get some kind of alert in the browser with the content of the file inside my zip.
What am I doing wrong?
def download_logs(request):
date = datetime.datetime.now().__str__().replace(" ", "_").split(".")[0]
os.system("df -h . > /tmp/disk_space")
response = HttpResponse(mimetype='application/zip')
response['Content-Disposition'] = 'filename=logs_%s.zip' % date
files = []
files.append("/tmp/disk_space")
buffer = StringIO()
zip = zipfile.ZipFile(buffer, "w", zipfile.ZIP_DEFLATED)
for name in files:
file = open(name, "r")
zip.writestr(name, file.read())
file.close()
zip.close()
buffer.flush()
ret_zip = buffer.getvalue()
buffer.close()
response.write(ret_zip)
return response
You should tell the browser to treat the response as a file attachment.
From the docs, you should do something like:
>> response = HttpResponse(my_data, mimetype='application/vnd.ms-excel')
>>> response['Content-Disposition'] = 'attachment; filename=foo.xls'
Here is a link to actual working code for building a ZipFile in memory and returning it to the user as a file to download: django-rosetta's view.py