How do I Correctly Render Python Data in Jinja HTML Templates? - python-3.x

I'm extremely new to python/flask, so I don't have the capacity to describe what I'm doing wrong. Please be patient with me.
As I'm in the learning stages, my goal is to create a simple app with GET, PUT, POST, DELETE functionalities. I'm using flask to render a template for a project; I'm using forms to make an HTML page. The one key issue here is:
in my routes.py
#app.route("/")
#app.route("/home")
def home():
all_pokemon = Pokémon.query.all()
result = ""
for pokemon in all_pokemon:
result += f"{pokemon.id} | {pokemon.pkname} | {pokemon.nickname}<br>"
return result
This, gives me what I want, which is this:
However, I have an home.html setup to do exactly the same thing. Simply show me this information.
in my home.html
{% extends 'layout.html' %}
{% block body %}
<h1>version 1.0.0</h1>
<br>
{% for pokemon in all_pokemon %}
{{ "{pokemon.id} | {pokemon.pkname} | {pokemon.nickname}" }}
<br>
{% endfor %}
{% endblock body %}
and to get that working, i have this in my routes.py
#app.route("/")
#app.route("/home")
def home():
all_pokemon = Pokémon.query.all()
return render_template("home.html", all_pokemon=all_pokemon)
But, doing this, gives me this output:
My question is, how do I get the render_template to NOT display the incorrect output, and to properly display the correct output of my forms?
I've been searching for an answer for this issue without an understanding of the proper terminology and have consequently made no progress for the past 3.5 hours. Any help is greatly appreciated. Swift help is incredibly appreciated.
Thank you. If I need to post more information please let me know.

I've figured it out, and as I suspected it was embarassingly simple.
The render template worked, but where I had
in my home.html
{{ "{pokemon.id} | {pokemon.pkname} | {pokemon.nickname}" }}
I should have had
{{ pokemon.id }} | {{ pokemon.pkname }} | {{ pokemon.nickname }}
I apologise for anybody who may have wasted their time searching for an answer, and I hope this can serve as a helpful guide to any other newbies like myself who get stuck on this.
Hopefully 7 years from now I can look back on this moment affectionately and laugh about it.

{% for pokemon in all_pokemon %}
{{ "{pokemon.id} | {pokemon.pkname} | {pokemon.nickname}" }}
<br>
{% endfor %}
Firstly you should use your context like that:
{{pokemon.id}} | {{pokemon.pkname}} | {{pokemon.nickname}}
And if you want to render your context, your view should be like that:
#app.route("/home")
def home():
all_pokemon = Pokémon.query.all()
result = ""
for pokemon in all_pokemon:
result += f"{pokemon.id} | {pokemon.pkname} | {pokemon.nickname}<br>"
if all_pokemon is not None:
return render_template("home.html", all_pokemon=all_pokemon, result=result)
else:
return render_template("home.html",all_pokemon=all_pokemon, result = "")
If you use your context named result, you should use a control in your template folder like that:
.html
{% if result|length > 1 %}
...
{% endif %}

Related

Is there a way to count the sorted queryset using count instead of length?

I am working on search part of my project. On one of my filter, I have to use sorted() function. Code:-
posts = Post.objects.filter(title__icontains=query)
posts = sorted(posts, key=lambda obj: obj.titleFilter())
But, in other filters in the project, I don't have to use sorted as it can be done with django annotate and so on. So, in the template I usually have to do posts.count except for the title filter.
My template, code:-
<div>{{ posts.count }} results found.</div>
I know, I can use if cases in template to work and apply length function like:
<div>
{% if title_filter %}
{{ posts|length }}
{% else %}
{{ posts.count }}
{% endif %} results found.
</div>
Also, I am not sure if length should be fine as there were some article not to use length for querysets.
Thank you!!! Feel free to ask.
After doing sorted posts is no longer a QuerySet.
You can do this
posts = Post.objects.filter(title__icontains=query)
count = posts.count()
posts = sorted(posts, key=lambda obj: obj.titleFilter())
send count as a variable

