Wring file retrieved using Blobstore - python

I have form upload and handler which allows download uploaded files from blobstore.
The problem is when I click Download button of any related-field it downloads the same file every time. I.e. I've uploaded 3 files (1.txt, 2.txt, 3.txt) and it always downloads only 1.txt whenever I clicked another Download buttons. You can see it at http://my77notes.appspot.com/show (or http://my77notes.appspot.com/upload first for uploading your own files).
When I've researched source code it shows me different keys for every hidden fields..
What did I wrong?
Here is my files:
template file:
<h2>Files uploaded to Blobstore</h2>
<table border="3">
<tr>
<td>#</td>
<td>Filename</td>
<td>Content-Type</td>
<td>Creation</td>
<td>Size</td>
<td>Download</td>
</tr>
<form id="show_blob" name="show_blob" method="post" action="{{ download_blob }}">
{% for file in blob_files %}
<tr>
<td>{{ loop.index }}</td>
<td>{{ file.filename }}</td>
<td>{{ file.content_type }}</td>
<td>{{ file.creation }}</td>
<td>{{ file.size }}</td>
<td>
<input type="submit" name="download" value="Download"/>
<input type="hidden" name="blobkey" value="{{ file.key() }}" />
</td>
</tr>
{% endfor %}
</form>
</table>
handler.py
class BlobstoreServeHandler(RequestHandler, BlobstoreDownloadMixin):
def post(self):
blob_info = blobstore.BlobInfo.get(self.request.form.get('blobkey'))
return self.send_blob(blob_info, save_as=True)
urls.py
rules = [
Rule('/', endpoint='index', handler='apps.77notes.handlers.IndexPageHandler'),
Rule('/upload', endpoint='upload/html', handler = 'apps.77notes.handlers.BlobstoreUploadFormHandler'),
Rule('/upload/handler', endpoint='upload/handler', handler='apps.77notes.handlers.UploadHandler'),
Rule('/download', endpoint='download/html', handler = 'apps.77notes.handlers.BlobstoreDownloadFormHandler'),
Rule('/download/file', endpoint='download/file', handler='apps.77notes.handlers.BlobstoreServeHandler'),
Rule('/show', endpoint='show/html', handler='apps.77notes.handlers.ShowUploadedFilesHandler'),
]
variables
blob_files = uploaded_files_to_blobstore = blobstore.BlobInfo.all()
download_blob = self.url_for('download/file')
Thanks!

Of course it's always the first. You're declaring three hidden fields with the same name but various values. How could the server understand you want “the hidden field nearest to the download button I clicked”?
You could do this with Javascript but it's overkill. Maybe you should rather create forms for each item, but I'm not sure it is HTML-valid.
{% for file in blob_files %}
<tr>
<!-- stuff -->
<td><form class="show_blob" name="show_blob" method="post" action="{{ download_blob }}">
<input type="submit" name="download" value="Download" />
<input type="hidden" name="blobkey" value="{{ file.key() }}" />
</form></td>
</tr>
{% endfor %}
If you don't like this, you could also provide the index of the desired blobkey within the download submit button. Something like this:
{% for file in blob_files %}
<tr>
<!-- stuff -->
<td>
<input type="submit" name="dl{{ loop.counter0 }}" value="Download" />
<input type="hidden" name="blobkey" value="{{ file.key() }}" />
</td>
</tr>
{% endfor %}
Then, server-side, you get the right blobkey using:
# don't forget to handle errors here, NTUI
ind = int([_[2:] for _ in self.request.form if _.startswith('dl')][0])
blobkeys = self.request.form.getlist('blobkey')
blobkey = blobkeys[ind]
# stuff

if you what to download through form you need to do as many form as blob you have
{% for file in blob_files %}
<tr>
<td>{{ loop.index }}</td>
<td>{{ file.filename }}</td>
<td>{{ file.content_type }}</td>
<td>{{ file.creation }}</td>
<td>{{ file.size }}</td>
<td>
<form id="show_blob" name="show_blob" method="post" action="{{ download_blob }}">
<input type="submit" name="download" value="Download"/>
<input type="hidden" name="blobkey" value="{{ file.key() }}" />
</form>
</td>
</tr>
{% endfor %}
or you can do it by ordinary A tag like this <a href = '/get/{{ file.key() }}'>

