Return json file in a view - django

In a view I create new file with:
sys.stdout = open(backup_name, 'w')
call_command('dumpdata')
How can I now return this file to user?
I tried to change mimetype in HttpResponse to 'application/json' but how can I add file content to response?
Or maybe there is other way to return file?
.

I know it's a bit late, but I found this a useful starting point so I thought others could benefit from what I found too.
For a small file, if you place the json file in a template folder, django can find it and you can return it with render_to_response:
return render_to_response(data_file,mimetype='application/json')
I found this to be problematic for large datasets on certain browsers. I would get the error An existing connection was forcibly closed by the remote host. An alternative approach fixed this.
First you must create full path to your file. Use the PROJECT_ROOT variable (defined by PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__)) in settings.py). To access this and the os methods you must import settings, os in views.py. Once you have this file location you can return it using the code below:
backup_path = os.path.join(settings.PROJECT_ROOT, "templates", "json_dumps", "large_file.json")
return HttpResponse(open(backup_path, 'r'),content_type = 'application/json; charset=utf8')
I found this worked well for even very large files.

OK I have it:
response = HttpResponse(open(backup_path, "r"), mimetype='application/json', )
response['Content-Disposition'] = "filename=%s" % backup_name"
After saving file on disc I open it for reading and set file name in response.
Anyone has another idea?

I was trying to return a dictionary as a json file. Here is my solution:
import json
import cStringIO as StringIO
from wsgiref.util import FileWrapper
from django.http import HttpResponse
data_string = json.dumps(data)
json_file = StringIO.StringIO()
json_file.write(data_string)
json_file.seek(0)
wrapper = FileWrapper(json_file)
response = HttpResponse(wrapper, content_type='application/json')
response['Content-Disposition'] = 'attachement; filename=dump.json'
return response

Just copy/link/call the dumpdata code related to model serialization, and dump it directly into the response, so you avoid permission problems and filesystem pollution. Content-disposition and mimetype still applies.
Remember anyway that dumpdata can be a lenghty process, so you are exposed to timeouts.

My final solution is (thanks to saverio):
response = HttpResponse(mimetype='application/json', )
response['Content-Disposition'] = "filename=%s" % backup_name
sys.stdout = response
call_command('dumpdata')

Related

how to prompt user to save a pdf file to his local machine in django?

