Django实现音乐节流派统计动态化:无需硬编码适配新流派
问题描述
我做了一份音乐节各流派参演音乐人数量的报表,Musician模型的genre字段是指向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'
视图代码
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 %}
解决方案
核心思路是用Django的聚合查询动态统计各流派数据,彻底抛弃硬编码,让系统自动适配Genre表的新增/修改。
1. 修改视图函数
用values+annotate实现分组统计,同时处理日期匹配(注意Musician的startdate是DateTimeField,需要转成日期再和音乐节的DateField匹配):
from django.db.models import Count def festivalreport(request, pk): # 获取目标音乐节 festival = Festival.objects.filter(id=pk) festival_object = festival.last() startdate = festival_object.startdate enddate = festival_object.enddate # 动态统计该音乐节日期范围内各流派的参演人数 # 过滤掉无流派的音乐人,按流派名称排序 genre_stats = Musician.objects.filter( startdate__date__range=[startdate, enddate], genre__isnull=False ).values('genre__id', 'genre__name').annotate( musician_count=Count('id') ).order_by('genre__name') # 可选:如果需要显示所有流派(包括无参演的),就获取全量流派并转成字典匹配 all_genres = Genre.objects.all() genre_count_map = {item['genre__id']: item['musician_count'] for item in genre_stats} context = { 'festival': festival, 'genre_stats': genre_stats, # 仅包含有参演的流派 'all_genres': all_genres, # 全量流派(可选) 'genre_count_map': genre_count_map # 流派ID对应人数的字典(可选) } return render(request, "wwdb/reports/festivalreport.html", context)
2. 修改模板
根据需求选择两种渲染方式:
方式一:仅显示有参演的流派
直接循环统计结果,无需额外处理:
<!DOCTYPE html> {% extends "wwdb/base.html" %} {% block content %} <div class="container p-5"> {% for f in festival %} <h2>{{f.number}} 音乐节报表</h2> </br> <p>开始日期: {{f.startdate}}</p> <p>结束日期: {{f.enddate}}</p> <p></p> {% endfor %} </div> <div class="container p-5"> <h2>流派统计</h2> </br> {% for stat in genre_stats %} <h5>{{stat.genre__name}}</h5> <p>{{stat.genre__name}} 参演人数: {{stat.musician_count}}</p> </br> {% empty %} <p>暂无音乐人参演</p> {% endfor %} </div> {% endblock content %}
方式二:显示所有流派(包括无参演的)
需要自定义模板过滤器来匹配字典中的数据:
- 在你的app(如
wwdb)下创建templatetags目录,新建custom_filters.py:
from django import template register = template.Library() @register.filter def get_dict_value(dictionary, key): return dictionary.get(key, 0)
- 修改模板,加载过滤器并循环全量流派:
<!DOCTYPE html> {% extends "wwdb/base.html" %} {% load custom_filters %} {% block content %} <div class="container p-5"> {% for f in festival %} <h2>{{f.number}} 音乐节报表</h2> </br> <p>开始日期: {{f.startdate}}</p> <p>结束日期: {{f.enddate}}</p> <p></p> {% endfor %} </div> <div class="container p-5"> <h2>流派统计</h2> </br> {% for genre in all_genres %} <h5>{{genre.name}}</h5> {% with count=genre_count_map|get_dict_value:genre.id %} {% if count > 0 %} <p>{{genre.name}} 参演人数: {{count}}</p> {% else %} <p>暂无{{genre.name}}流派的音乐人参演</p> {% endif %} {% endwith %} </br> {% endfor %} </div> {% endblock content %}
内容的提问来源于stack exchange,提问作者Bonny Clarke
相关产品推荐
相关产品推荐