Related

Django: select all data from a row in a html table and use them in a view

I am new to both django and web development.
The task is to run a script when pressin a button using the data contained in the specific row of an html table.
So if i clik on the second button "Run script" it uses all the data in that row (8888, 2020/06/21 06:00) in a separate script to performe some task.
Currently my html file looks like this:
There are 2 sections one for uplading the information that goes in the table one the table which displays them
<h1>Approach Path KML Generator</h1>
<h2>Generate the KML file here:</h2> <form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button onclick="location.href='{% url 'approach_path_receptorsKML:Proejct Creator' %}'">Upload</button> </form>
<h2>Projects Available:</h2>
<table class="table">
<thead>
<tr>
<th>Project ID</th>
<th>Date KML</th>
<th>Time KML</th>
<th>Date-Time Uploaded</th>
<th>Run The Conversion</th>
<th>KML File</th>
</tr>
</thead>
<tbody>
{% for project in latest_project_list %}
<tr>
<td>{{ project.project_ID }}</td>
<td>{{ project.date_kml }}</td>
<td>{{ project.time_kml }}</td>
<td>{{ project.upload_time }}</td>
<td>
<button method="post" value="collect data" name="{{ project.project_ID }}|{{ project.date_kml }}|{{ project.time_kml }}|{{ project.upload_time }}">Run script</button>
</td>
<td>
Download KML File
</td>
</tr>
{% endfor %}
</tbody> </table>
And this is the view I have created:
def ProjectCreator(request):
form = DocumentForm()
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid:
form.save()
elif 'collect data' in request.POST.values():
values = [key for key in request.POST.keys()]
print(values)
else:form = DocumentForm()
I have tried to use this guide (How do I pass table data from a template over to Django on a button/submit click?) however, I have been insuccesfull.
If anyone can spot the mistake I have made and give me some explanation I would be grateful.
Thanks
It doesn't work because you have incorrect HTML layout. Button itself doesn't do anything - in order to send POST request, it should be in <form> tag. Try following:
{% for project in latest_project_list %}
<tr>
<td>{{ project.project_ID }}</td>
<td>{{ project.date_kml }}</td>
<td>{{ project.time_kml }}</td>
<td>{{ project.upload_time }}</td>
<td>
<form method="post">
{% csrf_token %}
<button value="collect data"
name="{{ project.project_ID }}|{{ project.date_kml }}|{{ project.time_kml }}|{{ project.upload_time }}">
Run script
</button>
</form>
</td>
<td>
Download KML File
</td>
</tr>
{% endfor %}
This will work, but I doubt this is great way of achieving this. You can just send project.pk with POST request and fetch project in view. This way you can be sure user will not send incorrect/malicious data with request. It is especially important since your code will run script based on data.

Python Django Edit HTML rows and update database

