I am working on a flask app that gets data from a database and should render this in on a page template. It is working okish, but there is the request to display data differently. I am now looking how I can do this best.
The data is an example, my data set has up to 20 columns and more than 1000 rows
Assume the SQL table looks like this:
State | City | Population |
---|---|---|
Alabama | Huntsville | 220k |
Alabama | Montgomery | 199k |
Alabama | Birmingham | 198k |
Alabama | Mobile | 185k |
Alaska | Anchorage | 288k |
Alaska | Fairbanks | 33k |
Alaska | Juneau | 32k |
Arizona | Phoenix | 1,625k |
Arizona | Tucson | 543k |
Arizona | Mesa | 509k |
The data is going directly to a dict which is passed to jinja2
cursor.execute("SELECT State , City , Population from table")
result = cursor.fetchall()
return render_template('template.html', results = results)
in Jinja2 it looks like this:
{% for result in results %}
<tr>
<td>{{result['State']}}</td>
<td>{{result['City']}}</td>
<td>{{result['Population']}}</td>
</tr>
{% endfor %}
Now the request is to deliver the output in div with a structure like this:
Alabama
-- Huntsville 220k
-- Montgomery 199k
-- Birmingham 198k
Alaska
-- Anchorage 228k
-- Fairbanks 33k
-- Juneau 32k
The jinja loop could be something link this
{% for states in results %}
<div>
<div>{{result['State']}}</div>
<div>
{% for cities in results %}
{% if states['State'] == cities ['State'] %}
<div>--</div>
<div>{{result['City']}}</div>
<div>{{result['Population']}}</div>
{% endif %}
{% endfor %}
</div>
</div>
{% endfor %}
what is the best way to loop this dictionary again? Would it be better if the data would be prepared differently before putting into the template?
I am not looking for the exact code, but some input what could be a great approach.
CodePudding user response:
You don't need that nested loop, you can use the loop.nextitem
and loop.previtem
special variable to asses if you need to display the state or not:
{% for result in results | sort(attribute="State") %}
{% if not loop.previtem or loop.previtem.State != result.State %}
<div>
<div>{{ result.State }}</div>
<div>
{% endif %}
<div>--</div>
<div>{{ result.City }}</div>
<div>{{ result.Population }}</div>
{% if not loop.nextitem or loop.nextitem.State != result.State %}
</div>
</div>
{% endif %}
{% endfor %}
Another way would be to, first, select the unique states, with the unique
filter, and then, only select the elements having those state, with the selectattr
filter:
{% for state in results | unique(attribute="State") %}
<div>
<div>{{ state.State }}</div>
<div>
{% for city in results | selectattr('State', '==', state.State) %}
<div>--</div>
<div>{{ city.City }}</div>
<div>{{ city.Population }}</div>
{% endfor %}
</div>
</div>
{% endfor %}
CodePudding user response:
I think it would be simpler first group data and lated send it to template.
You may use itertools.groupby(data, key)
to group it
groups = itertools.groupby(results, lambda row:row['State'])
Minimale working example
results = [
{'State': 'Alabama', 'City': 'Huntsville', 'Population': '220k'},
{'State': 'Alabama', 'City': 'Montgomery', 'Population': '199k'},
{'State': 'Alabama', 'City': 'Birmingham', 'Population': '198k'},
{'State': 'Alabama', 'City': 'Mobile', 'Population': '185k'},
{'State': 'Alaska', 'City': 'Anchorage', 'Population': '288k'},
{'State': 'Alaska', 'City': 'Fairbanks', 'Population': '33k'},
{'State': 'Alaska', 'City': 'Juneau', 'Population': '32k'},
{'State': 'Arizona', 'City': 'Phoenix', 'Population': '1,625k'},
{'State': 'Arizona', 'City': 'Tucson', 'Population': '543k'},
{'State': 'Arizona', 'City': 'Mesa', 'Population': '509k'}
]
#print(results)
import itertools
groups = itertools.groupby(results, lambda row:row['State'])
# conver only to display it in one line but `for`-loop doesn't need it
#groups = [(state, list(data)) for state, data in groups]
#print(groups)
# run similar loops in template
for state, data in groups:
print(state)
for item in data:
print('---', item['City'], item['Population'])
Results:
Alabama
--- Huntsville 220k
--- Montgomery 199k
--- Birmingham 198k
--- Mobile 185k
Alaska
--- Anchorage 288k
--- Fairbanks 33k
--- Juneau 32k
Arizona
--- Phoenix 1,625k
--- Tucson 543k
--- Mesa 509k