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>