Django. Access list index in child for loop - python

How can I change the accessed index of an inner for loop list based on a counter from the outer loop? In normal python I would do something like
parent_list = ['One','Two','Three']
child_list = ['A','B','C']
for idx, item in enumerate(parent_list):
for child_item in child_list[idx]:
print(str(idx), item, child_item)
I've been looking at using with and forloop.counters but I either run into the index not being accessed or index not changing. This is what I currently have.
{% for item in payout_items %}
{% with forloop.counter0 as outer_counter %}
<h2>{{ item.market_place }} on {{ item.entry_date }}:
{% for item in royalty_items.outer_counter %}
<tr>
<th scope="row">{{ item.id }}</th>
<td>{{ item.entry_date }}</td>
<td>{{ item.market_place }}</td>
</tr>
{% endfor %}
{% endwith %}
{% endfor %}
If I change
{% for item in royalty_items.outer_counter %}
to
{% for item in royalty_items.0 %}
I get the first index repeated many times. I can see that outer_counter is incrementing from the output but just need royalty_items to increment the accessed index as well.
As requested the view is
detail_object = AccountingPeriod.objects.get(pk=detail_id)
payout_items = Payout.objects.filter(sales_period_start__range=[detail_object.period_start, detail_object.period_end])
royalty_items = []
for payout in payout_items:
temp = Royalty.objects.filter(entry_date__range=[payout.sales_period_start, payout.sales_period_end]).filter(market_place=payout.market_place)
print(str(payout.sales_period_start) + " - " + str(payout.sales_period_end) + " - " + payout.market_place + " = " + str(len(temp)))
royalty_items.append(temp)
And the following render call is passed.
render(request, 'royalties/accounting_period/summary.html', {'detail_object': detail_object, 'payout_items': payout_items, 'royalty_items': royalty_items})
Solution: I created a template filter but feel there should be a more elegant answer.
#register.filter()
def getRoyaltySet(royalty_items, outer_counter):
return royalty_items[outer_counter]

I would just add this to the items, so:
detail_object = get_object_or_404(AccountingPeriod, pk=detail_id)
payout_items = Payout.objects.filter(
sales_period_start__range=(
detail_object.period_start,
detail_object.period_end,
)
)
for payout in payout_items:
temp = Royalty.objects.filter(
entry_date__range=(payout.sales_period_start, payout.sales_period_end),
market_place=payout.market_place,
)
print(
f'{payout.sales_period_start} - {payout.sales_period_end} - {payout.market_place} = {len(temp)}'
)
payout.royalities = temp
Then you can access this through .royalities:
{% for item in payout_items %}
<h2>{{ item.market_place }} on {{ item.entry_date }}</h2>:
{% for subitem in item.royalites %}
<!-- … -->
{% endfor %}
{% endfor %}

You can use forloop.parentloop to get to the outer forloop
{{ forloop.parentloop.counter }}
You can even chain them if you have nested loops:
{{ forloop.parentloop.parentloop.counter }}
If you wanna absolutely use your own index you can access the dictionary with .items:
{% for key, value in mydict.items %}
{% for key2, value2 in otherdict.items %}
<div>{{ key }} - {{ key2 }}</div>
{% endfor %}
{% endfor %}

Related

Streaming an iterable and other variable in Flask using Jinja

