Get nested dict items using Jinja2 in Flask - python

for this dictionary with this Flask controller
projects = {
'life-calc':{'url':'life-calc',
'title': 'Life Calculator'},
'text-game':{'url':'text-game',
'title':'Text Adventure'},
'fill-it-up':{'url':'fill-it-up',
'title':'Fill It Up'},
'rock-paper-scissors':{'url':'rock-paper-scissors',
'title':'Rock, Paper, Scissors'},
'bubble-popper':{'url':'bubble-popper',
'title':'Bubble Popper'}
}
#app.route('/')
def index():
return render_template("index.html",
projects = projects)
and the template as such
<h1>
List of My Projects
</h1>
<ol>
<li>
Life Calculator
</li>
<li>
Adventure Game
</li>
<li>
Fill It Up
</li>
<li>
Rock Paper Scissors
</li>
<li>
Bubble Popper
</li>
</ol>
<p>test section below</p>
<ol>
{% for project in projects %}
<li>{{ project['title'] }} </li>
{% endfor %}
</ol>
{% endblock %}
How can I access the items in the dict to print a list of my projects as in the HTML above the test?
I solved my own problem with help from Rendering a python dict in Jinja2 / Werkzeug
The template block should be
{% for key, value in projects.iteritems() %}
<li><a href={{value['url']}}>{{value['title']}}</a></li>
{% endfor %}
But I'm still curious as to how to access further nested dictionaries, and if this is the smartest way to create a simple menu.

I think you want to know how access the nested dict in template
If you think I got your question
Generally, This is the way to access the nested dictionary items in dictionary.
If the iterables are getting nested further just you have to increase the forloop depth level whether it is list or dict.
Here I am giving just a generic example in my own way for your understanding
Data:
parent_dict = {1: {'A':'val1','B':'val2'}, 2:{'C':'val3','D':'val4'}}
iteration in jinja2:
{% for key,parent_dict_item in parent_dict.items() %}
{% for key2, nested_value in parent_dict_item.items() %}
<li>{{ nested_value }} </li>
{% endfor %}
{% endfor %}
Answer:
<li>val1 </li>
<li>val2 </li>
<li>val3 </li>
<li>val4 </li>

Instead of expanding the key and value in the loop, you can also use the key to reference the item in the dict itself:
{% for project in projects %}
<li>{{ projects[project].title }} </li>
{% endfor %}

Related

how to use a django variable in an <a> tag's href?

I want to create a list of entries in which each entry is linked to its page like this: wiki/entry-title. I'm using a for loop to add <li>s to HTML. here's the code:
<ul>
{% for entry in entries %}
<li>{{ entry }}</li>
{% endfor %}
</ul>
urlpattern:
path('wiki/<str:title>', views.entry, name='entry')
what should I type in href to link the <li> to wiki/entry?
You can set your value in url as
<ul>
{% for entry in entries %}
<li>{{ entry }}</li>
{% endfor %}
</ul>
Its better to use {% url %} [Django-doc] template tags as
<ul>
{% for entry in entries %}
<li>{{ entry }}</li>
{% endfor %}
</ul>
NOTE : change value with your value accordingly. For e.g. {{entry.value}} or {{entry.title}} or {{entry.id}}
There is a clear example in the django docs
<ul>
{% for yearvar in year_list %}
<li>{{ yearvar }} Archive</li>
{% endfor %}
</ul>
In django, the template parser always handles django code first, then html/javascript. So you would insert a django variable into an anchor tag the same way you'd insert it anywhere in the template and the parser will replace it before it tries to render the html. If it's a django url, you can use the {% %} format as referenced in the previous answer, and if it's a url that's perhaps stored on the object, you can just use {{ }} (like {{ entry.wiki_url }}). You can also use text for some of the url and a variable for part. So if you have a wiki site that has a base url of, for instance, https://mywiki.com you'd write the href like:
<ul>
{% for entry in entries %}
<li>{{ entry.title }}</li>
{% endfor %}
</ul>
I found out that the only solution that was working for me was this format
{{ x }}
x: this is variable that we want to implement inside href attribute
'url_path_name' : this is the name of the url we gave in urls.py , see :
path("wiki/<str:name>" , views.entry, name="url_path_name")
So this defined url should look like this:
wiki/ ?
href="{% url 'url_path_name' x %}"