I have a table and I wish to add an edit button that updates the certain record both visually and in the database.
The HTML I have.
{% for work_entry in work_entries %}
{% if work_entry.date == date.date %}
<form action="{% url 'work_entries:object_edit' work_entry.id %}" method="post">
{% csrf_token %}
<tr>
<td>
<button onclick="return confirm('Are you sure you want this edit?')">Edit
</button>
</td>
<td> <form action="{% url 'work_entries:object_delete' work_entry.id %}" method="post">
{% csrf_token %}
<button onclick="return confirm('Are you sure you want to delete this record?')">Delete
</button>
</form>
</td>
<td>{{ work_entry.id }}</td>
<td><input type="text" value="{{ work_entry.description }}" name="description"></td>
<td><input type="number" value="{{ work_entry.num_hours }}" name="hours"></td>
</tr>
</form>
{% endif %}
{% endfor %}
The views.py
def object_edit(request, object_id):
record = get_object_or_404(WorkEntry, pk=object_id)
if request.method == "POST":
record.description = request.POST["description"]
record.num_hours = request.POST["hours"]
record.save()
return redirect("/employeePage")
return render(request, "employeePage.html")
And urls.py
app_name = "work_entries"
urlpatterns = [
path("", views.employee_view, name="employeePage"),
url(r"^delete/(?P<object_id>[0-9]+)/$", views.object_delete, name="object_delete"),
url(r"^edit/(?P<object_id>[0-9]+)/$", views.object_edit, name="object_edit"),
]
I used an input tag in the as I thought that would allow me to change data and save it. However this is giving me MultiValueDictKeyError at /employeePage/edit/14/
'description' error. I am not too experienced with jquery which from research I saw that could work but I don't seem to get it right. Can someone help or even suggestions on how I should approach this would be useful.
Note: there is already a button to delete the record which works, I tried a similar approach for editing, but it doesn't work.
I fully encourage you to use the forms provided by Django, it will make your life easier.
And I fully encourage you as well to not use a form for your delete stuff, it should be a simple link, it would avoid to have a form in a form. I think your problem is here. Having a form in a form with the button in the middle make impossible for your browser to know which parts of the form you want to submit.
As well you have two buttons but none of them is submit type.
If you don't want to use the Django forms a way to do it would be
{% for work_entry in work_entries %}
{% if work_entry.date == date.date %}
<form action="{% url 'work_entries:object_edit' work_entry.id %}" method="post">
{% csrf_token %}
<tr>
<td>
<button>
Delete
</button>
</td>
<td> <button type="submit" onclick="return confirm('Are you sure you want to update this record?')">
Update
</button>
</td>
<td>{{ work_entry.id }}</td>
<td><input type="text" value="{{ work_entry.description }}" name="description"></td>
<td><input type="number" value="{{ work_entry.num_hours }}" name="hours"></td>
</tr>
</form>
{% endif %}
{% endfor %}
It's not the most beautiful way to do it I tried to keep your achitecture

Django : Zipping selected object's pdf

I have an Index view that displays Trainer name, location, etc as well s a button to download their Profile(already in database as a pdf). I search and filter these based on some parameters. Now i want to be able to select a few of these and download the selected Trainer Profiles.Im new to Django and can't figure out how to do that. Here's my code. It says 'FieldFile object is not callable'
views.py
def download(request):
import zipfile
import os
file = zipfile.ZipFile("C:\\Downloads\\test.zip", "w")
filelist = []
filelist+= 'Trainer.checkboxoption' in request.REQUEST
for i in filelist:
file.write(i)
file.close()
return render(request,'trainer/index.html')
index.html
<form action="download/" method="post">
<div class="caption">
<div >
<table style="width:100%" class="table">
<tr>
<th>#</th>
<th>Name</th>
<th>Technology</th>
<th>Location</th>
<th> View</th>
<th>Download</th>
<th>Delete</th>
</tr>
{% for trainer in all_trainers %}
<tr>
<td><input type="checkbox" id="trainer{{ forloop.counter }}" name="trainer" value="{{ trainer.id }}"></td>
<td> <a href="/trainer/{{ trainer.id }}">{{ trainer.name }}</td>
<td>{{ trainer.technology }}</td></a>
<td>{{ trainer.location }}</td>
<!-- View Details -->
<td>View Details</td>
<td>Download PDF</td>
<!-- Delete Album -->
<td><form action="trainer/{{trainer.id }}/delete/" method="post">
{% csrf_token %}
<input type="hidden" name="trainer_id" value="{{ trainer.id }}" />
<button type="submit" class="btn btn-default btn-sm">
<span class="glyphicon glyphicon-trash"></span>
</button>
</form></td>
</tr>
{% endfor %}
</table>
</div>
</div>
<input type="submit" value="Download ZIP">
</form>
views.py:
def download(request):
import zipfile
filef = zipfile.ZipFile("test.zip", "w")
var = request.POST.getlist('checks')
pdfprofile = []
for i in var:
trainer= Trainer.objects.get(pk=i)
pdfprofile.append(trainer.trainer_profile)
for i in pdfprofile:
filef.write("D:\\Django\\myproduct\\media\\"+str(i))
filef.close()
response = HttpResponse(open("test.zip", "rb").read(), content_type='application/zip')
response['Content-Disposition'] = 'attachment; filename=test.zip'
return response

