How to make dropdown list change a table data with Jinja2 + Flask - python-3.x

I'm using Jinja2 Template to change dynamically a result table for my Motor Racing webapp. It's not perfect, but I'm making my way with patience.
I have a template that is rendered by the URL 'webapp.com/driver/' that's supposed to show the results of a Race where the driver was present inside a table. To make it clear:
The user selects a Driver and goes to webapp.com/driver/someDriver
The someDriver page has a dropdown list with ALL Races the someDriver has been through
User selects someRace from dropdown list
Table is fed by someRace's results
The python flask route I wrote for it:
#app.route('/driver/<driverid>')
def driver(driverid):
driver = db.Drivers.find_one({'_id': ObjectId(driverid)})
races = db.Races.find({'finalresult.driver':driver['unique_name']}).sort([('timestamp', -1)])
racesList = []
for race in races:
raceItem = {
'filename':race['filename'],
'date':datetime.datetime.fromtimestamp(float(race['timestamp'])).strftime('%d-%m-%Y'),
'finalresult':race['finalresult'],
'id':str(race['_id'])}
racesList.append(raceItem)
return render_template('user.html', driver=driver, racesList=racesList)
Now I'm trying to make a dynamic template that changes the Result table everytime the user change the selection.
<div class="row">
<form name="racesForm">
<label for="selRace">Escolha uma corrida!</label>
<select class="form-control" id="selRace">
{% for race in racesList %}
<option value="{{race.filename}}">
{{race.filename}}
</option>
{% endfor %}
</select>
</form>
<hr>
</div>
<div class="row">
<table>
<thead>
<th>Position</th>
<th>Driver</th>
</thead>
<tbody>
<tr>
{% position, drivername in race.finalresult %}
<th>{{position}}</th>
<td>{{drivername}}</td>
{% endfor %}
</tr>
</tbody>
</table>
</div>
But now I'm receiving a error:
Encountered unknown tag 'position'.
And that's it..I'm not sure of how to make this dropdown list change the table dynamically. Any insights 'd be aprecciated.
UPDATE//Giving more info:
Driver is a very simples dict/json that has two keys ['name'] and ['_id']
racesList is an array of many "raceItem"s.
Each "raceItem" has a key anmed 'finalresult' that has data of the positions each driver has finished.
this array is filled with many objects it may seem like the example below:
'finalresult': [
{'position': 1,
'drivername': 'John Doe'},
'position': 2,
'drivername': 'Foo Bazz', etc
}]
As I told before, I want this data to be displayed in a table, depending on what RACE has been selected from the "raceList" dropdown list.
As a solution, I'm thinking of creating a new routing...but I wanted this routing to be displayed in the same page. I don't want the user to be directed to a new window.

First of all, you are getting Encountered unknown tag 'position' as you are using a different data structure. As you mentioned finalresult is a list of dicts. So you need to iterate through it when populating your table.
{% for driver_stat in race.finalresult %}
<td>{{driver_stat.position}}</td>
<td>{{driver_stat.drivername}}</td>
{% endfor %}
But with your approach, this table will not be updated dynamically as you select different race from your dropdown. Instead I suggest to use jquery. So you don't have to nevigate to another page to display the driver stats. I found several useful SO question that have been already answered on this. Follow them.
Ref:
Populate table based in select
Populate HTML table based on second dropdown selection

Related

INDEX only pulling one row of data from database

For the life of me I cannot get my index page to properly display the data from my database. I have tried multiple methods and research on several sites. I'm thinking it is something right in front of my face, but right now I can't see the forest through the trees. I have included my code below.
Application:
#app.route("/")
#login_required
def index():
users = db.execute("SELECT * FROM users WHERE id = :user_id", user_id=session["user_id"])
stocks = db.execute("SELECT * FROM transactions WHERE id = :user_id", user_id=session["user_id"])
quotes = {}
for symbol in stocks:
quotes[symbol["symbol"]] = lookup(symbol["symbol"])
return render_template ("index.html", quotes=quotes,stocks=stocks, symbol=symbol)
index.html:
{% extends "layout.html" %}
{% block title %}
Portfolio
{% endblock %}
{% block main %}
<table class="table table-striped">
<tr>
<th> Symbol</th>
<th> Current Share Price</th>
<th> Shares Owned</th>
<th> Shares Owned Value</th>
</tr>
{% for stock in stocks %}
<tr>
<td>{{symbol.symbol}}</td>
<td>{{quotes[stock.symbol]["price"] | usd }}</td>
<td>{{symbol.shares}}</td>
<td>{{symbol.total_price}}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
Finally here is a screenshot of my index.html as it generates now:
And the data in my database table:
As you can see, the table displays the symbol, shares and total price from only one of the transactions. It shows the correct updated price for each stock, but I can't get it to pull the correct data from the table. Thank you so much for any help.
symbol has the value of the last iteration of for symbol in stocks:. Looks like the html should be creating the td elements from stock ({% for stock in stocks %}) not from symbol.
Suggestion to simplify:
In the for symbol in stocks: loop in the .py, add a key to the symbol dictionary for price. That would render the quotes object unnecessary and the stocks list would be self contained.

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)