Render django formset with two forms per row [duplicate]

I need to represent collection in the template and wrap every four elements in the
<li></li>
The template should be like this:
<ul>
<li>
<a></a>
<a></a>
<a></a>
<a></a>
</li>
<li>
<a></a>
<a></a>
<a></a>
<a></a>
</li>
<li>
<a></a>
<a></a>
<a></a>
<a></a>
</li>
</ul>
So i need to do it in the {% for %}
{% for obj in objects %}
{#add at 1th and every 4th element li wrap somehow#}
<a>{{object}}</a>
{# the same closing tag li#}
{% endfor %}
The following should solve your problem, using built-in template tags :
<ul>
<li>
{% for obj in objects %}
<a>{{ obj }}</a>
{# if the the forloop counter is divisible by 4, close the <li> tag and open a new one #}
{% if forloop.counter|divisibleby:4 %}
</li>
<li>
{% endif %}
{% endfor %}
</li>
</ul>
You can use the divisibleby tag as mentioned before, but for template clearing purposes I usually prefer a helper function that returns a generator:
def grouped(l, n):
for i in xrange(0, len(l), n):
yield l[i:i+n]
example simplistic view:
from app.helpers import grouped
def foo(request):
context['object_list'] = grouped(Bar.objects.all(), 4)
return render_to_response('index.html', context)
example template:
{% for group in object_list %}
<ul>
{% for object in group %}
<li>{{ object }}</li>
{% endfor %}
</ul>
{% endfor %}
you can use divisibleby built-in filter, here is link to django documentation
so something like this would work
{% if value|divisibleby 4 %}
#your conditional code
{% endif %}
if you want to work it with checking first forloop and last forloop you could use this :
<ul>
{% for obj in objects %}
{% if forloop.first %}
<li>
{% endif %}
<a>{{obj}}</a>
{% if forloop.counter|divisibleby:4 and not forloop.first %}
</li>
<li>
{% endif %}
{% if forloop.last %}
</li>
{% endif %}
{% endfor %}
</ul>
I personally would consider to separate the elements in the view before passing them to the template and then using nested for loops. Except this you really only have the filter or templatetag option as Vaibhav Mishra mentioned.

Flask Jinja Route

What is the correct syntax for the href on an html page with Jinja2 code, that allows for navigation between two pages? The first html template has a list of names, while the other has person details.
Here is the code I have for the listOfNames.html page that displays the list of names.
<ul>
{% for rownum, row in listNames.iterrows() %}
<li>{{ row.firstName }} {{ row.lastName }}</li>
{% endfor %}
</ul>
Here is the server.py code that gets/puts (correct use of term?) the names on the listOfNames.html.
#app.route('/listNames/<bo>/')
def listNames(bo):
listNames = getListNames(bo)
return render_template('listOfNames.html', listNames=listNames)
This is code for the personInformation.html.
<main role="main" class="col-sm-9 ml-sm-auto col-md-10 pt-3">
<h1>{{ person.firstName }}
{{ person.lastName }}
</h1>
<h2>Office:
{{ person.bo }}
</h2>
<h2>Courses Completed
</h2>
<ul>
{% for rownum, row in personCompleted.iterrows() %}
<li><a href="/courses/{{row.courseId}}">
{{row.courseTitle}}
</a></li>
{% endfor %}
</ul>
</main>
And here is the server.py code.
#app.route('/people/<person_id>')
def person(person_id):
person = getPersonOrganization(person_id)
personCompleted = getPersonCompleted(person_id)
return render_template('personInformation.html', person=person, personCompleted=personCompleted)
The best way to do this is by using the url_for() function from Flask. Here is your new listOfPeople.html template with the link:
<ul>
{% for rownum, row in listNames.iterrows() %}
<li>{{ row.firstName }} {{ row.lastName }}</li>
{% endfor %}
</ul>
It's best to not hardcode your URLs in the templates, because if you ever need to reorganize them, then you would need to update URLs all over the place. With url_for() Flask takes care of generating the URLs for you using the information you provided in the app.route decorators.

Django/Python HTML recommend friends

So I'm trying to recommend users for a social network based on this condition: If A follows B and B follows C then, we should recommend that A follows C.
I have this code so far which displays mutual friends, all users and those that follow you
<ul>
{% for member in members %}
<li> {{ member.username }}
{% if member in following %}
{% if member in followers %}
↔ is a mutual friend [drop] </li>
{% else %}
← you are following [drop] </li>
{% endif %}
{% else %}
{% if member in followers %}
→ is following you [recip] </li>
{% else %}
[follow] </li>
{% endif %}
{% endif %}
{% endfor %}
</ul>
But I'm stuck on doing this condition so any help would be great, thanks!
Django templates should not encapsulate overly complex logic, by design. They are mainly a design tool, and the main logic should run in python. This is why there are clear limits what you can do in a template without too much hoops.
Here is an simple option, how to implement your requirements
def get_status(user,member):
... calculate follower status here ...
return msg,href,link_text
In the view, where you run your queryset:
members = Members.objects.all()
for member in members:
member.msg,member.href,member.link_text = get_status(member,request.user)
Finally, in the template:
<ul>
{% for member in members %}
<li> {{ member.username }}
↔ {{ member.msg }} [{{ member.link_text }}] </li>
{% endfor %}
</ul>
Note: this is a simple answer. You should really think through this logic, where you want to put it (in the model maybe, as Member method), use reverse for urls etc. But the idea is not to put basic app rules in the template

How can I Iterate through the same dictionary multiple times in a Django template?

I have a dictionary called categories. And I just want to iterate through this dictionary twice in a Django template. Below is my code:
<div class="first">
{% for category in categories %}
<li class="{{category.name}}">{{category.name}</li>
{% endfor %}
</div>
<div class="lenDict">
<h2>{{categories|length}}</h2>
</div>
<div class="second">
{% for category in categories %}
{% for facet in allFacets %}
{% if category.name == facet.category %}
<p id="{{facet.id}}">{{facet.facet}}</p>
{% endif %}
{% endfor %}
{% endfor %}
</div>
When I do it like this the first loop under the div first works fine.
But when it comes to the second loop under the div second it gives no result.
Also the code under the div lenDict also gives no result.
Are there any limitations in Django templates that we cannot iterate the same dictionary twice?
Any help will be appreciated.
To iterate through an entire dict in Python, you should use .iteritems(). It creates a new iterator over the (key, value) pairs of the dict. You could also use items() if you wanted a list of items instead.
<div class="first">
{% for key, value in categories.iteritems() %}
<li class="{{ key }}">{{ value }}</li>
{% endfor %}
</div>
<div class="lenDict">
<h2>{{ categories|length }}</h2>
</div>
<div class="second">
{% for key, value in categories.iteritems() %}
{% for facet in allFacets %}
{% if key == facet.category %}
<p id="{{facet.id}}">{{facet.facet}}</p>
{% endif %}
{% endfor %}
{% endfor %}
</div>
Note that your dict will be unordered. If you want to preserve the orders of the keys, use an OrderedDict.
So I think this works. I think your issue is facet.category. Is this a fk? because if this is a fk the if statement should be
{% if category.id == facet.category %}
In python iterators don't need to be reset, so I don't think you have to reset your iterators in the tamplate.
Note: I would leave this is in a comment but rep isn't high enough.

Categories

Resources