Reputation: 820
I have sorted list of cities called sorted_cities=[Atlanta, Berlin, Bern, Calgary] that I am sorting in alpha order and would like to display on the web with ability to click on the city to get detailed information as well as select city to get notifications via e-mail. I want the output on the web look as follows:
A
Atlanta
B
Berlin
Bern
C
Calgary
I have the following code in Python 3.x that works fine:
sorted_names=sorted(names,key=str.lower)
print(sorted_names)
for letter, words in groupby(sorted_names,key=itemgetter(0)):
print(letter)
for word in words:
print(word)
I created this code in Jinja2 but it does not work:
{% block content %}
<div>
<form action="cities" method="post">
{% for group in sorted_names|groupby("letter") %}
<li><b>{{ group.grouper }} </b></li>
{% for word in group.list %}
<input type="checkbox" name="city" value="{{ word }}"> <a href="/{{ word }}">{{ word }}</a><br>
{% endfor %}
{% endfor %}
<input type="submit" value="Submit">
</form>
</div>
{% endblock %}
Upvotes: 0
Views: 1721
Reputation: 4670
The default filter groupby
in jinja2 need the attribute to group your items. Howerver, you give the 'letter' attribute but jinja2 can't understand it since your names
list don't have such attribute.
groupby(value, attribute)
Group a sequence of objects by a common attribute.
To reuse your python code, you can register a filter to jinja2 via [template_filter
](http://flask.pocoo.org/docs/dev/api/#flask.Flt’s now possible to use dotted notatask.template_filter). So, you can try this way:
from itertools import groupby
from operator import itemgetter
from flask import Flask, render_template
app = Flask(__name__)
@app.template_filter()
def groupby_letter(iterable):
return groupby(sorted(iterable), key=itemgetter(0))
@app.route('/')
def index():
names = ['Atlanta','Calgary','Berlin','Austin']
return render_template('index.html', names=names)
if __name__ == '__main__':
app.run(debug=True)
and use the filter in your template like this:
{% for key, group in names|groupby_letter %}
<li><b>{{ key }}</b></li>
{% for word in group %}
<p>{{ word }}</p>
{% endfor %}
{% endfor %}
Yet another way is that you need do something to your names
if you still want to use the groupby
filter. We need to build a list dicts and each dict contains a 'first_letter' key, we will use it as the group attribute when using groupby
filter.
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
names = ['Atlanta','Calgary','Berlin','Austin']
name_dicts = [{'first_letter': name[0], 'name': name} for name in names]
return render_template('index.html', name_dicts=name_dicts)
if __name__ == '__main__':
app.run(debug=True)
and use the groupby
filter in your template like this:
{% for group in name_dicts|groupby('first_letter') %}
<li><b>{{ group.grouper }}</b></li>
{% for item in group.list %}
<p>{{ item.name }}</p>
{% endfor %}
{% endfor %}
Upvotes: 1
Reputation: 2769
Here's a quick test that gets the basic functionality you are looking for. I piggy backed off of @1844144's comment. But basically just append the groupby elements to an array on the python side and pass it through to jinja. Then in jinja I do a not so clever conditional thing with a jinja filter (Length of string in Jinja/Flask)
app.py:
from flask import Flask, render_template
from itertools import groupby
from operator import itemgetter
app = Flask(__name__)
@app.route('/')
def main():
names = ['Atlanta','Calgary','Berlin','Austin']
sorted_names = sorted(names, key=str.lower)
grouped = []
for letter, words in groupby(sorted_names,key=itemgetter(0)):
grouped.append(letter)
for word in words:
grouped.append(word)
return render_template('index.html', grouped=grouped)
if __name__ == '__main__':
app.run(debug=True)
templates/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% for name in grouped %}
{% if name|length == 1 %}
{{name}}<br>
{% else %}
<a href="/"> {{name}} </a><br>
{%endif%}
{% endfor %}
</body>
</html>
Here's how it looks in the browser:
Upvotes: 1