GCP Datastore render in JINJA2 shows also the complete key

So I pass the result from ds_data = list(query.fetch()) to JINJA2:
return render_template('index.html',images=ds_data)
To python this can be iterated as list with no problems but for JINJA2:
data-json="{{image|safe}}"
instead of having some dict: '{}' in HTML, I have the also complete key:
data-json="<Entity('kind', 'id') {'someDictKey': 'value', 'someDictKey': 'value'}>"
Hm, any ideas how can I remove the Entity's key in python, or render it correctly in JINJA2?
Template Snippet:
{% block content %}
{% if images is defined %}
<div class="main-content" id="main-content">
...
{% for image in images %}
<tbody id="tbody{{ image['key'] }}" data-json="{{ image }}">
</tbody>
{% endfor %}
...
{% endif %}
{% endblock %}
Thank you!
You'll actually have to convert your data to a json string. E.g. json.dumps(image). Specifically, you can see you'll need to cast image to a dict to get the map you want (https://googleapis.dev/python/datastore/latest/entities.html).
This is what I did last night after all:
#app.template_filter('parse_entity_list')
def parse_filter(item):
# Recompose DS object to list of dicts
a = json.dumps(item)
return a
app.jinja_env.filters['parse_entity_list'] = parse_filter
JINJA2 side:
<tbody id="tbody{{ image['key'] }}" data-json="{{ image | parse_entity_list }}">

Flask grid edit using wtforms

I'm working on a project that pulls a list of all the spirits listed in the Oregon State liquor pricing sheet and stores it in a table in my flask app. The goal is to be able to let moderators see the list and check a box next to each spirit to designate if it is whiskey or not.
It's my first time really using flask, but I've gone through a book about flask twice now to try and understand it, so this is me trying to build my own thing.
Right now, if I check a box and then hit submit all the records are switched to is_whiskey = False instead of each one being done individually.
Here's my model:
class Whiskies(db.Model):
__tablename__ = 'whiskies'
id = db.Column(db.Integer, primary_key=True)
ols_name = db.Column(db.String(64), unique=True)
display_name = db.Column(db.String(64), unique=True, index=True)
display_distiller = db.Column(db.String(64), unique=False)
is_whiskey = db.Column(db.Boolean, default=True)
reviews = db.relationship('Review', backref='review', lazy='dynamic')
Here's my view:
#main.route('/update-whiskies', methods=['GET', 'POST'])
#login_required
#permission_required(Permission.MODERATE)
def update_whiskies():
whiskies = Whiskies.query.all()
is_whiskey_form = IsWhiskeyForm()
submitpageform = SubmitPageForm()
if submitpageform.validate_on_submit():
for w in whiskies:
w.is_whiskey = is_whiskey_form.is_whiskey.data
db.session.update(w)
db.session.commit()
flash("You updated a bunch of whiskies")
return redirect(url_for('.index'))
return render_template('update_whiskies.html', whiskies=whiskies,
is_whiskey_form=is_whiskey_form,
submitpageform=submitpageform)
Here's my .html page:
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block page_content %}
<div>
{% for whiskey in whiskies %}
<li class="whiskey">
{% if whiskey.display_name %}
{{ whiskey.display_name }}
{% else %}
{{ whiskey.ols_name }}
{% endif %}
{{ is_whiskey_form.is_whiskey.label }}
{{ is_whiskey_form.is_whiskey(value=whiskey.is_whiskey) }}
</li>
{% endfor %}
{{ wtf.quick_form(submitpageform) }}
</div>
{% endblock %}
Here are my forms:
class IsWhiskeyForm(Form):
"This is intended for use in bulk editing whiskies"
is_whiskey = BooleanField("Is Whiskey", default="checked")
class SubmitPageForm(FlaskForm):
"This is intended for submitting a batch edit"
submit = SubmitField('Update Whiskies')
This is what the page currently looks like. I want to be able to check a box and hit submit and then have it reflected in the db.
Thanks a lot to whoever is up to helping me with this. I've been stuck about a week and I just need to get past this hump to finish up my module.
I had a similar task to solve: producing a list of database records (1 record per row) and let the user choose one record. So I put a radiobox at the end of each row. The problem was how to inform the Flask app which row was checked. I came up with the following solution:
You must make the submit button of the form trigger a POST request
You need in the data you want to show in the browser one field which is unique so you can use it to identify a row (I had data from an SQL database to display, so the primary key of a record was a good solution)
Add a radiobutton in your html-template inside the {% for ... %} loop like this:
{% for data in dataset.items %}
... display the data of the row, plus:
<div>
<input type="radio" class="form-check-input cursor-pointer"
name="check_line" value="{{ data._id }}">
</div>
{% endfor %}
in the POST request data there will appear a name-value pair produced by the radiobuttons which consists of the name you provided (here: "check_line") and the value you provided. The trick is that the value can be used to identify the row (is unique) because the Flask-rendering-magic was used.
By the way: when testing POST-requests I often write:
if request.method == 'POST':
print(request.values)
in the route-functions where POST needs to be handled. This will display all the POST input parameters on the Flask-console.
Try this
if submitpageform.validate_on_submit():
for w in whiskies:
w.is_whiskey = is_whiskey_form.is_whiskey.data
db.session.commit()
Remove db.session.update(w)

