I'm building a simple web app that displays the logged-in users hardware asset register of sorts. In my Html page I have an accordion with a table as follows:
<tbody>
{% for hard in HARDWARE %}
<tr>
<td>
<form action="{{url_for('del_hardware', id=hard.id)}}" method="POST">
<input type="hidden" name="_method" value="DELETE">
<input type="submit" value="Delete" class="btn btn-danger">
</form>
</td>
<td>{{ hard.name }}</td>
<td>{{ hard.description }}</td>
<td>{{ hard.purchased }}</td>
</tr>
</tbody>
{% endfor %}
in my application.py file I then have the following code:
#app.route('/del_hardware/<string:id>', methods=["POST"])
#login_required
def del_hardware(id):
db.execute("UPDATE hardware SET actioned ='D' WHERE id=:hid", hid=id)
return redirect("/")
for whatever reason, the route does not work for the first record in the table.
Clicking the Delete button for the first record in the table:
No request is shown, the page reloads or is redirected to "/".
Clicking the Delete button for every other record in the table: I get the following request
INFO:werkzeug:...[23/Dec/2019 10:31:42] "POST /del_hardware/4 HTTP/1.0" 302 -
where 4 is the ID of the device in the hardware table, and it works fine - The SQL
statement is executed and I'm redirected to "/" as intended.
Any help would be appreciated, apologies if I haven't described the issue that well or if i've simply overlooked a silly mistake somewhere.
Related
I am developing a simple web front-end with Flask, which displays a table from database, and when an user selects a specific row, the Flask gets the information of a specific column of the selected row.
with the following codes, I display a table with 5 columns of data and one last column of 'submit' button. When the 'submit' button of the specific row, the second column ('Title') information is supposed to be posted back to Flask app.
It works 90%, because when the button is clicked, always the first row information is posted, even if a different row is selected. Could anyone figure what went wrong here?
Thanks!
here is a flask code
#app.route("/", methods=["GET", "POST"])
def home():
df = pd.read_excel('database.xlsx')
this_list = df.values.tolist()
if request.method =="POST":
if request.form.get('select_song') == 'select':
print('selected')
print(request.form.get("title"))
return render_template('basic_table.html', title='Basic Table',
table=this_list)
this is a basic_table.html
<form method="POST">
<table id="data" class="table table-striped">
<thead>
<tr>
<th>Type</th>
<th>Title</th>
<th>Location</th>
<th>Translation</th>
<th>Content</th>
<th>selection</th>
</tr>
</thead>
<tbody>
{% for row in table %}
<tr>
<td>{{ row[0] }}</td>
<td><input type="hidden" name="title" value="{{ row[1] }}"> {{ row[1] }}</td>
<td>{{ row[2] }}</td>
<td>{{ row[3] }}</td>
<td>{{ row[4] }}</td>
<td>
<input class="form__submit form__input" type="submit" value="select" name="select_song"/>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
</form>
When you click a button whose type is 'submit', it will submit the form attached to that submit button. Submitting the form means it will pass html elements by name/value pair.
In your code, you have ONLY 1 form and each row has the same input element with the name title. Therefore when you submit the form, it will submit the first instance of title
There are 2 possible solutions here
You change your code so that you have a form in each row. This way, when you submit, you are submitting the form for just that row.
You keep your code as is, but add Javascript code to 'intercept' your submit action. Your JS code will then determine which row you clicked, pick the value of the title in that row and manually submit the form with that value.
This question already has answers here:
Sending data from HTML form to a Python script in Flask
(2 answers)
Closed last year.
I'm creating a configurator app for heating systems. Essentially the idea is by putting in a few inputs - the app will spit out the part number for a system pack where a user can see a detailed bill of material (BOM).
One key element is the output where we need to show a few options. I.e. if someone needs a 200kW system, there could be 3-4 packs that are suitable (190kW -210kW might be more cost effective).
I want in the first instance to show on a route the pack options that are suitable- then the user selects the pack they want- which takes you to a route (/cart) which shows the BOM.
I have put in input variables min and max which searches a database cascades.db. This successfully shows me the table of options.
from cs50 import SQL
from flask import Flask, render_template, request, url_for, redirect
app = Flask(__name__)
db = SQL("sqlite:///cascades.db")
#app.route("/")
def index():
return render_template("index.html")
#app.route("/search", methods=["GET", "POST"])
def search():
output = request.form.get("output")
hydraulic = request.form.get("hydraulic")
layout = request.form.get("layout")
controls = request.form.get("controls")
min_output = int(output) - 15
max_output = int(output) + 15
cascades = db.execute("SELECT * FROM cascades WHERE hydraulic = ? AND layout = ? AND output BETWEEN ? and ?", hydraulic, layout, min_output, max_output)
return render_template("search.html", cascades=cascades)
#app.route("/cart", methods=["GET", "POST"])
def cart():
bom_id = request.form.get("bom_id")
bom = db.execute("SELECT * FROM bom WHERE bom_id = ?", bom_id)
return render_template("bom.html", bom = bom)
When running the app- the first bit works - i.e. it shows me a list of all the packs that meet the criteria- but when clicking the 'Choose' button Im getting stuck.
{% extends "layout.html" %}
{% block body %}
<h3> Boiler Cascade Pack Options:</h3>
<table class="table table-striped table-boardered">
<tr>
<th>Part Number</th>
<th>Description</th>
<th>Number of boilers</th>
<th>BOM</th>
</tr>
{% for cascades in cascades %}
<tr>
<td scope="cascades">{{ cascades["id"] }}</td>
<td>{{ cascades["description"] }}</td>
<td>{{ cascades["number_of_boilers"] }}</td>
<td>
<form action "/cart" method="post">
<input name="bom_id" type="hidden" value="{{ cascades["id"] }}">
<input class="btn btn-primary" type="submit" value="Choose">
</form>
</td>
</tr>
{% endfor %}
</table>
{% endblock %}
But when submitting the form- where they select the pack (which has the pack id number- as a hidden form) I get the following error:
File "/home/ubuntu/cascade2/application.py", line 26, in search
min_output = int(output) - 15 TypeError: int() argument must be a
string, a bytes-like object or a number, not 'NoneType'
It seems like the route is trying to use the same logic in the second search but at this point its redundant. The second search all I want is to show me information where the Pack id = BOM.
I've noticed the URL stays on the (/search) route with this and not going to the (/cart).
I've tried several things such as putting in a IF NOT output- redirect to Cart to try and bi pass this- which successfully loads the bill of material page but nothing comes up as I don't think its posted the id to the route.
I've also changed it to GET instead of POST, which results in the query string /search?bom_id=71723827132. Which shows its picking up the part number- but staying on the /search route where I see the error.
<h3> Boiler Cascade Pack Options:</33>
<table class="table table-striped table-boardered">
<tr>
<th>Part Number</th>
<th>Description</th>
<th>Number of boilers</th>
</tr>
{% for cascade in cascade %}
<tr>
<td scope="cascade">{{ cascades["id"] }}</td>
<td>{{ cascades["description"] }}</td>
<td>{{ cascades["number_of_boilers"] }}</td>
</tr>
{% endfor %}
</table>
<br>
<h4>Bill of material:</h4>
<br>
<table class="table table-striped table-boardered">
<tr>
<th>Product ID</th>
<th>Product Description</th>
<th>Quantity</th>
</tr>
{% for bom in bom %}
<tr>
<td scope="bom">{{ bom["part_number"] }}</td>
<td>{{ bom["product_description"] }}</td>
<td>{{ bom["quantity"] }}</td>
</tr>
{% endfor %}
</table>
Been stuck on this for a month. This to me is the last piece of the puzzle. Any help/suggestions would be great. I'm new to programming so I bet I've missed something obvious :)
Your <form action "/cart" method="post"> is missing an = after action. That means the action attribute is not properly defined, and the default for action always is the URL you're currently on. That's why it stays at /search.
Hello I have an app that displays some database information in a table. Inside of the html template I am making an edit link that I want to open another app(page viewLit) while passing a value to it's view. I have added my code below. My question is I am unsure of how to make this links url and pass the object data located inside circuit.circuitid along with it. I haven't been able to find the right way to code this yet and this is just how I thought that this should be done. If anyone has a better idea I am open to suggestions.
search_custom.html(code for link)
{% for circuit in filter.qs %}
<tr>
<td class="actions">
View
</td>
<td>{{ circuit.circuitid }}</td>
</tr>
{% endfor %}
myapp/myapp/urls.py
urlpatterns = [
path('viewLit/', include('viewLit.urls')),
]
myapp/viewLit/urls.py
urlpatterns=[
path('viewLit/circuitid.id', views.viewLit, name='viewLit'),
]
myapp/viewLit/views.py
def viewLit(request, circuitid):
#display records fields here
return HttpResponse("You are at the viewLit page!")
Have a look at the documentation:
Django documentation
myapp/viewLit/urls.py
urlpatterns=[
path('viewLit/(?P<circuit_id>\w+)', views.viewLit, name='viewLit'),
]
html- template:
search_custom.html(code for link)
{% for circuit in filter.qs %}
<tr>
<td class="actions">
View
</td>
<td>{{ circuit.circuitid }}</td>
</tr>
{% endfor %}
Working on converting an app from Flask to Django and getting stuck on how I can pull data from the value of a form button. I have some data that is rendered in a table with a for loop. Each entry has a button displayed that allows my to flip an integer field for the rendered data. Once it's flipped, it's no longer listed.
# manage.html
{% for ticket in tickets %}
<tr>
<td>{{ my_obj.id }}</td>
<td>{{ my_obj.tt }}</td>
<td>{{ my_obj.user }}</td>
<td>{{ my_obj.time }}</td>
<td><form action="/flip/" method="post">{% csrf_token %}
<input type="hidden" name="flip" value="{{ my_obj.id }}"/>
<input type="submit" class="btn btn-xs btn-danger" value="Kill"/>
</form>
</td>
</tr>
{% endfor %}
# views.py
def flip(request):
if request.method == 'POST':
tt_id = request.POST.get('value')
return HttpResponse(tt_id )
def manage(request):
my_obj = MyObject.objects.filter(status=1)
return render(request, 'manage.html', {'my_obj': my_obj})
Currently I am getting a response of None rather than the actual ID which is showing in the value field with firebug.
Thanks for looking!
You have to access the fields by the name, not with value. e.g.
request.POST.get('flip')
If you want to access the buttons value, you need to set the name attribute on it.
For details on your request it is helpful for print or return the whole request object. A debugger would be an other way to take a look at it.
I want to make sure I'm following best practices here. I have a table of data that pulls from the database and in the last column I have links to edit or delete that row. I feel like I've always been told to never modify data on the server with a GET request. How would I handle deleting this data row with anything other than a GET request?
Here's the code for the data table:
<table class="table table-hover notifications">
<thead><tr><th>Search Parameter</th><th>Subreddits</th><th>Actions</th></thead>
{% for notification in notifications %}
<tr>
<td>{{ notification.q }}</td>
<td>{% for s in notification.subreddits %} {{ s.r }}<br> {% endfor %}</td>
<td>Edit | Delete</td>
</tr>
{% endfor %}
</table>
I guess I'm not sure how to approach building the url for Delete. I can build a delete method and pass the id of the element I want to delete (ex: /delete/1), but is that not modifying data with a GET request then?
You can create a form that makes a POST request on submit, connected to a view that deletes the object when request.method is POST (passing the object ID in the URL, as you said).
I am not a Flask expert, but taking this code as example, your view should look something like:
#app.route('/delete/<int:id>', methods=['POST'])
def remove(id):
object = Object.query.get_or_404(id)
delete(object)
return redirect(url_for('index'))
And your form like:
<form method="post" action="{{ url_for('remove', id=object.id) }}">
<button type="submit">Delete</button>
</form>
The form action attribute forces the form to submit its information to the given URL/view.