i have an app in django that do some processes and build a file, in one section, i want to show the user that file to download that.
(for example, user enter a name in front-end and the app give him a pdf file to download).
the file building process is ok and it is in /app_dir/media/app/report/username/file.pdf
here is my code, but it does not worked and i faced some problems.
can you please help me, where is my problem?
and how i can make that user-specefic? each user just can access to his files.
views.py (my_main_function):
#login_required(login_url='/login/')
def my_function(request):
if request.method == 'GET':
return render(request, 'app/my_function.html')
else:
try:
#my main process to build .PDF file
except ValueError:
return render(request, 'app/dashboard.html', {'error':'Bad data passed in. Try again.'})
return render(request, 'app/download.html')
views.py (download function):
def download_file(request):
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
filename = f"{file}.pdf"
# Define the full file path
filepath = f"{BASE_DIR}/app/media/app/reports/{user_name}/{filename}"
# Open the file for reading content
path = open(filepath, 'rb')
path.read()
# Set the return value of the HttpResponse
response = HttpResponse(path, content_type='application/pdf')
# Set the HTTP header for sending to browser
response['Content-Disposition'] = "attachment; filename=%s" % filepath
# Return the response value
return response
urls.py:
path('download/', views.download_file, name='download_file'),
simple testing download.html file:
<html>
<title>Download File</title>
</head>
<body>
<enter>
<h1>Download File using link</h1>
Download PDF
</center>
</body>
</html>
It is not clear what problems you have encountered, but there are some lines I would correct in your code
# first of all why not use django.http.FileResponse instead of HttpResponse?
# https://docs.djangoproject.com/en/3.2/ref/request-response/#fileresponse-objects
from django.http import FileResponse
# if you want files to be accessible only to owners
# you probably should force user to login before download
#login_required
def download_file(request):
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
filename = f"{file}.pdf"
filepath = f"{BASE_DIR}/app/media/app/reports/{user_name}/{filename}"
# somewhere here you need to check if user has access to the file
# if files ownership based solely on {user_name} dir in filesystem
# then it is enough to check if file exists
if os.path.exists(filepath):
response = FileResponse(open(filepath, 'rb'))
# it is probably better to use filename instead of filepath
response['Content-Disposition'] = "attachment; filename=%s" % filename
return response
else:
raise Http404
Related
I am a beginner in Django. I am trying to let user download a file after the specific process is completed.
Here is view.py. The download button is shown after the process is completed. Users can download the file named WS_file_name+'.xlsx' by clicking the download button.
from django.shortcuts import render
from django.http import HttpResponse
def index(request):
if request.method == 'POST':
student = StudentForm(request.POST, request.FILES)
if student.is_valid():
handle_uploaded_file(request.FILES['file'])
firstname= student.cleaned_data.get("firstname")
lastname= student.cleaned_data.get("lastname")
### Processing ###
WS_file_name = lastname + firstname + newfile
Toollist_Raw = pd.read_excel(Toollist_path+Toollist_file_name)
WS_file = xlsxwriter.Workbook(WS_file_name+'.xlsx')
WS_file.close()
file_source = WS_Path + WS_file_name+'.xlsx'
Toollist_Raw.to_excel(file_source, sheet_name='CALM_Toollist',index=False)
### Process had completed, users can click download button to download the file ###
context= {'username': firstname, 'version':lastname,}
return render(request, 'template_Download.html', context)
else:
student = StudentForm()
return render(request,"template_Form.html",{'form':student})
##### Download Functions #####
import os
from django.http import FileResponse
def download_file(request):
# Define Django project base directory
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Define file name
filename = WS_file_name+'.xlsx'
# Define the full file path
filepath = BASE_DIR + '/Capital_Report_Website/Download_Files/Baseline_Cleanup_Toollist_vs_CALM_Download/' + filename +'.xlsx'
return FileResponse(open(filepath, 'rb'), as_attachment=True)
The below code is template_Form.html. This page is to let user fill in the information which is used to process the file.
<form method="POST" class="post-form" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Generate Report</button>
</form>
The below code is template_Download.html. This page is shown after the process is completed. The download button is right on this page.
<h3>Hi, {{username}} your toollist {{version}} vs CALM report is completed.</h3>
Download
The below code is urls.py which is used to call the functions in views.py.
urlpatterns = [
path('admin/', admin.site.urls),
path('toollistvscalm/', views.index),
path('toollistvscalm/download/', views.download_file),
]
It showed the below error after I clicked download button.
name 'WS_file_name' is not defined
After all, the whole process I want is as follows.
Users fill in lastname, firstname and upload the file newfile in form.
Django get newfile and do the process to create a new file (the file name have to be WS_file_name+'.xlsx') in backend.
After the process completed, it shows the button for users to download the file WS_file_name+'.xlsx'.
Users click the button to download the file WS_file_name+'.xlsx'.
I had tried everything to fix it but just in vain. Please help me.
Ok, so the problem is, that in your function download_file the variable WS_file_name is not defined.
As function download_file can not interact with function index (at least the way you set it up) you need to fill that variable WS_file_name with exactly the same filename you gave it when you stored the file in function index.
Then you should be fine!
I will give you an example with hardcoded filenames that should work:
from django.shortcuts import render
from django.http import HttpResponse
def index(request):
if request.method == 'POST':
student = StudentForm(request.POST, request.FILES)
if student.is_valid():
handle_uploaded_file(request.FILES['file'])
firstname= student.cleaned_data.get("firstname")
lastname= student.cleaned_data.get("lastname")
### Processing ###
WS_file_name = "hardcoded_filename" # HERE FILENAME
Toollist_Raw = pd.read_excel(Toollist_path+Toollist_file_name)
WS_file = xlsxwriter.Workbook(WS_file_name+'.xlsx')
WS_file.close()
file_source = WS_Path + WS_file_name+'.xlsx'
Toollist_Raw.to_excel(file_source, sheet_name='CALM_Toollist',index=False)
### Process had completed, users can click download button to download the file ###
context= {'username': firstname, 'version':lastname,}
return render(request, 'template_Download.html', context)
else:
student = StudentForm()
return render(request,"template_Form.html",{'form':student})
##### Download Functions #####
import os
from django.http import FileResponse
def download_file(request):
# Define Django project base directory
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Define file name
filename = "hardcoded_filename" +'.xlsx' # HERE SAME FILENAME
# Define the full file path
filepath = BASE_DIR + '/Capital_Report_Website/Download_Files/Baseline_Cleanup_Toollist_vs_CALM_Download/' + filename +'.xlsx'
return FileResponse(open(filepath, 'rb'), as_attachment=True)
But consider the following:
This requires you to store all those files on your host-machine. I feel like you should move the ### Processing ### section to the download_file function. Therefore you would not need to store the file on your machine and you can present it to the user "on the fly".
Edit:
As OP wants dynamic filenames - there you go...
models.py
class Student(models.Model):
firstname = models.CharField(max_lenght=30)
lastname = models.CharField(max_lenght=30)
path_to_xlsx = models.FilePathField(null=True, blank=True) # don't know if this works, or just put a default in.
views.py
def index(request):
[...]
if student.is_valid():
the_student = Student.objects.get(pk=<your-student>) # somehow grab your student here
[... file processing ...]
the_student.path_to_xlsx = os.path.join(path_to_your_file)
the_student.save()
the_student = student.save()
def download_file(request):
[...]
the_student = Student.objects.get(pk=<your-student>) # somehow grab your student here
return FileResponse(open(the_student.path_to_xlsx, 'rb'), as_attachment=True)
But still I do not consider this as good, as the excel files will pollute the filesystem and storage of your host machine
I have uploaded directory using django and JavaScript. Now I am trying to download this directory from django admin. For this purpose I have followed this link click here. When I have uploaded directory in django, the admin created the following table. File name indicates the uploaded folder name.
If I click on the file name, it shows me following error. I want to download this directory as a zip file from django admin. Can anyone please help me with this?
Here is my model.py:
class Prearchive(models.Model):
file = models.FileField(upload_to='media/', null=True)
file_name = models.CharField(max_length=500,primary_key=True)
file_id = models.ForeignKey(CreateScan, on_delete=models.CASCADE,default=1)
def _str_(self):
return self.file_name
admin.py:
from django.contrib import admin
from .models import CreateScan, Prearchive
# Register your models here.
class PrearchiveAdmin(admin.ModelAdmin):
model=Prearchive
list_display = ('file_name','file_id','file_link')
def file_link(self, obj):
if obj.file:
return "<a href='%s' download>Download</a>" % (obj.file.url,)
else:
return "No attachment"
file_link.allow_tags = True
file_link.short_description = 'File Download'
admin.site.register(Prearchive , PrearchiveAdmin)
view.py:
#login_required
def downloadScanView(request,url):
print('url',url)
response = HttpResponse(content_type='application/zip')
file_path = os.path.join(settings.MEDIA_ROOT, url)
zip_download = ZipFile(response, 'w',ZIP_DEFLATED)
if os.path.exists(file_path):
for root, dir, files in os.walk(file_path):
print('r = ',root)
for file in files:
zip_download.write(os.path.join(root,file))
response['Content-Disposition'] = 'attachment; filename={}'.format(os.path.basename(url)+'.zip')
return response
Create a view to download the file, and add urls.py entry for /media to this view.
views.py
def filedownload(request, filename):
zf = zipfile.ZipFile('download.zip', 'w', zipfile.ZIP_DEFLATED)
zf.write("media/" + filename)
response = HttpResponse(zf, content_type='application/force-download')
response['Content-Disposition'] = 'attachment; filename="download.zip"'
return response
Main urls.py
path('media/', include('your_app.urls')
App urls.py
path("<filename>/", views.filezip, name="filedownload")
I'm quite new to using Django and I am trying to develop a website where the user is able to upload a number of excel files, these files are then stored in a media folder Webproject/project/media.
def upload(request):
if request.POST:
form = FileForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return render_to_response('project/upload_successful.html')
else:
form = FileForm()
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('project/create.html', args)
The document is then displayed in a list along with any other document they have uploaded, which you can click into and it will displays basic info about them and the name of the excelfile they have uploaded. From here I want to be able to download the same excel file again using the link:
Download Document
My urls are
urlpatterns = [
url(r'^$', ListView.as_view(queryset=Post.objects.all().order_by("-date")[:25],
template_name="project/project.html")),
url(r'^(?P<pk>\d+)$', DetailView.as_view(model=Post, template_name="project/post.html")),
url(r'^upload/$', upload),
url(r'^download/(?P<path>.*)$', serve, {'document root': settings.MEDIA_ROOT}),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
but I get the error, serve() got an unexpected keyword argument 'document root'. can anyone explain how to fix this?
OR
Explain how I can get the uploaded files to to be selected and served using
def download(request):
file_name = #get the filename of desired excel file
path_to_file = #get the path of desired excel file
response = HttpResponse(mimetype='application/force-download')
response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(file_name)
response['X-Sendfile'] = smart_str(path_to_file)
return response
You missed underscore in argument document_root. But it's bad idea to use serve in production. Use something like this instead:
import os
from django.conf import settings
from django.http import HttpResponse, Http404
def download(request, path):
file_path = os.path.join(settings.MEDIA_ROOT, path)
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=' + os.path.basename(file_path)
return response
raise Http404
You can add "download" attribute inside your tag to download files.
<a href="/project/download" download> Download Document </a>
https://www.w3schools.com/tags/att_a_download.asp
Reference:
In view.py Implement function like,
def download(request, id):
obj = your_model_name.objects.get(id=id)
filename = obj.model_attribute_name.path
response = FileResponse(open(filename, 'rb'))
return response
When you upload a file using FileField, the file will have a URL that you can use to point to the file and use HTML download attribute to download that file you can simply do this.
models.py
The model.py looks like this
class CsvFile(models.Model):
csv_file = models.FileField(upload_to='documents')
views.py
#csv upload
class CsvUploadView(generic.CreateView):
model = CsvFile
fields = ['csv_file']
template_name = 'upload.html'
#csv download
class CsvDownloadView(generic.ListView):
model = CsvFile
fields = ['csv_file']
template_name = 'download.html'
Then in your templates.
#Upload template
upload.html
<div class="container">
<form action="#" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.media }}
{{ form.as_p }}
<button class="btn btn-primary btn-sm" type="submit">Upload</button>
</form>
#download template
download.html
{% for document in object_list %}
Download
{% endfor %}
I did not use forms, just rendered model but either way, FileField is there and it will work the same.
I've found Django's FileField to be really helpful for letting users upload and download files. The Django documentation has a section on managing files. You can store some information about the file in a table, along with a FileField that points to the file itself. Then you can list the available files by searching the table.
#Biswadp's solution worked greatly for me
In your static folder, make sure to have the desired files you would like the user to download
In your HTML template, your code should look like this :
Download
Using the below approach makes everything less secure since any user can access any user's file.
<a href="/project/download" download> Download Document </a>
Using the below approach makes no sense since Django only handles one requests at the time (unless you are using gunicorn or something else), and believe me, the below approach takes a lot of time to complete.
def download(request, path):
file_path = os.path.join(settings.MEDIA_ROOT, path)
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=' + os.path.basename(file_path)
return response
raise Http404
So what is the optimum solution?
Use Nginx authenticated routes. When requesting a file from Nginx you can make a request to a route and depending on the HTTP response Nginx allows to denies that request. This makes it very secure and also scalable and performant.
You can ready about more here
import mimetypes
from django.http import HttpResponse, Http404
mime_type, _ = mimetypes.guess_type(json_file_path)
if os.path.exists(json_file_path):
with open(json_file_path, 'r') as fh:
response = HttpResponse(fh, content_type=mime_type)
response['Content-Disposition'] = "attachment; filename=%s" % 'config.json'
return response
raise Http404
<a href='/your-download-view/' download>Download</a>
In your view:
from django.http import FileResponse
def download(request):
# pre-processing, authorizations, etc.
# ...
return FileResponse(open(path_to_file, 'rb'), as_attachment=True)
Simple using html like this downloads the file mentioned using static keyword
Download CV
1.settings.py:
MEDIA_DIR = os.path.join(BASE_DIR,'media')
#Media
MEDIA_ROOT = MEDIA_DIR
MEDIA_URL = '/media/'
2.urls.py:
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
3.in template:
<a href="{{ file.url }}" download>Download File.</a>
Work and test in django >=3
for more detail use this link:
https://youtu.be/MpDZ34mEJ5Y
If the file is a FileField in the model, this is the way I do it:
try:
download_file = PrintingFile.objects.get(pk=kwargs.get('pk_file', 0))
return FileResponse(download_file.file.open(), as_attachment=True)
except PrintingFile.DoesNotExist:
raise Http404
More here
I use this method:
{% if quote.myfile %}
<div class="">
<a role="button"
href="{{ quote.myfile.url }}"
download="{{ quote.myfile.url }}"
class="btn btn-light text-dark ml-0">
Download attachment
</a>
</div>
{% endif %}
If you hafe upload your file in media than:
media
example-input-file.txt
views.py
def download_csv(request):
file_path = os.path.join(settings.MEDIA_ROOT, 'example-input-file.txt')
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=' + os.path.basename(file_path)
return response
urls.py
path('download_csv/', views.download_csv, name='download_csv'),
download.html
a href="{% url 'download_csv' %}" download=""
I have a text file in the static folder in my project root.
I'd like to serve it so I've created:
#csrf_exempt
def display_text(request):
content =
return HttpResponse(content, content_type='text/plain; charset=utf8')
How do I set the path to the textfile, or how do I read it in to 'content', so that I can display it.
Have a look at this question that lets Apache handle the file delivery with mod_xsendfile.
If you insist on having Django itself delivering the file, you could do the following:
from django.http import StreamingHttpResponse
#csrf_exempt
def display_text(request):
content = open('/your/file', 'r').read()
response = StreamingHttpResponse(content)
response['Content-Type'] = 'text/plain; charset=utf8'
return response
Is it possible to show a PDF file in the Django view, rather than making the user have to download it to see it?
And if it is possible, how would it be done?
This is what I have so far -
#login_required
def resume(request, applicant_id):
#Get the applicant's resume
resume = File.objects.get(applicant=applicant_id)
fsock = open(resume.location, 'r')
response = HttpResponse(fsock, mimetype='application/pdf')
return response
Django has a class specifically for returning files, FileResponse. It streams files, so that you don't have to read the entire file into memory before returning it. Here you go:
from django.http import FileResponse, Http404
def pdf_view(request):
try:
return FileResponse(open('foobar.pdf', 'rb'), content_type='application/pdf')
except FileNotFoundError:
raise Http404()
If you have really large files or if you're doing this a lot, a better option would probably be to serve these files outside of Django using normal server configuration.
Simplistically, if you have a PDF file and you want to output it through a Django view, all you need to do is dump the file contents into the response and send it with the appropriate mimetype.
def pdf_view(request):
with open('/path/to/my/file.pdf', 'r') as pdf:
response = HttpResponse(pdf.read(), mimetype='application/pdf')
response['Content-Disposition'] = 'inline;filename=some_file.pdf'
return response
pdf.closed
You can probably just return the response directly without specifying Content-Disposition, but that better indicates your intention and also allows you specify the filename just in case the user decides to save it.
Also, note that the view above doesn't handle the scenario where the file cannot be opened or read for whatever reason. Since it's done with with, it won't raise any exceptions, but you still must return some sort of response. You could simply raise an Http404 or something, though.
PDF files must be opened as rb not r.
def pdf_view(request):
with open('/path / to /name.pdf', 'rb') as pdf:
response = HttpResponse(pdf.read(),content_type='application/pdf')
response['Content-Disposition'] = 'filename=some_file.pdf'
return response
Take out inline; if you want your file to be read from server. And also, the HttpResponse kwarg mimetype has been replaced by content_type:
(response['Content-Disposition'] = 'inline;filename=some_file.pdf')
def pdf_view(request):
with open('/app/../Test.pdf', 'r') as pdf:
response = HttpResponse(pdf.read(),content_type='application/pdf')
response['Content-Disposition'] = 'filename=some_file.pdf'
return response
pdf.closed
Following #radtek's answer above I decided to investigate a class-based view display. I tried to use View but it didn't have get_context_data() method.
I looked here for some guidance. I settled for BaseDetailView since I wanted to display just one object.
from django.http import FileResponse
from django.shortcuts import get_object_or_404
from django.views.generic.detail import BaseDetailView
class DisplayPdfView(BaseDetailView):
def get(self, request, *args, **kwargs):
objkey = self.kwargs.get('pk', None) #1
pdf = get_object_or_404(Pdf, pk=objkey) #2
fname = pdf.filename() #3
path = os.path.join(settings.MEDIA_ROOT, 'docs\\' + fname)#4
response = FileResponse(open(path, 'rb'), content_type="application/pdf")
response["Content-Disposition"] = "filename={}".format(fname)
return response
Commentary
1 This line accesses a named argument pk passed by the url calling the view.
2 This line gets the actual pdf model object.
3 I defined a method filename(self): return os.path.basename(self.file.name) in my model to help me get just the filename plus extension.
4 This line gets the complete filepath.
Then use file response as explained in the answers above. Also remember to use rb to read the pdf file
Here is a typical use-case for displaying a PDF using class-based views:
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
class DisplayPDFView(View):
def get_context_data(self, **kwargs): # Exec 1st
context = {}
# context logic here
return context
def get(self, request, *args, **kwargs):
context = self.get_context_data()
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'inline; filename="worksheet_pdf.pdf"' # Can use attachment or inline
# pdf generation logic here
# open an existing pdf or generate one using i.e. reportlab
return response
# Remove login_required if view open to public
display_pdf_view = login_required(DisplayPDFView.as_view())
For generating your own pdf with reportlab see the Django project Docs on PDF Generation.
Chris Pratt's response shows a good example of opening existing PDFs.
Browsers aren't PDF readers (unless they have the proper plugin/addon).
You may want to render the PDF as HTML instead, which can be done from the backend or the frontend.
it worked for me
import re, os
import os
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def export_auto_doc(request):
name = request.GET.get('name', "")
filename = "path/to/file"+name+".pdf"
try:
if not re.search("^[a-zA-Z0-9]+$",name):
raise ValueError("Filename wrong format")
elif not os.path.isfile(filename):
raise ValueError("Filename doesn't exist")
else:
with open(filename, 'r') as pdf:
response = HttpResponse(pdf.read(), content_type='application/pdf')
response['Content-Disposition'] = 'inline;filename='+name+'.pdf'
return response
pdf.closed
except ValueError as e:
HttpResponse(e.message)
The easiest way to do this is probably with an anchor in a template. For example, if you are using Django's templating engine (as most people who search for this probably are), simply serve it as a static file through an anchor.
In your template that will contain a link to the file, add at the very top
{% load static %}
Then, wherever you want to link to your pdf, put
Click me
The first line tells Django to look in the directories configured for static files in settings.py. The path that you use in the anchor tag is relative to any of the directories that you configured as static directories in settings.py. When you click the rendered link, it should display the PDF in your browser, provided you have your static files pathed correctly.
I am just throwing this out there.
You can simply add your PDF resume to your static files.
If you are using White Noise to serve your static files, then you don't even need to make the view. Just then access your resume at the static location.
I added mine, here it is: TIm-D_Nice.pdf
Warning: This doesn't solve the login_required requirement in the question
Use iframe url=url of pdf tag and give url of that pdf and make sure that your user will have full control of the project then pdf will be displayed on web screen
def pdf_view(request,pdfSlug):
a = Pdf.objects.get(pdf_slug=pdfSlug)
with open(str(a.pdf_file.path), 'rb') as pdf:
response = FileResponse(pdf.read(), content_type='application/pdf')
response['Content-Disposition'] = 'filename=a.pdf'
return response
pdf.closed
look this i'ts warked for me