Twig Access Array Index?

Is it possible to directly access an array index from within a Twig template?
Here's my setup, using Silex:
return $app['twig']->render('template', array('numbers' => array('one', 'two', 'three')));
so can I do something like this?
{{numbers[0]}}
Just before posting this I realized, that's exactly what you can do, but as I didn't find the answer anywhere in the docs or google (correct me if I'm wrong), I've posted this anyway.
{{numbers[0]}}
The answer of Adam, is correct, only to make it clear and improve,
you can have access directly to array index
{{ myArray[0] }}
if you need to access in a loop
{% set arrayOfItems = ['ZERO', 'ONE'] %}
{% set myArray = ['APPLE', 'ORANGE'] %}
{% for oneItem in arrayOfItems %}
<p>{{ oneItem }} equals {{ myArray[loop.index0] }}</p>
{% endfor %}
in this example I used an array inside a non related loop so the result is:
ZERO equals APPLE
ONE equals ORANGE
Thats actually something what doesnt work for me when using Twig with shopware 6.
I try to access an object like
{{ page.cart.lineItems.elements[0].quantity }}
what will lead into a parsing error of the Twig Template
I can use
{{ page.cart.lineItems.elements | first }}
to get the first Element, but dont know how i can then access a property of this first element

How do you translate array items and join them?

Using twig, how can I translate all items in an array and join them with a slash?
Do I have to use an additional variable or is there a cleverer method?
For the moment, I'm doing something like this:
{% set labels = [] %}
{% for feature in menu_item.features %}
{% set labels = labels|merge([feature|trans([], 'features')]) %}
{% endfor %}
{{ labels | join(' / ')}}
It sucks.
Why not just output the content while you're looping ?
{% for feature in menu_item.features %}
{% if loop.index0 > 0 %}/{% endif %}
{{feature|trans}}
{% endfor %}
Maybe I'm late to the party, but you can now do this easily with the map filter:
{{ menu_item.features|map(feature => feature|trans)|join(' / ') }}
See documentation:
Twig >v1.41: https://twig.symfony.com/doc/1.x/filters/map.html
Twig >v2.10: https://twig.symfony.com/doc/2.x/filters/map.html
Twig v3.x: https://twig.symfony.com/doc/3.x/filters/map.html
Not everything should be done within the "view".
This type of code is probably much better placed within your controller logic and then passed into the view as the merged+joined result. Because in your example all you're doing is compiling a result which can much more easily be done within code.

Resources