i created a little web app that allows to upload and download files (pdf,txt,docx) to a sqlite3 database. I use flask as backend framework. When i hit the download button the download route gets called and everything seems fine but the download tab of the browser is not opening and nothing gets downloaed.
The print statement logs a valid filename to the terminal. Where did i go wrong ?
html snippet for upload and download :
{% block main %}
<div class="grid-item">
<h4>Datenbank</h4>
<table style="display: flex;justify-content: space-evenly;">
<tr >
<td>
download
</td>
<td>
Datei
</td>
<td>
löschen
</td>
</tr>
{% for file in current_user.files %}
<tr>
<td>
<button type="button" class="close" onClick="download_files({{ file.id }})">
<i class="bi bi-box-arrow-down" style="color: black;"></i>
</button>
</td>
<td>
<li class="list-group-item">
{{ file.filename }}
</li>
</td>
<td >
<button type="button" class="close" onClick="delete_files({{ file.id }})" >
<span aria-hidden="true" style="color:black">×</span>
</button>
</td>
</tr>
{% endfor %}
</table>
</div>
{% endblock %}
{% block scripts %}
<script type="text/javascript" src="{{ url_for('static', filename='index.js') }}"></script>
{% endblock%}
javascript used:
function download_files(fileid) {
fetch("/download", {
method: "POST",
body: JSON.stringify({ fileid: fileid }),
})
}
flask code:
#files.route("/download" , methods=["POST"])
#login_required
def download():
file = json.loads(request.data)
fileID = file["fileid"]
file = Upload.query.filter_by(id=fileID).first()
if file:
if file.user_id == current_user.id:
print(f"file {file.filename} downloaded !" )
return send_file(BytesIO(file.data), download_name=file.filename,as_attachment=True )
EDITE 1 :
Upload code
html:
<div class="grid-item">
<h4>Datei hochladen</h4>
<h5>Unterstütze Dateiformate: .doc .docx .pdf .txt .odt</h5>
<form action="{{ url_for('files.upload') }}" id="dropzone" class="dropzone" multiple accept=".doc,.docx,.pdf,.txt,.odt">
</form>
</div>
python:
#files.route("/upload", methods=["GET", "POST"])
#login_required
def upload():
if request.method == 'POST':
allowed_files = [".doc",".docx",".pdf",".txt",".odt"]
file = request.files['file']
file_ext = os.path.splitext(file.filename)[1]
if file_ext not in allowed_files:
flash(f"Dateiformat {file_ext} nicht unterstütt! ",category="error")
return render_template("drive.html")
elif not file.filename == '':
upload = Upload(filename=secure_filename(file.filename), data=file.read(),date=datetime.datetime.now(),user_id=current_user.id)
db.session.add(upload)
db.session.commit()
flash(f"Dateien hochgeladen ! ",category="sucess")
return redirect(url_for("views.drive"))
else:
flash(f"Ungültiger Dateiname Dateiname: {file.filename} ! ",category="error")
return render_template("drive.html")
else:
return render_template("drive.html")
I did check that the correct file gets submited to the download function. Also that the file exists and that the funcion runs till the return statement.
I checked the flask send_file documentation for mistakes of the send_file() function and checked the dev tools of chrome to see if there is a payload.
EDIT 2:
I did try it with the following code which works. Maybe someone can explain the difference to me. From my consol logs my code does exactly the same, just gets the id through javascript fetch() and not by a flask route credential.
#app.route('/download/<upload_id>')
def download(upload_id):
upload = Upload.query.filter_by(id=upload_id).first()
return send_file(BytesIO(upload.data), download_name=upload.filename, as_attachment=True)
The reason why i dont use this is, because i dont know how to send the file id to the flask route without a javascript part. Maybe someone can help me on that part.
Related
I'm developing a quick url shortner which also returns a svg version of a QR code for the same shortened URL. I'm using flask and python
I've created a specific route in flask to serve the file and return the file for download with send_file, the file gets created in the root directory (I'll need to fix this later eventually, but that's a different problem) and is in there, but when I click the link in the front, flask defaults to the 404.
This is the download route:
#short.route('/<qr_file>')
def download(qr_file):
return send_file(qr_file, as_attachment=True)
and here's the add_link route (creates the link, commits to db, creates the qrcode, stores it, returns short and original url to the template, along with the qr_file location for the link to download)
#short.route('/add_link', methods=['POST'])
def add_link():
original_url = request.form['original_url']
link = Link(original_url=original_url)
db.session.add(link)
db.session.commit()
qr_url = pyqrcode.create(link.short_url)
qr_file = link.short_url + '.svg'
qr_url.svg(qr_file, scale = 8)
return render_template('link_added.html',
new_link=link.short_url, original_url=link.original_url, qr_file=qr_file)
Here's the relevant in the link_added.html:
<div class="field">
<label class="label">New URL</label>
<div class="control">
<input class="input" type="text" name="original_url" value="{{ url_for('short.redirect_url_to_url', short_url=new_link, _external=True) }}">
</div>
</div>
<div class="field">
<label class="label">QR Code</label>
{{ qr_file }} <br>
<img src="{{ qr_file }}" height="90" width="106" /><br>
</div>
I have files which are saved to the MEDIA_ROOT - and I am displaying the file paths as URLs in a table in my UI. I would like for these files to download when the user clicks the link in the table. However, when that happens I get an error because I don't have a URL or View defined to handle this I suppose. Problem is, I'm not really sure where to start - any suggestions. Below is my model, and the .html which displays the table and the link.
models.py
class Orders(models.Model):
...
order_file = models.FileField(upload_to='web_unit', null=True, blank=True)
...
def __str__(self):
return self.reference
index.html
<div class="table-responsive">
<table id="main_table" class="table table-striped table-bordered" cellspacing="0" style="width="100%">
<thead>
<tr>
....
</thead>
<tbody>
{% for orders in orders %}
<tr>
<td>
<!-- Update book buttons -->
<button type="button" class="update-book btn btn-sm btn-primary" style="color: #FFCF8B; border-color: #FFCF8B; background-color: #FFF;" data-id="{% url 'order_update' orders.pk %}">
<span class="fa fa-pencil"></span>
</button>
</td>
....
<td>Download</td> #this is the link
</tr>
{% endfor %}
</tbody>
</table>
When the link in the table is clicked - I'd like for the file to be downloaded - I need help on how to define the URL and the View to make this happen.
This has been marked as a duplicate a few times now - but I don't believe it is. The link that I have been referred to only shows a view. I don't understand how I am to trigger that view using a url since there will be many download links in the same screen. How does the view know which file link I have clicked on? Wouldn't that need to leverage the URL somehow?
First, don't do {% for orders in orders %}; instead do {% for order in orders %}
Then this should work (assuming order_file is the field name you didn't show in the model)
<td>Download</td>
I have a 4 buttons on first page which put me through to another page(All those 'buttons' are connected to database in mysql) and on this second page I have some data from mysql tables which I wanna display depending on what I have chosen in the first page. Right now I just display everything I have and I don't really know how to change that. I was looking for solutions but none worked.
views.py
def kategorie_list(request):
obj = Kategorie.objects.all()
context ={'obj': obj}
return render(request, "kategorie/lista.html", context)
def uslugodawcy_list(request):
obj = Uslugodawcy.objects.all()
context ={'obj': obj}
return render(request, "uslugodawcy/uslugodawcy_lista.html", context)
first html page
{% for Kategorie in obj %}
<p> <a class="btn btn-primary" href="/uslugodawcy"><button type="nw" style="height: 65px; width: 170px"> <font size="4">{{Kategorie.idkategorie}} . {{Kategorie.nazwakategorii}}</font> </button> </a> </p>
{% endfor %}
second
{% for Uslugodawcy in obj %}
<p> <a class="btn btn-primary" href="/promocje"><button type="nw" style="height: 65px; width: 170px"> <font size="4">{{Uslugodawcy.iduslugodawcy}} . {{Uslugodawcy.nazwa_uslugodawcy}} </font> </button> </a> </p>
{% endfor %}
There are many approaches. In outline:
Style a link as a button. . If the href is http://djangoserver/url?foo=bar then request.GET['foo'] will be available and equal to "bar".
Make all the buttons relate to a form that you are displaying and POSTing, so all of them do a SUBMIT but have diffferent values that you can find in request.POST. <button form="form-id" type="submit" ...>
Use Javascript to cause clicking the button to fill in fields on the form (which may be hidden fields, so the user has no other way of changing them.
I am using python and flask to make a web app. I am new to it, but have gotten most of what I am trying to accomplish done. Where I am stuck, is that I have a label whose value is a python variable( {{id}} ) This id is the id of a row I need to update in a sqlite database. My code is below. when I click the approve button, it takes me to a route which does the update query, but I have no way to pass the {{id}} with it. This would have been much easier if I could have just used javascript for the update query, but everything I've found using javascript, is for web sql, not sqlite, even though some of them say they are for sqlite.
</script>
<table border='1' align="center">
{% for post in posts %}
<tr>
<td>
<label>{{post.id}}</label>
<h1 id ='grill1'>{{post.photoName}}</h1>
<span>
<img id = 'photo1' src='{{post.photo}}' alt="Smiley face" height="200" width="200">
</span><br>
<h5 id ='blurb1'>
{{post.blurb}}
</h5>
<br>
<div style=" padding:10px; text-align:center;">
<input type="button" value="Approve" name="approve" onclick="window.location='/approve;">
<input type="button" value="Deny" onclick="window.location='/deny';"><br>
</div>
</td>
</tr>
{% endfor %}
</table>
Why not just do:
...
<input type="button" value="Approve" name="approve" onclick="window.location='/approve/{{post.id}};">
<input type="button" value="Deny" onclick="window.location='/deny/{{post.id}}';">
...
Then your flask route for approve and / or deny can just take a parameter for the post to approve or deny. i.e.:
#app.route("/approve/<int:post_id>")
def approve(post_id):
"""approve this post!"""
I am new to Python, GAE and the datastore model. So there are lots of things which I do not know yet, so please be patient :)
I am working on a web service that allows people to post 'name' and 'desc' (description) of an item and it will be included in a table on the same page. However when I clicked the submit button I got the error: 404 Not Found, The resource could not be found.
I am expecting a lot of things to be wrong in my code shown below (I only include short snippets of my code which I think is relevant to make it easier for reading), and my biggest problem is I have no idea which parts are wrong or which specific questions to ask. But I hope I can use this chance to learn more about everything that's involved in my code (Jinja, HTML, GQL etc), and how I can fit them all together.
class Events(ndb.Model):
name = ndb.StringProperty()
desc = ndb.StringProperty()
class Promote(webapp2.RequestHandler):
def get(self):
query = ndb.gql("SELECT * "
"FROM Events "
)
template_values = {"events" : query,}
template = jinja_environment.get_template('promote.htm')
self.response.out.write(template.render(template_values))
def post(self):
event = Events(name = self.request.get('name'), desc = self.request.get('desc'))
event.put()
self.redirect('/promote')
app = webapp2.WSGIApplication([('/', Main),
('/publicsearch', PublicSearch),
('/promote', Promote)],
debug=True)
This is my html code for that page.
<div class="jumbotron">
<div class = "container">
<form action="/promote" method="post">
<fieldset>
<div class="row-fluid">
<p> Promote your event here! </p>
<div class="row-fluid">
<div class="span6">
<p> Name of event: <br>
<textarea class="input-block-level" name="name" rows="1" cols = "50"> </textarea></p>
<p> Event description: <br>
<textarea class="input-block-level" name="desc" rows="3" cols = "50"> </textarea></p>
<p><input type="submit" value="Submit">
</div>
</div>
</div>
</div>
</div>
<h4> Events feed </h4>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th width="30%">Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
{% for event in events %}
<tr>
<td>{{ event.name }} </td>
<td>{{ event.desc }} </td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
Your form is trying to post to a handler with a url of /wishlist however the only handler for POST methods you have registered is for /promote.
These things need to match up. Either change the form or the handler mapping.
Also while you are at it check that your app.yaml makes sense. Have a look in the logs whilst you are at it, you will see what URL is being requested.