I am fairly new to Django and my project requires me to prompt user to open a pdf upon clicking a link. I already have the pdf file on my local machine and dont want to recreate it using Reportlab. Is there any way to do it?
I tried
with open("/user/some/directory/somefilename.pdf") as pdf:
response = HttpResponse(pdf, content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"'
return response
but it returned 404 page not found as the requested url wasn't in the URLconf of myproject.urls
What am I missing?
In general, when user click "Download", you can:
- If file is not existed:
- Generate pdf file use ReportLab as you did.
- Store generated file to a public dir.
return HttpResponseRedirect(file_url_to_public_dir)
The way that worked for me is by using FileSystemStorage
from django.core.files.storage import FileSystemStorage
from django.http import HttpResponse
fs = FileSystemStorage("/Users/location/where/file/is_saved/")
with fs.open("somefile.pdf") as pdf:
response = HttpResponse(pdf, content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="my_pdf.pdf"'
return response
and now its prompting the user to save the file as it normally would!

django pandas dataframe download as excel file

I have a Django app that will be placed in a Docker container.
The app prepares data in Dataframe format. I would like to allow the user to download the data to his/her local drive as excel file.
I have used df.to_excel in the past, but this won't work in this case.
Please advise best way to do this.
As of pandas-0.17, you can let Django write to a BytesIO directly, like:
from django.http import HttpResponse
from io import BytesIO
def some_view(request):
with BytesIO() as b:
# Use the StringIO object as the filehandle.
writer = pd.ExcelWriter(b, engine='xlsxwriter')
df.to_excel(writer, sheet_name='Sheet1')
writer.save()
# Set up the Http response.
filename = 'django_simple.xlsx'
response = HttpResponse(
b.getvalue(),
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
)
response['Content-Disposition'] = 'attachment; filename=%s' % filename
return response
You might need to install an Excel writer module (like xlsxwriter, or openpyxl).
I think it can be even simpler and more concise these days. You can just pass the http response directly to the Excel writer. The following works for me:
from django.http import HttpResponse
import pandas as pd
# df = CREATE YOUR OWN DATAFRAME
response = HttpResponse(content_type='application/xlsx')
response['Content-Disposition'] = f'attachment; filename="FILENAME.xlsx"'
with pd.ExcelWriter(response) as writer:
df.to_excel(writer, sheet_name='SHEET NAME')
return response

Error sending a file using Django - file turns out empty

This is my views.py files:
from django.http import HttpResponse
def render(request):
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"'
response['X-Sendfile'] = '/files/filename.pdf'
# path relative to views.py
return response
When I run the server and request
http://localhost:8080/somestring
I get an empty file called somefilename.pdf. I suspect that there is some crucial part missing in render.
The other parts of this app outside of views.py are correct to my understanding.
Here is the code that solved my problem:
from django.http import HttpResponse
from wsgiref.util import FileWrapper
def render(request):
response = HttpResponse(FileWrapper(open('file.pdf', 'rb')), content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"'
return response
The manage.py runserver development serer doesn't support X-Sendfile. In production, you need to enable X-Sendfile for your server (e.g. Apache).
You may find the django-sendfile package useful. It has a backend that you can use in development. However, it hasn't had a release in some time, and I found that I had to apply pull request 62 to get Python 3 support.

Django with mod_XSENDFILE unable to download complete file

Attached is the code which downloads a file from browser using django 1.3 and Apache 2.2 with mod_xsendfile
#login_required
def sendfile(request, productid):
path = settings.RESOURCES_DIR
filepath = os.path.join('C:/workspace/y/src/y/media/audio/','sleep_away.mp3')
print "filepath",filepath
filename = 'sleep_away.mp3' # Select your file here.
print "Within sendfile size", os.path.getsize(filepath)
wrapper = FileWrapper(open(filepath,'r'))
content_type = mimetypes.guess_type(filename)[0]
response = HttpResponse(wrapper, content_type = content_type)
print "Within wrapper"
from django.utils.encoding import smart_str
response['X-Sendfile'] = smart_str(filepath)
response['Content-Length'] = os.path.getsize(filepath)
from django.utils.encoding import smart_str
response['Content-Disposition'] = 'attachment; filename=%s/' % smart_str(filename)
return response
The console shows the following filesize which is the right size
Within sendfile size 4842585
But when I download/save the file it shows 107 KB...i.e 109,787 bytes.Where am I going wrong. Why isnt it downloading the complete file?
I consider your new to django or python. Try to put the import statements at the beginning of the method. Once imported it can be used through the method no need import every time you use. In windows you should use "rb" (read binary) to serve anything other than text files. Try not to use variable names that might conflict with method names or other keywords of the language. Your method should be like this
#login_required
def sendfile(request, productid):
from django.utils.encoding import smart_str
##set path and filename
resource_path = settings.RESOURCES_DIR # resource dir ie /workspace/y/src/y/media
filename = "sleep_away.mp3" #file to be served
##add it to os.path
filepath = os.path.join(resource_path,"audio",filename)
print "complete file path: ", filepath
##filewrapper to server in size of 8kb each until whole file is served
file_wrapper = FileWrapper(file(filepath,'rb')) ##windows needs rb (read binary) for non text files
##get file mimetype
file_mimetype = mimetypes.guess_type(filepath)
##create response with file_mimetype and file_wrapper
response = HttpResponse(content_type=file_mimetype, file_wrapper)
##set X-sendfile header with filepath
response['X-Sendfile'] = filepath ##no need for smart_str here.
##get filesize
print "sendfile size", os.stat(filepath).st_size
response['Content-Length'] = os.stat(filepath).st_size ##set content length
response['Content-Disposition'] = 'attachment; filename=%s/' % smart_str(filename) ##set disposition
return response ## all done, hurray!! return response :)
Hope that helps
You could have a look at the django-private-files project. Haven't tested it myself, but it looks promissing.
link to the docs --> http://readthedocs.org/docs/django-private-files/en/latest/usage.html
cheers

How to return static files passing through a view in django?

I need to return css files and js files according to specific logic. Clearly, static serve does not perform what I need. I have a view, whose render method uses logic to find the proper file, but then I have to return it. Technically, I can just read the file and stuff it into a HttpResponse object with the proper mime type, but I was wondering if there was a better strategy. (like fpassthru() in php)
This is what I used:
test_file = open('/home/poop/serve/test.pdf', 'rb')
response = HttpResponse(content=test_file)
response['Content-Type'] = 'application/pdf'
response['Content-Disposition'] = 'attachment; filename="%s.pdf"' \
% 'whatever'
return response
What webserver software are you using?
At least for Apache and NginX, there is a module enabling you to use the X-SendFile HTTP header. The NginX website says Lighty can do this, too.
In your wrapper view:
...
abspath = '/most_secret_directory_on_the_whole_filesystem/protected_filename.css'
response = HttpResponse()
response['X-Sendfile'] = abspath
response['Content-Type'] = 'mimetype/submimetype'
# or let your webserver auto-inject such a header field
# after auto-recognition of mimetype based on filename extension
response['Content-Length'] = <filesize>
# can probably be left out if you don't want to hassle with getting it off disk.
# oh, and:
# if the file is stored via a models.FileField, you just need myfilefield.size
response['Content-Disposition'] = 'attachment; filename=%s.css' \
% 'whatever_public_filename_you_need_it_to_be'
return response
Then you can connect the view via http://mysite.com/url_path/to/serve_hidden_css_file/.
You can use it anytime you need to do something upon a file being requested that should not be directly accessible to users, like limiting who can access it, or counting requests to it for stats, or whatever.
For Apache: http://tn123.ath.cx/mod_xsendfile/
For NginX: http://wiki.nginx.org/NginxXSendfile
Why don't you use Django staticfiles inside your view
from django.contrib.staticfiles.views import serve
...
def view_function(request):
return serve(request, 'absolute_path_to_file_name')
Why not return an HttpResponseRedirect to the location of the correct static file?
Serving files directly from a view is very slow. If you are looking for normal file serving see this question: Having Django serve downloadable files
To very easily serve files through a view (for debug purposes, for example) keep reading.
# In your urls.py:
url(r'^test-files/(?P<name>.+)/$', views.test_files, name='test_files'),
# In your views.py:
from django.http.response import HttpResponse
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt # (Allows file download with POST requests, can be omitted)
def test_files(request, name):
if name == "myxml":
fsock = open("/djangopath/data/static/files/my.xml", "rb")
return HttpResponse(fsock)
This allows you to download the file from: http://127.0.0.1:8080/app/test-files/myxml/
Pass an iterator (such as the result of open()) to the HttpResponse constructor.
you can use below code in your view:
Note:in this function I return images but you can return every thing based your need and set your context_type
from django.http import HttpResponse,Http404
import os
def img_finder(request, img_name):
try:
with open(os.path.dirname(os.path.abspath(__file__)) + '/static/img/' + img_name, 'rb') as f:
return HttpResponse(f.read(), content_type="image/jpeg")
except IOError:
raise Http404
Here the most simple and efficient way to do this.
app/urls.py
from django.urls import re_path
from app import views
urlpatterns = [
re_path(r'^(?P<public_url>.*)$', views.public, name="public"),
]
Warning : put the URL pattern at the end
app/views.py
import os
from django.conf import settings
from django.views.static import serve
def public(request, public_url):
public_folder = os.path.join(str(settings.BASE_DIR), 'folder_path')
return serve(request, public_url, document_root=public_folder)
It should be wasteful to use django to serve static content (not to mention, several orders of magnitude slower).
I'd rather convert the view into a context processor and use the variables in templates to find what blocks to include.