How do I use Flask's url_for in a template? - python

I am building a Flask application to manage character sheets for an RPG using an SQL database.
Currently, I have the following script that displays the list of a user's characters currently in the database.
#app.route("/<cha_name>")
#login_required
def character_sheet():
characters = db.execute(
"""SELECT
*
FROM
characters
WHERE
user_id = :user_id AND
name = :cha_name
""",
user_id=session["user_id"],
cha_name="???",
)
if not characters:
return render_template("add_char.html")
I would like to include a button that navigates to the character sheet for the specic chosen character. So the page below would detail some of the stats for the character, and then a button would take the user to a populated character sheet on another page.
This is what I have so far for displaying a specific user's characters.
{% extends "main.html" %}
{% block title %}
Characters
{% endblock %}
{% block main %}
<table border = 1>
<thead>
<td>Player</td>
<td>Name</td>
<td>Race</td>
<td>Class</td>
<td>Level</td>
<td>Status</td>
<td>View</td>
</thead>
{% for row in rows %}
<tr>
<td>{{ character["user"] }}</td>
<td>{{ character["name"] }}</td>
<td>{{ character["race"] }}</td>
<td>{{ character["cha_class"] }}</td>
<td>{{ character["level"] }}</td>
<td>{{ character["status"] }}</td>
<td><a href={{ url_for('cha_name') }}>View Sheet</a></td> <!-- HERE -->
</tr>
{% endfor %}
</table>
Add a new Character
Go back to home page
</body>
{% endblock %}
What do I use on the line with <!-- HERE --> to make a link to the character sheet URL?

Whew, there's a lot to unpack here!
Your example is/was invalid, e.g. cha_name= on the end of the db.execute() call, and there are no calls to render_template if a character is found, so it'll never produce a response even with a valid request.
Next, you've told Flask to pass a cha_name parameter to your character_sheet function, but haven't defined a parameter on the function itself.
Finally, in your template you're passing cha_name to the url_for function, which (so far as we can see from the sample code) isn't a route that you've defined, so can't work.
You need to include more information for us to help, such as telling us what error you're seeing. Right now, I imagine the Flask service won't even start due to the syntax error. Once that's fixed, I'd expect to see something like the following:
werkzeug.routing.BuildError: Could not build url for endpoint 'cha_name'. Did you mean 'character_sheet' instead?
I'd suggest you head back to the documentation on URL building and also look at the docs for the route decorator. You'll see on the latter that "Flask itself assumes the name of the view function as endpoint". That means that in order to get Flask to generate a URL for your character_sheet function, you'll need to pass that name to url_for, and then the parameters, like this:
url_for('character_sheet', cha_name=character.name)
If a user were to rename their character, all of the URLs would change, which is a bad user experience — what would happen if they'd bookmarked a particular character, then fixed a typo in the name?
Putting this all together, here's a hopefully-better example:
# app.py
#login_required
#app.route("/<character_id>")
def character_sheet(character_id):
characters = db.execute(
"""SELECT
*
FROM
characters
WHERE
id = :id AND
user_id = :user_id
""",
id=character_id,
user_id=session["user_id"],
)
if not characters:
return abort(404)
return render_template(
"characters/sheet.html",
character=characters[0],
)
<!-- templates/characters/list.html -->
{% extends "main.html" %}
{% block title %}
Characters
{% endblock %}
{% block main %}
<table>
{% for character in characters %}
<tr>
<td>{{ character.name }}</td>
<td><a href={{ url_for('character_sheet', character_id=character.id) }}>View Sheet</a></td>
</tr>
{% endfor %}
</table>
Add a new Character
{% endblock %}

Related

passing variable to a table in flask

I am following this video and everything is working perfectly. The output of a SQL query is saved within a variable that I can call on in another .html page.
so my sql query is:
#app.route('/all_data')
def all_data():
customers = db.get_customers()
for customer in customers:
var = customers
return render_template('all_data.html',var=var)
When calling this {{var}} in all_data.html page the output is for a long tuple:
('name','email','comment','gender'),('name','email','comment','gender') etc etc
I am looking to put this {{var}} into a table instead?.. I but am hitting a brick wall in finding out how to do this?
any help would be much appreciated!
Assuming you're using Flask's default template engine (Jinja2), you can simple use a for loop to iterate over the elements of the collection inside the template. The syntax is:
{% for item in items %}
{{ item }}
{% endfor %}
So simply create an HTML table, and add a row for each item in your var variable.
IIRC, Jinja tuples act the same as Python's, so you can probably do something along the lines of:
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Comment</th>
<th>Gender</th>
</tr>
</thead>
<tbody>
{% for current in var %}
<tr>
<td>{{ current[0] }}</td>
<td>{{ current[1] }}</td>
<td>{{ current[2] }}</td>
<td>{{ current[3] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
I haven't checked it (as I don't have a Flask project available right now), but if my memory serves me right, that's how I would do it
Here's a link to an usefull part of Jinja2's documentation
Also, what is this piece of code for ?
for customer in customers:
var = customers
You're iterating over each element of the list, but your never use the customer variable (containing the current element). Instead, you assign the var variable to the customers list multiple times. Assuming customers is a simple Python list, you could remove this loop and simply do return render_template('all_data.html',var=customers)

Flask - cant post to route [duplicate]

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.

Catch specific element from for loop using jinja2

I have a for loop created with jinja2 on my html, showing a list of users in my sqlite database:
{% for user in users %}
<tr>
<td>{{ user.id }}</td>
<td>{{ user.username }}</td>
<td>{{ user.email }}</td>
<td>
{% if user.admin == True: %}
Admin Icon
{% else: %}
Not Admin Icon
{% endif %}
</td>
</tr>
{% endfor %}
I would like to know how can I pick the specific user from the line the link is clicked?
I tried to catch it through url_for:
Admin Icon
But I got this message on terminal:
jinja2.exceptions.TemplateSyntaxError: expected token ',', got 'string'
I don't know what is the more appropriated way to do this.
I think this might be because of a syntax error rather than anything to do with your loops. You seem to have an extra double quote in your url_for arguments: user=user" should be user=user. This extra quotation mark has the undesired effect of closing the value of the href attribute which also starts with a double quote, leaving the closing brackets and braces outside of the attribute. Your <a> tag should look like this:
Admin Icon
It is generally considered best practice to use double quotes in HTML attribute values, so use single quotes when inserting strings with Jinja for arguments.

SQLAlchemy/Flask not showing records in table

Currently programming a small app in Flask for school.
I made a DB and the I've put some data in there, simple nothing to worry about.
I created a query to get all of the records out and show them in a html table,
all I get is <player 1> or <player 2>, but not the actual name or player ID.
In the route:
#main.route('/')
#login_required
def index():
from .models import Players
playerdetails = Players.query.all()
return render_template('index.html', name=current_user.name, playerdetails=playerdetails)
the HTML:
<tbody>
{% for players in playerdetails %}
<tr>
<td>{{ playerdetails[1] }}</td>
<td>{{ playerdetails[2] }}</td>
</tr>
{% endfor %}
</tbody>
This is what I have in the database:
This is what the website shows:
I can add more players, and the table gets bigger. and if I change it to {{ playerdetails[3] }}
it'll just show < players 4 >
Figured it out.
I had to do {{ players.name }} and {{ players.player_number }}
small mistake that can really grind your gears for hours.

Django - how to make a link in an html template open an app and pass a objects value as a parameter

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 %}

Categories

Resources