Django dynamically filter objects and render in template

I produced a report that provides a count of how many musicians played each genre at a festival. the musician model has a genre field that is a foreign key to the genre table. I can successfully produce a template that renders this report, but it is hard coded in a way that is not ideal. I would like to be able to have users edit the genre table without me having to edit the view and template to account for new genres.

What direction can I take this where the genres played at a festival are identified dynamically, based on the musicians that played within the daterange of the festival, then passed as context to the template? See model, template, and view below:

Model:

class Festival(models.Model):
    id = models.AutoField(db_column='Id', primary_key=True, blank=True, null=False)   
    number = models.TextField(db_column='Number', blank=True, null=True)   
    startdate = models.DateField(db_column='StartDate', blank=True, null=True)   
    enddate = models.DateField(db_column='EndDate', blank=True, null=True)     

    class Meta:
        managed = True
        db_table = 'Festival'
        verbose_name_plural = 'Festival'

class Musician(models.Model):
    id = models.AutoField(db_column='Id', primary_key=True, blank=True, null=False)  
    startdate = models.DateTimeField(db_column='StartDate', null=False)  
    enddate = models.DateTimeField(db_column='EndDate', blank=True, null=True)   
    genre = models.ForeignKey('Genre', models.DO_NOTHING, db_column='GenreId', null=True)  

    class Meta:
        managed = True
        db_table = 'Musician'
        verbose_name_plural = 'Musician'

class Genre(models.Model):
    id = models.AutoField(db_column='Id', primary_key=True, blank=True, null=False)  
    name = models.TextField(db_column='Name', blank=True, null=True)

    class Meta:
        managed = True
        db_table = 'Genre'
        verbose_name_plural = 'Genre'

view:

def festivalreport(request, pk):
    

    #festival object and musicians by festival daterange
    festival=Festival.objects.filter(id=pk)
    festival_object=festival.last()
    startdate=festival_object.startdate
    enddate=festival_object.enddate
    musician=Musician.objects.filter(startdate__range=[startdate, enddate])

    #genre 1 calculations
    musician_genre1=musician.filter(genre='1')
    musician_genre1_count=musician_genre1.count()

    #genre 2 calculations
    musician_genre2=musician.filter(genre='2')
    musician_genre2_count=musician_genre2.count()

    #genre 3 calculations
    musician_genre3=musician.filter(genre='3')
    musician_genre3_count=musician_genre3.count()

    context ={
        'festival':festival,
        'musician': musician,
        'musician_genre1_count':musician_genre1_count,
        'musician_genre2_count':musician_genre2_count,
        'musician_genre3_count':musician_genre3_count,
        }

    return render(request, "wwdb/reports/festivalreport.html", context)

Template:

<!DOCTYPE html>
{% extends "wwdb/base.html" %}

{% block content %}
<div class="container p-5">
    {% for f in festival %}
        <h2>{{f.number}} Festival Report</h2>
        </br>
        <p>start date: {{f.startdate}}</p>
        <p>end date: {{f.enddate}}</p>
        <p></p>
    {% endfor %}
</div>
<div class="container p-5">
    <h2>Genre Report</h2>
    </br>
    <h5>Genre 1</h5>
    {% if musician_genre1_count %}
        <p>Genre 1 musician count: {{musician_genre1_count}}</p>
    {% else %}
        <p>No musicians played genre 1</p>
    {% endif %}
    </br>
    <h5>Genre 2</h5>
    {% if musician_genre2_count %}
        <p>Genre 2 musician count: {{musician_genre2_count}}</p>
    {% else %}
        <p>No musicians played genre 2</p>
    {% endif %}
    </br>
    <h5>Genre 3</h5>
    {% if musician_genre3_count %}
        <p>Genre 2 musician count: {{musician_genre3_count}}</p>
    {% else %}
        <p>No musicians played genre 3</p>
    {% endif %}
    </br>
</div>
{% endblock content %}

One possibility is to get all musician who played within the date range. Then from there loop over each musician and separate them into a dictionary. Then you can get all the counts and send the count dict to the template.

View

def festivalreport(request, pk):
    # festival object and musicians by festival daterange
    festival = Festival.objects.filter(id=pk)
    festival_object = festival.last()
    startdate = festival_object.startdate
    enddate = festival_object.enddate

    # Use select_related to prevent duplicate db queries
    musician = Musician.objects.select_related("genre").filter(
        startdate__range=[startdate, enddate]
    )

    # Separate the musicians by genre
    musician_by_genre = {}
    for m in musician:
        if m.genre not in musician_by_genre:
            musician_by_genre[m.genre] = []

        musician_by_genre[m.genre].append(m)

    # Get the count of all the musicians in each genre
    musician_by_genre_count = {key: len(val) for key, val in musician_by_genre}

    # Create context with the new genre count
    context = {
        "festival": festival,
        "musician": musician,
        "musician_by_genre_count": musician_by_genre_count,
    }

    return render(request, "wwdb/reports/festivalreport.html", context)

Template

{% block content %}
<div class="container p-5">
    {% for f in festival %}
        <h2>{{f.number}} Festival Report</h2>
        </br>
        <p>start date: {{f.startdate}}</p>
        <p>end date: {{f.enddate}}</p>
        <p></p>
    {% endfor %}
</div>
<div class="container p-5">
    <h2>Genre Report</h2>
    </br>
    
    {% for genre, count in musician_by_genre_count %}
        <h5>Genre {{genre.name}}</h5>
        {% if count %}
            <p>Genre 1 musician count: {{count}}</p>
        {% else %}
            <p>No musicians played genre 1</p>
        {% endif %}
    {% endfor %}
    </br>
</div>
{% endblock content %}

Here is the solution I went with:

The view:

def festivalreport(request, pk):
    
    festival=Festival.objects.filter(id=pk)
    festival_object=festival.last()
    startdate=festival_object.startdate
    enddate=festival_object.enddate
    musician = Musician.objects.filter(startdate__range=[startdate, enddate])

    musician_by_genre = {}
    for m in musician:
        if m.genre not in musician_by_genre:
            musician_by_genre[m.genre] = []

        musician_by_genre[m.genre].append(m)
        
    musician_by_genre_count = {}
    for m in musician_by_genre: 
        musician_by_genre_count[m]=len(musician_by_genre[m])

    context = {
        "festival": festival,
        "musician": musician,
        "musician_by_genre_count": musician_by_genre_count,
    }

    return render(request, "wwdb/reports/festivalereport.html", context)

The template:

<div class="container p-5">
    {% for f in festival %}
    <h2>{{f.number}} Festival Report</h2>
    </br>
    <p>start date: {{m.startdate}}</p>
    <p>end date: {{m.enddate}}</p>
    <p></p>
    {% endfor %}
</div>
<div class="container p-5">
    <h2>Genre Report</h2>
    </br>

    {% for genre, count in musician_by_genre_count.items %}
    <h5>{{genre}}</h5>
    {% if count %}
    <p>musician count: {{count}}</p>
    {% endif %}
    {% endfor %}
    </br>
</div>
Back to Top