I am trying to access data from a parent to a child via a foreign key.
WHAT WORKS – the views
The data in the child is not "ready to be used" and need to be processed, to be represented in a progress bar in %.
The data processing is handled in the views. When I print it on the console, it seems to work and stored into a variable reward_positions
.
Reward positions = [(<Venue: Venue_name>, reward_points, reward_position_on_bar)]
So this part works.
The plan is therefore to access reward_position_on_bar
by calling {{reward_positions.2}}
WHAT DOESNT WORK – the template
But something is not working to plan in the template.
The template renders the last child_model
(thats rewardprogram) objects of the last parent_id
(thats venue) irrespective of the actual parent_id
processed in the for loop.
TEST RESULT & WHERE I THINK THE PROBLEM IS
I think my problem lies in my nested forloop. The parent_id
in the parent forloop does not match the ‘{{reward_position.0}}’ in the nested forloop.
Doing a verification test, {{key}}
should be equal to {{reward_position.0}}
as they both go through the same parent forloop.
However, if {{key}}
does change based on venue.id (parent forloop id), {{reward_position.0}}
is stuck to the same id irrespective of the parent forloop id.
Can anyone seem what I am doing wrong?
THE CODE
models
class Venue(models.Model):
name = models.CharField(verbose_name="Name",max_length=100, blank=True)
class RewardProgram(models.Model):
venue = models.ForeignKey(Venue, null = True, blank=True, on_delete=models.CASCADE, related_name="venuerewardprogram")
title = models.CharField(verbose_name="reward_title",max_length=100, null=True, blank=True)
points = models.IntegerField(verbose_name = 'points', null = True, blank=True, default=0)
views
def list_venues(request):
venue_markers = Venue.objects.filter(venue_active=True)
#Progress bar per venue
bar_total_lenght = 100
rewards_available_per_venue = 0
reward_position_on_bar = 0
venue_data = {}
reward_positions = {}
for venue in venue_markers:
print(f'venue name ={venue}')
#list all reward programs
venue.reward_programs = venue.venuerewardprogram.all()
reward_program_per_venue = venue.reward_programs
#creates a list of reward points needed for each venue for each object
reward_points_per_venue_test = []
#appends the points to empty list from reward program from each venue
for rewardprogram in reward_program_per_venue:
reward_points_per_venue_test.append(rewardprogram.points)
#sorts list in descending order
reward_points_per_venue_test.sort(reverse=True)
#set position of highest reward to 100 (100% of bar length)
if reward_points_per_venue_test:
highest_reward = reward_points_per_venue_test[0]
if not reward_program_per_venue:
pass
else:
#counts reward program per venue
rewards_available_per_venue = venue.reward_programs.count()
if rewards_available_per_venue == 0:
pass
else:
#position of reward on bar
reward_positions = []
for rewardprogram in reward_program_per_venue:
#list all points needed per reward program objects
reward_points = rewardprogram.points
#position each reward on bar
reward_position_on_bar = reward_points/highest_reward
reward_positions.append((venue, reward_points, reward_position_on_bar))
#reward_positions[venue.id] = reward_position_on_bar
reward_positions = reward_positions
print(f'Reward positions = {reward_positions}')
context = {'reward_positions':reward_positions,'venue_data':venue_data,'venue_markers':venue_markers}
return render(request,'template.html',context)
template
{%for venue in venue_markers%}
{%for key, value in venue_data.items%}
{%if key == venue.id%} #venue.id = 3
{% for reward_position in reward_positions %}#test result
{{reward_position.0.id}} # = id = 7 (thats not the good result)
{{key}} #id = 3 (thats the good result)
{% endfor %}
<div class="progress-bar bg-success" role="progressbar" style="width: {{value}}%" aria-valuenow="{{value}}" aria-valuemin="0" aria-valuemax="100"></div>
{%endif%}
{%endfor%}
{%endfor%}
2
Answers
You are overcomplicating things, which makes the template very complex (which often will only introduce extra bugs), and makes it even quite slow because of an N+1 problem in the view.
then in the template, we can render this as:
this will thus take the
.position_on_bar
attributes we added to therewardProgram
s that we prefetched for theVenue
s.The context only contains the rewards of the last venue because you are overriding
reward_positions
in every loop.