Capture values from multiple select form and POST via Flask [duplicate]

This question already has answers here:
Converting Flask form data to JSON only gets first value
(2 answers)
Closed 4 years ago.
I need to capture multiple select form vlaue (generated from a MongoDB collection ) and POST via a Flask route to another MongoDB collection: recipes
Below is the relevant form div:
<form action="{{ url_for('insert_recipe') }}" method="POST" class="col s12">
...
<div class="input-field col s6 l6">
<i class="material-icons prefix">warning</i>
<select multiple id="allergen_name" name="allergenlist">
<option value="" disabled selected>Choose allergens</option>
{% for allergen in allergens %}
<option value="{{allergen.allergen_name}}">{{allergen.allergen_name}}</option>
{% endfor %}
</select>
</div>
</div>
...
</form>
I want to capture the selected options and POST them via Flask:
# Get all recipes
#app.route('/get_recipes')
def get_recipes():
return render_template("recipes.html",
recipes=mongo.db.recipes.find())
# Render HTML form
#app.route('/add_recipe')
def add_recipe():
return render_template('addrecipe.html',
users=mongo.db.users.find(),
allergens=mongo.db.allergens.find(),
cuisines=mongo.db.cuisine.find(),)
# Send the form
#app.route('/insert_recipe', methods=['POST'])
def insert_recipe():
recipes = mongo.db.recipes
recipes.insert(request.form.to_dict())
return redirect(url_for('get_recipes'))
However, only the first selected option is being captured and sent.
Any help would be appreciated.
EDIT:
When looking at: http://werkzeug.pocoo.org/docs/0.12/datastructures/#werkzeug.datastructures.MultiDict.to_dict
... relaised that I need to set to_dict(flat=false) in order to have all values of dict returned.
See EDIT above, the correct way is to:
# Send the form
#app.route('/insert_recipe', methods=['POST'])
def insert_recipe():
recipes = mongo.db.recipes
recipes.insert_one(request.form.to_dict(flat=False))
return redirect(url_for('get_recipes'))
Also, just found a duplicate as notified by #davidism :
Converting Flask form data to JSON only gets first value

What is the best way to check for nth element in Twig loop?

So currently the codes below do the following:
class "custom-border-right-blog" if item is in the first column or second column in a 3 column row
class "last" if item is the last in the loop.
<div class="large-4 medium-12 small-12 columns {% if (loop.index is divisibleby(3) == false) %}custom-border-right-blog{% endif %}{% if loop.last %} last {% endif %}">
Is there a cleaner way to achieve this? I am quite new to twig and the way I am doing this right now seems rather messy to me.
You can use the cycle twig function to apply a class according to a modulo. This is really useful when you are applying different classes for odd and even rows. In your case, this would look like:
class="(...) {{ cycle(['custom-border-right-blog', 'custom-border-right-blog', ''], loop.index) }} (...)"
Anyway, in your specific case, your way to apply classes looks the best to me.
In terms of shortening the code for better readability i would supoose this one:
<div class="large-4 medium-12 small-12 columns {{ (loop.index % 3) ? 'custom-border-right-blog' : 'last' }}">
UPDATE:
More flexible way in case you really just need the class on the last element:
<div class="large-4 medium-12 small-12 columns {{ (loop.last) ? 'last' : 'custom-border-right-blog' }}">

Add "items per page" drop down box in SHOPIFY

I'm building a web store on shopify. I want to add a drop down box on a page that displays my products where I can select number of items to display per page (i.e. 25, 50, 100).
Any suggestions on where to start with this and if there are any shortcodes in shopify that will help me do this?
Here is the solution for the collection page; try the following code:
<div class="sort-per-page">
<label for="sel1">Show per page:</label>
<select id="sel1" class="num">
<option value="/collections/{{ collection.handle }}?view=9" {% if limit == 9 %}selected="selected"{% endif %}>9</option>
<option value="/collections/{{ collection.handle }}?view=12" {% if limit == 12 %}selected="selected"{% endif %}>12</option>
<option value="/collections/{{ collection.handle }}?view=24" {% if limit == 24 %}selected="selected"{% endif %}>24</option>
<option value="/collections/{{ collection.handle }}?view=36" {% if limit == 36 %}selected="selected"{% endif %}>36</option>
</select>
</div>
<script>
$(document).ready(function(){
jQuery('.sort-per-page select.num').on('change', function(){
window.location.replace(jQuery(this).val());
});
});
</script>
One thing is for sure. You will be dealing with Collections. When you render a collection, you choose the number of Products to show. Further Shopify has Pagination you setup and control.
You can thus render your selection mechanism however you choose by enacapsulating the built-in Liquid Collection rendering variables.
Alternatively, you can render ALL the products in a collection into some client-side data structure (that could take some time depending on the number of products) and then render any number of them using your own algorithms and ideas.

Resources