Create a custom URL and outputting HTML form data

For my first Flask project I wanted to create a basic Flask app using Riot Game's API for League of Legends. I've got all the processing of API working but I'm having trouble going about outputting it.
I take the input from a form on one page.
<form class="navbar-form navbar-left" action="{{ url_for('current_game_output') }}" method="POST">
<div class="form-group">
<input type="text" class="form-control" placeholder="Summoner Name" name="summoner_name">
<select class="form-control" name="region">
<option value="oce">Oceanic</option>
<option value="na">North America</option>
<option value="euw">Europe West</option>
</select>
</div>
<button type="submit" class="btn btn-default" value="Send">Submit</button>
</form>
And I am trying to output the data returned from the API onto the next page.
{% extends "header.html" %}
{% block body %}
<h3> Team 2 </h3>
<table class="table table-bordered" width="50%">
<tr>
<th width="48%">Summoner Name</th>
<th width="48%">Champion</th>
<th width="4%">Pic</th>
</tr>
{% for player in team1 %}
<tr>
<td>{{ player[0] }}</td>
<td>{{ player[1] }}</td>
<td><img width="20px" src="{{ url_for('static', filename='images/championIcons/') }}{{ player[1].replace(" ", "") }}_Square_0.png"></td>
</tr>
{% endfor %}
</table>
<h3> Team 1 </h3>
<table class="table table-bordered" width="50%">
<tr>
<th width="48%">Summoner Name</th>
<th width="48%">Champion</th>
<th width="4%">Pic</th>
</tr>
{% for player in team2 %}
<tr>
<td>{{ player[0] }}</td>
<td>{{ player[1] }}</td>
<td><img width="20px" src="{{ url_for('static', filename='images/championIcons/') }}{{ player[1].replace(" ", "") }}_Square_0.png"></td>
</tr>
{% endfor %}
</table>
{% endblock %}
I'd like the URL of the output page to be dynamic'/currentgame/region/username' but keep getting errors when trying to do so.
Relevant part of my views.py file (hidden my api key):
#app.route('/header')
def header():
return render_template("header.html")
#app.route('/current')
def current():
return render_template("current.html")
#app.route('/currentgame/<region>/<name>', methods=['POST'])
def current_game_output(region, name):
region = request.form['region']
summoner_name = request.form['summoner_name']
api = RiotAPI('APIKEYGOESHERE', region)
team1, team2 = current_game_data(summoner_name, region, api)
return render_template("output.html",team1=team1,team2=team2)
Any help/pointers on the best way to output/return the data would be appreciated.
Thanks
You should post the error as well.
In a quick looks this should be fixed:
#app.route('/currentgame/<string:region>/<string:name>', methods=['POST'])
def current_game_output(region, name):
region = request.form['region']
summoner_name = request.form['summoner_name']
api = RiotAPI('APIKEYGOESHERE', region)
team1, team2 = current_game_data(summoner_name, region, api)
return render_template("output.html",team1=team1,team2=team2)
Change route to /currentgame/<string:region>/<string:name>

Passing a template variable as input to a hidden form input field

I have a template index.html which contains a table of data. At the end of each row data, there is an edit button which allows you to edit the column entries in that row.
Here is how my table is constructed in index.html:
<table style="width:300px">
<tr>
<td> Mailing List Name</td>
<td> Mailing List Creation Date</td>
</tr>
{% for listEntry in lists %}
<tr>
<td>{{ listEntry.name }}</td>
<td>{{ listEntry.create_date }}</td>
<td>
<form action="/list_edit" method="post">
<input id="submit" type="submit" value="Edit" />
**Extra hidden input fields would be added here**
</form>
</td>
</tr>
{%endfor%}
</table>
How can I pass variables defined in the ListEntry model as hidden input fields to a form?
Try:
<input name="{{ listEntry.someId }}" type="hidden" value="{{ listEntry.value }}" />
However if you have one <form> element per row, you need to give submit action a value based on which row it is too.

Categories