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
相关产品推荐
相关产品推荐