I have a long process that I managed to stream into a Jinja template, but now I would like to show not only results but also that could be viewed by the user as a meaning of progress.
This is my current code: it iterates over a huge collection of items, some of which produce results and others do not. I only want to show the items that match the search.
The rendering part:
lista_pantallas = buscar_componente_stream_generate(componente, atributo, valor)
return Response(stream_template('consultas/busqueda_comp.html',
lista_pantallas=lista_pantallas,
componente=componente, atributo=atributo, valor=valor,
error_msg=err_msg))
This is the way I generate the iterator:
def buscar_componente_stream_generate(componente, atributo, valor):
with uopy.connect(...) as session:
with uopy.File(...) as fapant:
pantallas_fmpant = uopy.List()
pantallas_fmpant.select(fapant)
functor = BuscadorObjetoAtributo(componente, atributo, valor)
for idx, pantalla in enumerate(pantallas_fmpant):
try:
if print_pant:
print(f'{idx} - Pantalla: {pantalla}')
procesar_pantalla(pantalla, functor)
for item in functor.lista_objetos():
yield item
functor.borrar_objetos()
except Exception as ex:
print('{0} - {1} - {2}'.format(idx, pantalla, str(ex)))
And the Jinja2 template
{% if lista_pantallas %}
<h1>Lista de pantallas</h1>
<h2>Condición: {{ componente }}.{{ atributo }} = {{ valor }}</h2>
<h2>Ultima pantalla procesada: {{idx}} - {{pantalla}}</h2>
<table>
<thead>
<th>Pantalla</th>
<th>Atributo</th>
</thead>
<tbody>
{% for item in lista_pantallas %}
{% if loop.index0 is even() %}
{% set par_css = 'par' %}
{% else %}
{% set par_css = 'impar' %}
{% endif %}
<tr class={{ par_css }}>
<td>{{ item['fichero'] }}</td>
<td>{{ item['prop'] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
How can I refresh the template with the values of the variables idx and pantalla?

Python Jinja2 filtering

if my table is like this
how can I output the number of events with the same name, like test should be 5 and hello should be 3.
Edit:
Here's my Jinja2 code snippet
{% for event in events %}
{% set count = 0 %}
<tr>
<td>{{ event.name }}</td>
{% for ticket in tickets %}
{% if ticket.event_name == event.name%}
{% set count = count + 1 %}
{% endif %}
{% endfor %}
<td>{{count}}<td>
<td>
But its not counting right
Never mind, I was able to solve it.
{% for event in events %}
{% set count = namespace(value=0) %}
<tr>
<td>{{ event.name }}</td>
{% for ticket in tickets %}
{% if ticket.event_name == event.name%}
{% set count.value = count.value + 1 %}
{% endif %}
{% endfor %}
<td>{{count.value}}<td>
<td>

How can i access a jinja2 variable outside the for loop?

im trying to use a for loop to add up some numbers for each day
and i would like to access the variable outside the for loop im not sure how to go about this I am using the flask framework with python and just come from weppy where this was not a problem is there a way to make it work the same way in flask?
here is some simple code
{% set newtotal = 0 %}
{% for item in daily: %}
{% set newtotal = newtotal + item[10]|float %}
{% endfor %}
<div class="bottom">
<span>Total: {{ newtotal }}</span>
</div>
the numbers collected by item[10] are dollar values
if i place the {{ newtotal }} before the endfor it shows every value as its being added up this is not what I want
EDIT:
if it helps daily is a list of 8 tuples
Please keep in mind that it is not possible to set variables inside a block or loop and have them show up outside of it.
As of version 2.10 this can be handled using namespace objects which allow propagating of changes across scopes.
Here is your code using namespace:
{% set ns = namespace (newtotal = 0) %}
{% for item in daily: %}
{% set ns.newtotal = ns.newtotal + item[10]|float %}
{% endfor %}
<div class="bottom">
<span>Total: {{ ns.newtotal }}</span>
</div>
One solution (probably the "simplest") would be to change your Python script to pass in a variable named newtotal that would simply be the length of the daily list!
Alternatively, you could use the length filter:
{{things|length}}
In which case your code could look something like this:
{% set newtotal = 0 %}
{% for item in daily: %}
{% set newtotal = newtotal + item[10]|float %}
{% endfor %}
<div class="bottom">
<span>Total: {{daily|length}}</span>
</div>
Hope it helps!
Additional Sources:
jinja2: get lengths of list
How do I access Jinja2 for loop variables outside the loop?
EDIT
Sorry, I misread the question!
You can use the sum filter instead ({{ list | sum() }}).
So your code could look like:
{% set newtotal = 0 %}
{% for item in daily: %}
{% set newtotal = newtotal + item[10]|float %}
{% endfor %}
<div class="bottom">
<span>Total: {{ daily | sum() }}</span>
</div>
New sources:
Documentation
Sum elements of the list in Jinja 2
Use the namespace object.
https://jinja.palletsprojects.com/en/master/templates/#assignments
Here’s a working example from my config.
{% set i= namespace(fxp0_ip=0) %}
{% set i= namespace(mgmt_ip = 0) %}
{% set i= namespace(loopback_ip = 0) %}
{% set i= namespace(lan_ip = 0) %}
{% set i= namespace(wan_ip = 0) %}
{% for interface in device_vars.interfaces %}
{% elif interface.name == "ge-0/0/0" %}
{% set i.mgmt_ip = interface.ip_addr %}
{% elif interface.name == "lo0" %}
{% set i.loopback_ip = interface.ip_addr %}
{% elif interface.name == "ge-0/0/2" %}
{% set i.lan_ip = interface.ip_addr %}
{% elif interface.name == "ge-0/0/1" %}
{% set i.wan_ip = interface.ip_addr %}
{% endif %}
{% endfor %}
{{i.mgmt_ip}}
{{i.wan_ip}}

How to globally define a counter in jinja2 so that it will remember its last value

Here is my code. I want that after the ending of the for loop the count should remember its last value so that i can check whether it entered in the if clause under for or not. But here it is not remembering the last value and all the time print "no coupons available" along with the coupons even if it find coupons.
So what is the solution to do so??
{% set count = 1 %}
{% for x in coupon_codes %}
{% if x[2]=="example.com" %}
<tr>
<td><code>{{ x[0] }}</code></td>
<td>{{ x[1] }}</td>
{% set count = count + 1 %}
</tr>
{% endif %}
{% endfor %}
{% if count==1 %}
<b>{% print "No Coupons Available." %}</b>
{% endif %}
You can use the filtered for feature of Jinja2:
{% for x in coupon_codes if x[2]=="example.com" %}
<tr>
<td><code>{{ x[0] }}</code></td>
<td>{{ x[1] }}</td>
</tr>
{% else %}
<b>No Coupons Available.</b>
{% endfor %}

how to iterate through dictionary in a dictionary in django template?

My dictionary looks like this(Dictionary within a dictionary):
{'0': {
'chosen_unit': <Unit: Kg>,
'cost': Decimal('10.0000'),
'unit__name_abbrev': u'G',
'supplier__supplier': u"Steve's Meat Locker",
'price': Decimal('5.00'),
'supplier__address': u'No\r\naddress here',
'chosen_unit_amount': u'2',
'city__name': u'Joburg, Central',
'supplier__phone_number': u'02299944444',
'supplier__website': None,
'supplier__price_list': u'',
'supplier__email': u'ss.sss#ssssss.com',
'unit__name': u'Gram',
'name': u'Rump Bone',
}}
Now I'm just trying to display the information on my template but I'm struggling. My code for the template looks like:
{% if landing_dict.ingredients %}
<hr>
{% for ingredient in landing_dict.ingredients %}
{{ ingredient }}
{% endfor %}
Print {{ landing_dict.recipe_name }}
{% else %}
Please search for an ingredient below
{% endif %}
It just shows me '0' on my template?
I also tried:
{% for ingredient in landing_dict.ingredients %}
{{ ingredient.cost }}
{% endfor %}
This doesn't even display a result.
I thought perhaps I need to iterate one level deeper so tried this:
{% if landing_dict.ingredients %}
<hr>
{% for ingredient in landing_dict.ingredients %}
{% for field in ingredient %}
{{ field }}
{% endfor %}
{% endfor %}
Print {{ landing_dict.recipe_name }}
{% else %}
Please search for an ingredient below
{% endif %}
But this doesn't display anything.
What am I doing wrong?
Lets say your data is -
data = {'a': [ [1, 2] ], 'b': [ [3, 4] ],'c':[ [5,6]] }
You can use the data.items() method to get the dictionary elements. Note, in django templates we do NOT put (). Also some users mentioned values[0] does not work, if that is the case then try values.items.
<table>
<tr>
<td>a</td>
<td>b</td>
<td>c</td>
</tr>
{% for key, values in data.items %}
<tr>
<td>{{key}}</td>
{% for v in values[0] %}
<td>{{v}}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
Am pretty sure you can extend this logic to your specific dict.
To iterate over dict keys in a sorted order - First we sort in python then iterate & render in django template.
return render_to_response('some_page.html', {'data': sorted(data.items())})
In template file:
{% for key, value in data %}
<tr>
<td> Key: {{ key }} </td>
<td> Value: {{ value }} </td>
</tr>
{% endfor %}
This answer didn't work for me, but I found the answer myself. No one, however, has posted my question. I'm too lazy to
ask it and then answer it, so will just put it here.
This is for the following query:
data = Leaderboard.objects.filter(id=custom_user.id).values(
'value1',
'value2',
'value3')
In template:
{% for dictionary in data %}
{% for key, value in dictionary.items %}
<p>{{ key }} : {{ value }}</p>
{% endfor %}
{% endfor %}
If you pass a variable data (dictionary type) as context to a template, then you code should be:
{% for key, value in data.items %}
<p>{{ key }} : {{ value }}</p>
{% endfor %}
I am thankful for the above answers pointing me in the right direction. From them I made an example for myself to understand it better. I am hoping this example will help you see the double dictionary action more easily and also help when you have more complex data structures.
In the views.py:
bigd = {}
bigd['home'] = {'a': [1, 2] , 'b': [3, 4] ,'c': [5,6] }
bigd['work'] = {'e': [1, 2] , 'd': [3, 4] ,'f': [5,6] }
context['bigd'] = bigd
In the template.html:
{% for bigkey, bigvalue in bigd.items %}
<b>{{ bigkey }}</b> <br>
{% for key, value in bigvalue.items %}
key:{{ key }} <br>
----values: {{ value.0}}, {{value.1 }}<br>
{% endfor %}
<br>
{% endfor %}
Notice the list in the second dictionary is accessed by the index in the list.
Result in browser is something like:

Categories