Django динамически фильтрует объекты и отображает их в шаблоне

Я создал отчет, который предоставляет подсчет количества музыкантов, играющих в каждом жанре на фестивале. В модели музыканта есть поле genre, которое является внешним ключом к таблице genre. Я могу успешно создать шаблон, который отображает этот отчет, но он жестко закодирован, что не является идеальным. Я бы хотел, чтобы пользователи могли редактировать таблицу жанров без необходимости редактировать представление и шаблон для учета новых жанров.

В каком направлении я могу двигаться, если жанры, играемые на фестивале, определяются динамически, на основе музыкантов, игравших в течение периода проведения фестиваля, а затем передаются в качестве контекста в шаблон? См. модель, шаблон и представление ниже:

Модель:

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)

Шаблон:

<!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 %}

Одна из возможностей - получить всех музыкантов, игравших в диапазоне дат. Затем перебрать всех музыкантов и выделить их в словарь. Затем вы можете получить все подсчеты и отправить дикту подсчета в шаблон.

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)

Шаблон

{% 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 %}

Вот решение, которое я выбрал:

Вид:

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)

Шаблон:

<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>
Вернуться на верх