You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

方式二:显示所有流派(包括无参演的)

需要自定义模板过滤器来匹配字典中的数据:

  1. 在你的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)
  1. 修改模板,加载过滤器并循环全量流派:
<!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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 03:44:53