In my django web app, I create a .csv file from a list in my views.py and I want the user to be able to download it when the button is pressed. However, when this is run, I get the error that 'headers' is an unexpected argument.
This is the code in my views.py:
output = ['a', 'b', 'c']
response = HttpResponse(content_type='text/csv', headers={'Content-Disposition': 'attachment; filename="myfile.csv"'})
writer = csv.writer(response)
writer.writerows(output)
return response
I have no idea why this doesn't work, since I have used identical code elsewhere with success in the past. Does anyone know if this could be a browser support issue?
Another way to set your header for HttpResponse is like this:
response['Content-Disposition'] = 'attachment; filename="myfile.csv"'
For the error you are getting, can you double check that your django version is indeed 3.2?
I was able to make it work (but not really). I pass the output to the same webpage in a template in JSON format. Then I download the contents of that template using JavaScript. It works, but it feels janky and there are two fairly significant drawbacks to this approach:
All of the form fields on that webpage are reset
Commas in strings become a new cell in the .csv file, shifting everything after to the right.
views.py
output = json.dumps(output)
form = MyForm(initial={'data':my_data})
return render(request, 'wireCalculator/chassis.html', {'base':base, 'download':output, 'form':form})
html page addition:
<div style="display: none;">
<p id="download">{{ download }}</p>
</div>
Javascript addition:
function download(filename, text) {
// creates a link, downloads it, then deletes the link
var element = document.createElement('a')
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text))
element.setAttribute('download', filename)
element.style.display = 'none'
document.body.appendChild(element)
element.click()
document.body.removeChild(element)
}
const download = JSON.parse(document.getElementById("download").innerHTML)
if (download != "") {
var downloadText = ""
for (i=0; i < download.length; i++){
downloadText = downloadText.concat(download[i])
downloadText = downloadText.concat("\n")
}
download("filename.csv", downloadText)
}
Related
Summary
I am trying to set my FormData properly using javascript.
I need to be able to upload jpg/png, but I might need to upload some other file types pdf/csv in the future using fetch.
Expected
I expect it to append the data to the form
Error
Working
This snippet is working fine:
const formData = new FormData(document.querySelector('form'));
formData.append("extraField", "This is some extra data, testing");
return fetch('http://localhost:8080/api/upload/multi', {
method: 'POST',
body: formData,
});
Not working
const formData = new FormData();
const input = document.querySelector('input[type="file"]');
formData.append('files', input.files);
Question
Does fetch support multiple file upload natively?
If you want multiples file, you can use this
var input = document.querySelector('input[type="file"]')
var data = new FormData()
for (const file of input.files) {
data.append('files',file,file.name)
}
fetch('http://localhost:8080/api/upload/multi', {
method: 'POST',
body: data
})
The issue with your code is in the lineformData.append('files', input.files);
Instead of that, you should upload each file running a loop with unique keys, like this
const fileList = document.querySelector('input[type="file"]').files;
for(var i=0;i<fileList.length;i++) {
formData.append('file'+i, fileList.item(i));
}
I have created a simple error fiddle here with your code. You can check its' submitted post data here, where you can see that no file has been uploaded.
At the bottom of the page you can find
.
I have corrected the fiddle here with the fix. You can check its'post data from the server, where it shows the details of the two files that I uploaded.
I mentioned this on a similar question: I had the same problem, but with a PHP backend. The unique formData keys work, but I found that the classic HTML notation worked just fine and simply results in an array on the server.
formData.append('file[]', data[i]);
I like that a lot better, since I can use the same methods to process this as with a classic <input type="file" multiple />.
I have a Python flask application which takes input id's and dynamically generates data into a html file. Below is my app.py file.
#app.route('/execute', methods=['GET', 'POST'])
def execute():
if request.method == 'POST':
id = request.form['item_ids']
list = [id]
script_output = subprocess.Popen(["python", "Search_Script.py"] + list)
# script_output = subprocess.call("python Search_Script.py "+id, shell=True)
# render_template('running.html')
script_output.communicate()
#driver = webdriver.Chrome()
#driver.get("home.html")
#driver.execute_script("document.getElementById('Executed').style.display = '';")
return render_template('execute.html')
#app.route('/output')
def output():
return render_template('output.html')
output.html file has below code at the bottom.
<div class="container" style="text-align: center;">
{% include 'itemSearchDetails.html' %}
</div>
itemSearchDetails.html is generated every time dynamically based on the input. I check for different inputs and it is generating perfectly. When I run it with some input(assume 2) values for the first time, it runs perfectly and shows the output correctly. But, when I run for different values(assume 4) for the next time, the file 'itemSearchDetails.html' is generated for those 4 values but the browser only shows output for the first 2 values. No matter how many times I run it, browser shows only output with the first run values.
So, every time only the first inputted values are shown no matter how many times I run. I am not sure if it is browser cache issue since I tried "disabling cache" in chrome. Still it didn't work. Please let me know if there is something I am missing.
Try solution from this answer:
Parameter TEMPLATES_AUTO_RELOAD
Whether to check for modifications of the template source and reload
it automatically. By default the value is None which means that Flask
checks original file only in debug mode.
Original documentation could be found here.
Looks like Jinja is caching the included template.
If you don't need to interpret the HTML as a Jinja template, but instead just include its contents as-is, read the file first and pass the contents into the template:
with open('itemSearchDetails.html', 'r') as infp:
data = infp.read()
return render_template('execute.html', data=data)
...
{{ data|safe }}
(If you do need to interpret the HTML page as Jinja (as include will), you can parse a Jinja Template out of data, then use the include tag with that dynamically compiled template.)
I have tried to solve this problem for a week now and couldn't find a suitable solution.
I have a search bar to input gene ids (alternatively upload afile). It then queries the database and returns interactions between these genes. We display those interactions in a matrix on the html page. (I basically return a python array).
Now I want to include an export button to download the matrix / table in a csv or text file so the user can manipulate it for further research.
How can I do this without saving every query result in a file?
Because after returning the matrix, the python script (views.py) has run and the gene ids are gone.
Can it be done with jQuery?
Thank you so much for your help.
You only need to provide a link that contains current url plus a flag to indicate that it's a download csv request not a render page request:
def return_result(request):
# your original query
query = request.GET.get('id', None)
if query:
results = Model.objects.filter(field=query)
response = request.GET.get('download', None)
if response == 'csv':
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
# create a csv
return response
else:
context = {'results': results}
return render(request, 'results.html', context)
In your page you create a link to download csv, {{ request.get_full_path }} is to get current url from request:
Download as CSV
I've been reading the book 'Head First Python' where the writer talks about creating dynamic webpages using a module he created called 'yate', an HTML template engine (which I renamed to site_yate in the code below). The example he works through is a hypothetical coach wanting his athletes to be able to check their times online. The design is as follows: first you enter the homepage which has a link to run a script which generates a webpage where you can select the athlete whose times you want to view. Then when you select your athlete and click submit the form calls another script called "site_generate_timing_data.py" where you can views the athlete's top times. So I decided to take it further and add functionality to add a time for the athlete, using this extra line of code in my python script.
print(site_yate.do_form("addtime.py", [athlete_id]))
The HTML this will generate will be this:
<form action="addtime.py" method="POST">
<h1>Want to add a time?</h1>
<input type="Text" name="1" size=40> //"1" is the athlete's id in this example
<input type="Submit" value="Submit">
</form>
As you can see this code calls the script 'addtime.py' which has the following code:
import cgi
import sqlite3
data = cgi.FieldStorage().value[0] #this attribute will be in the form MininFieldStorage(name, value)
id = data.name #this attribute is the input's name i.e. athlete's id
time = data.value #this attribute is the input's value i.e. the time
connection = sqlite3.connect("NUACDB.sqlite") #my DB's name
cursor = connection.cursor()
cursor.execute("""INSERT INTO timing_data (athlete_id, time)
VALUES (?, ?)""",
(id, time)) #just SQL stuff
connection.commit()
connection.close()
Which works fine, however I want to change a few thing about this, since it leaves the user on a blank page. I could generate some HTML code to provide links to the homepage etc. or even JavaScript code to redirect the user automatically, but I want to keep this script HTML-free so that I can also use it elsewhere.
What I want to do instead is make the script execute on the same page. Not only that, but I would also prefer if I could put the addtime.py code as a function in another module called 'athletemodel.py' and call it form there, i.e. athletemodel.addtime() (or I could do from athletemodel import addtime so I can call the function directly). How can I call a python function using HTML code? I'm aware of the onsubmit="" form attribute but apparently that is for JavaScript functions. Another thing I'm unsure about is whether the data submitted in the form will still be accessible through CGI FieldStorage and hence whether my addtime.py code will still work as it is.
This stuff is so confusing! All help is appreciated.
Not sure if you already had it in mind, but I would use ajax (remember to include the jQuery library). Here's a rough example to get you started if this is what you want. It'll keep them on the same page:
JavaScript file:
$('#submitButtonId').click(function (event) {
event.preventDefault();
$('#submitButtonId').hide();
$('#thinking').show(); //some div with a nice ajax loader gif...
$.ajax({
type: 'POST',
data: $('#formId').serialize(),
url: '/URL_path_to_function',
success: function (data) {
$('#loading').hide();
var response = data.result //now do stuff with your response
}
error: function(error){
console.log('Error')}
});
Python view/function:
import jsonify
if request.method == 'POST':
value = request.form['input value'] #flask...
#Do stuff
return jsonify(result='Some response')
So, I'm trying to make a simple call using jQuery .getJSON to my local web server using python/django to serve up its requests. The address being used is:
http://localhost:8000/api/0.1/tonight-mobile.json?callback=jsonp1290277462296
I'm trying to write a simple web view that can access this url and return a JSON packet as the result (worried about actual element values/layout later).
Here's my simple attempt at just alerting/returning the data:
$.getJSON("http://localhost:8000/api/0.1/tonight-mobile.json&callback=?",
function(json){
alert(json);
<!--$.each(json.items, function(i,item){
});-->
});
I am able to access this URL directly, either at http://localhost:8000/api/0.1/tonight-mobile.json or http://localhost:8000/api/0.1/tonight-mobile.json&callback=jsonp1290277462296 and get back a valid JSON packet... So I'm assuming it's in my noob javascript:)
My views.py function that is generating this response looks as follows:
def tonight_mobile(request):
callback = request.GET.get('callback=?', '')
def with_rank(rank, place):
return (rank > 0)
place_data = dict(
Places = [make_mobile_place_dict(request, p) for p in Place.objects.all()]
)
xml_bytes = json.dumps(place_data)
xml_bytes = callback + '(' + xml_bytes + ');'
return HttpResponse(xml_bytes, mimetype="application/json")
With corresponding urls.py configuration:
(r'^tonight-mobile.json','iphone_api.views.tonight_mobile'),
I am still somewhat confused on how to use callbacks, so maybe that is where my issue lies. Note I am able to call directly a 'blah.json' file that is giving me a response, but not through a wired URL. Could someone assist me with some direction?
First, callback = request.GET.get('callback=?', '') won't get you the value of callback.
callback = request.GET.get( 'callback', None )
Works much better.
To debug this kind of thing. You might want to include print statements in your Django view function so you can see what's going on. For example: print repr(request.GET) is a helpful thing to put in a view function so that you can see the GET dictionary.