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

Django实现字段联动:选艺术家后仅显示对应作品名

Django表单字段联动实现方案(Artist与Title联动)

核心思路

要实现「选择艺术家后,作品标题仅显示该艺术家的作品」,需要后端提供动态查询API + 前端监听输入并更新下拉选项,模型可根据数据规范程度选择是否调整。


1. 模型是否需要修改?

快速方案:不修改现有模型

当前SpotifyData是扁平结构,直接通过artist字段过滤title即可满足需求,无需调整模型,适合快速实现。

规范方案:拆分一对多模型(可选)

如果数据中存在大量重复的艺术家名称,建议拆分模型,减少冗余并提高查询效率:

# models.py
from django.db import models

class Artist(models.Model):
    name = models.CharField(max_length=60, unique=True, verbose_name="艺术家")

class Song(models.Model):
    title = models.CharField(max_length=60, verbose_name="作品标题")
    artist = models.ForeignKey(Artist, on_delete=models.CASCADE, related_name="songs", verbose_name="所属艺术家")
    rank = models.IntegerField(verbose_name="排名")
    date = models.DateField(verbose_name="日期")
    region = models.CharField(max_length=20, verbose_name="地区")
    chart = models.CharField(max_length=8, verbose_name="榜单类型")
    streams = models.IntegerField(verbose_name="播放量")

    class Meta:
        indexes = [
            models.Index(fields=['title']),
            models.Index(fields=['rank']),
            models.Index(fields=['date']),
            models.Index(fields=['region']),
            models.Index(fields=['chart']),
        ]

注:拆分后需要迁移现有数据,将原SpotifyData中的artist和title映射到新模型。


2. 后端实现动态查询API

添加一个视图函数,接收选中的艺术家名称,返回对应的作品标题列表:

# views.py
from django.http import JsonResponse
from .models import SpotifyData  # 若拆分模型则导入Song

def get_titles_by_artist(request):
    artist_name = request.GET.get('artist', '')
    if not artist_name:
        return JsonResponse({'titles': []})
    
    # 未拆分模型的查询方式
    titles = SpotifyData.objects.filter(artist=artist_name)\
                               .values_list('title', flat=True)\
                               .distinct()\
                               .order_by('title')
    
    # 拆分模型后的查询方式
    # titles = Song.objects.filter(artist__name=artist_name)\
    #                      .values_list('title', flat=True)\
    #                      .distinct()\
    #                      .order_by('title')
    
    return JsonResponse({'titles': list(titles)})

在urls.py中添加API路由:

# urls.py
from django.urls import path
from .views import SongRankChangesChart, get_titles_by_artist

urlpatterns = [
    path('song-rank-changes/', SongRankChangesChart.as_view(), name='song_rank_changes_chart'),
    path('get-titles-by-artist/', get_titles_by_artist, name='get_titles_by_artist'),
    # 其他路由...
]

3. 前端实现联动逻辑

修改HTML模板,添加JavaScript监听艺术家输入的变化,动态更新作品标题的下拉选项:

