Pass a graph per object as context data in a ListView

Hi everyone, glad to find a forum dedicated to Django :wink:

I´m learning Python & Django by working a project: a sports team management app. As of right now, it´s mainly a bunch of forms and tables, so I want to add some “fun” stuff to break this repetitive pattern. In comes graphs that show how the players line up on the field.

I am able to make the graph appear on my ListView, but it´s the same for all items in the list.

Can you please guide me on how to get the context data to be specific to the object ?

ListView:

class FormationList(LoginRequiredMixin, generic.ListView):
    model = PersonnelFormation
    template_name = 'playbook/formations_list.html'
    context_object_name = 'formations'
    paginate_by = 8
    
    def get_context_data(self,**kwargs):
        context = super(FormationList, self).get_context_data(**kwargs)
        plt.switch_backend('agg')
        for obj in context['object_list']: 
            #print(f'pk: {obj.pk}')
            form = PersonnelFormation.objects.filter(pk=obj.pk)
            pos_list = form.values_list('positions', flat=True)
            points_x = []
            points_y = []
            for k in pos_list:
                for v in k:
                    for i in v:
                        if i == 'position':
                            pos = PersonnelRolePosition.objects.get(name__exact=v[i])
                            points_x.append(pos.x_offset)
                            points_y.append(pos.y_offset)
            plt.scatter(points_x, points_y)
            plt.xlabel("X")
            plt.ylabel("Y")
            fig = plt.gcf()
            #convert graph into dtring buffer and then we convert 64 bit code into image
            buf = io.BytesIO()
            fig.savefig(buf,format='png')
            buf.seek(0)
            string = base64.b64encode(buf.read())
            uri =  urllib.parse.quote(string)
            context['data'] = uri
        return context
    

    def get_queryset(self):
        user_filter = self.request.user.pk
        return PersonnelFormation.objects.filter(user=user_filter)

Relevant template part:

{% for formation in formations %}
                            <tr>
                                <td class="px-4 py-3">{{formation.name}} ({{formation.code_name}})</td>
                                <td class="px-4 py-3">{{formation.description}}</td>
                                <td class="px-4 py-3">{{formation.personnel_group}}</td>
                                <td class="px-4 py-3">
                                    <img src="data:image/png;base64,{{ data }}" alt="" height="250" width="250">
                                </td> 
                            </tr>

Thanks a lot !

You’re creating multiple images, but storing them all in the same location (context['data']). You need to assign each image to a different location that you can iterate through in your template.
(example: You could create a dict where the key is the pk of the related Formation object and the value is the image.)

Hi, not sure I follow. I understand making a dict with the image, but I do refer to this in the template? I tried a few things, but I don´t see how to iterate with images.
I´m trying to pass this:

context[str(obj.pk)] = uri

In the template, this does not seem to work (test with hard value):

     {{plot1}}
or
     <img src="data:image/png;base64,{{ formation.pk}}" alt="" height="250" width="250">

Any ideas?

In your get_context_data method, you’re iterating over context[‘object_list’] to process each formation.

What I would do is create a new dict for your context with two elements - the Formation and the image.

Roughly:

context['formation_list'] = [
    {'formation': obj, 'image': self.make_image(obj)} for obj in context['object_list']
]

where make_image is a function to create an image given an object of type PersonnelFormation.

In your template, you iterate over formation_list, and reference those elements as iteration_variable.formation.whatever and iteration_variable.image.

Side note:

You’re performing an unnecessary query here. The variable obj is an instance of PersonnelFormation. So you’re taking an instance of PersonnelFormation and using its primary key to retrieve itself from the database.