<!-- song_rank_changes_chart.html -->
{% extends 'base.html' %}
{% block content %}
<body>
  <div class="form_box">
    <form method="GET" name="chart_1" action="{% url 'song_rank_changes_chart' %}">
      {% csrf_token %}

      <div>
        Region
        <input list="list_of_regions" id="region_input" placeholder="Choose region"
            aria-label="Search" name="region" value="{{ selected_region }}">
        <datalist id="list_of_regions">
            {% for name in ch_region %}
            <option value="{{ name.region }}">
            {% endfor %}
        </datalist>
      </div>
      
      <div>
        Chart
        <input list="list_of_charts" id="chart_input" placeholder="Choose chart" aria-label="Search"
             name="chart" value="{{ selected_chart }}">
        <datalist id="list_of_charts">
            {% for name in ch_chart %}
            <option value="{{ name.chart }}">
            {% endfor %}
        </datalist>
      </div>

      <div>
        Artist
        <input list="list_of_artists" id="artist_input" placeholder="Choose artist" aria-label="Search"
             name="artist" value="{{ selected_artist }}">
        <datalist id="list_of_artists">
            {% for name in ch_artist %}
            <option value="{{ name.artist }}">
            {% endfor %}
        </datalist>
      </div>

      <div>
        Title
        <input list="list_of_titles" id="title_input" placeholder="Choose title" aria-label="Search"
             name="title" value="{{ selected_title }}">
        <datalist id="list_of_titles">
            {% for name in ch_title %}
            <option value="{{ name.title }}">
            {% endfor %}
        </datalist>
      </div>

      <div>
        <button type="submit">Submit</button>
      </div>
    </form>
  </div>
  <div class="chart_box">
    {{ chart|safe }}
  </div>

  <script>
    // 获取DOM元素
    const artistInput = document.getElementById('artist_input');
    const titleDatalist = document.getElementById('list_of_titles');
    const titleInput = document.getElementById('title_input');

    // 监听艺术家输入变化
    artistInput.addEventListener('change', function() {
        const artistName = this.value.trim();
        // 清空标题输入和选项
        titleInput.value = '';
        titleDatalist.innerHTML = '';

        if (!artistName) return;

        // 发送请求到后端API
        fetch(`{% url 'get_titles_by_artist' %}?artist=${encodeURIComponent(artistName)}`)
            .then(response => response.json())
            .then(data => {
                // 添加新的标题选项
                data.titles.forEach(title => {
                    const option = document.createElement('option');
                    option.value = title;
                    titleDatalist.appendChild(option);
                });
            })
            .catch(error => console.error('加载作品列表失败:', error));
    });
  </script>
</body>
{% endblock %}

4. 调整现有视图逻辑

修改SongRankChangesChart视图,支持前端参数过滤,并根据选中的艺术家动态返回标题列表:

# views.py
class SongRankChangesChart(TemplateView):
    template_name = 'song_rank_changes_chart.html'  # 确保指定模板路径

    def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
        context = super().get_context_data(**kwargs)

        start = '2019-01-01'
        end = '2020-01-01'
        
        # 获取前端提交的筛选参数
        selected_region = self.request.GET.get('region')
        selected_chart = self.request.GET.get('chart')
        selected_artist = self.request.GET.get('artist')
        selected_title = self.request.GET.get('title')

        # 构建基础查询集
        queryset = SpotifyData.objects.filter(date__range=(start, end))
        # 按参数过滤
        if selected_region:
            queryset = queryset.filter(region=selected_region)
        if selected_chart:
            queryset = queryset.filter(chart=selected_chart)
        if selected_artist:
            queryset = queryset.filter(artist=selected_artist)
        if selected_title:
            queryset = queryset.filter(title=selected_title)
        
        # 获取下拉列表选项
        ch_region = SpotifyData.objects.values("region").order_by("region").distinct()
        ch_chart = SpotifyData.objects.values("chart").order_by("chart").distinct()
        ch_artist = SpotifyData.objects.values("artist").order_by("artist").distinct()
        
        # 根据选中的艺术家过滤标题列表
        if selected_artist:
            ch_title = SpotifyData.objects.filter(artist=selected_artist)\
                                         .values("title")\
                                         .order_by("title")\
                                         .distinct()
        else:
            ch_title = SpotifyData.objects.values("title").order_by("title").distinct()

        # 准备图表数据
        data = queryset.values_list("date", "rank")
        # 处理空数据情况
        if not data:
            fig = make_song_rank_changes_chart([], start, end, selected_artist or "无", selected_title or "无")
        else:
            fig = make_song_rank_changes_chart(data, start, end, selected_artist or ch_artist[0]['artist'], selected_title or ch_title[0]['title'])
        
        chart = fig.to_html()

        context.update({
            "chart": chart,
            "ch_title": ch_title,
            "ch_region": ch_region,
            "ch_chart": ch_chart,
            "ch_artist": ch_artist,
            # 回显选中的参数
            "selected_region": selected_region,
            "selected_chart": selected_chart,
            "selected_artist": selected_artist,
            "selected_title": selected_title,
        })

        return context

内容的提问来源于stack exchange,提问作者Damian Bylicki

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 12:10:50