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

Django:根据已选Game模型过滤League模型的可选选项

问题解决:Django中实现Game与League的联动过滤

问题背景

现有以下Django模型:

class Game(models.Model):
    name = models.CharField('Nom du jeu', max_length=100)
    logo = models.ImageField('Logo', blank=True, upload_to='game_logos')

class League(models.Model):
    game = models.ForeignKey(Game, verbose_name='Jeu', null=True, on_delete=models.SET_NULL)
    league = models.CharField('Ligue', max_length=20)
    hexcolor = models.CharField('Code couleur Hex', max_length=7, blank=True, default='#000000')

class Event(models.Model):
    game = models.ForeignKey(Game, verbose_name='Nom du jeu', null=True, on_delete=models.SET_NULL)
    event_type = models.CharField(verbose_name="Type d'événement", max_length=50, choices=type_course)
    league = models.ForeignKey(League, verbose_name='Ligue', null=True, on_delete=models.SET_NULL)
    circuit = models.ForeignKey(Circuit, verbose_name='Circuit', null=True, on_delete=models.SET_NULL)
    event_date = models.DateTimeField(verbose_name="Date de la course")

需求:创建Event时,选择Game后,League下拉框仅显示该Game关联的联赛(不同Game的League相互独立)。


实现方案

方案1:Django Admin中实现联动

1. 自定义Admin表单

编写自定义表单,初始化时根据已选Game过滤League选项:

from django import forms
from .models import Event, League

class EventAdminForm(forms.ModelForm):
    class Meta:
        model = Event
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 编辑已有Event时,自动过滤对应Game的League
        if self.instance.game:
            self.fields['league'].queryset = League.objects.filter(game=self.instance.game)
        else:
            self.fields['league'].queryset = League.objects.none()

2. 注册Admin并引入JS

在Admin中使用该表单,同时添加自定义JS实现动态联动:

from django.contrib import admin
from .models import Event
from .forms import EventAdminForm

class EventAdmin(admin.ModelAdmin):
    form = EventAdminForm
    # 引入静态JS文件
    class Media:
        js = ('js/event_admin.js',)

admin.site.register(Event, EventAdmin)

3. 编写前端联动JS

在static/js/event_admin.js中添加监听逻辑:

document.addEventListener('DOMContentLoaded', () => {
    const gameSelect = document.getElementById('id_game');
    const leagueSelect = document.getElementById('id_league');

    // 监听Game选择变化
    gameSelect.addEventListener('change', () => {
        const gameId = gameSelect.value;
        // 清空League选项
        leagueSelect.innerHTML = '<option value="">---------</option>';
        
        if (gameId) {
            // 请求对应Game的League列表
            fetch(`/api/leagues/?game_id=${gameId}`)
                .then(res => res.json())
                .then(data => {
                    data.forEach(league => {
                        const option = document.createElement('option');
                        option.value = league.id;
                        option.textContent = league.league;
                        leagueSelect.appendChild(option);
                    });
                });
        }
    });
});

4. 编写API接口提供League数据

在views.py中添加接口:

from django.http import JsonResponse
from .models import League

def league_list(request):
    game_id = request.GET.get('game_id')
    leagues = League.objects.filter(game_id=game_id).values('id', 'league')
    return JsonResponse(list(leagues), safe=False)

在urls.py中配置路由:

from django.urls import path
from .views import league_list

urlpatterns = [
    # 其他路由...
    path('api/leagues/', league_list, name='league-list'),
]

方案2:自定义前端表单实现

如果是自主开发的前端页面,逻辑类似:

1. 自定义表单

from django import forms
from .models import Event, League

class EventForm(forms.ModelForm):
    class Meta:
        model = Event
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['league'].queryset = League.objects.none()
        
        # 提交表单时根据选中的Game过滤League
        if 'game' in self.data:
            try:
                game_id = int(self.data.get('game'))
                self.fields['league'].queryset = League.objects.filter(game_id=game_id)
            except (ValueError, TypeError):
                pass
        # 编辑已有Event时自动过滤
        elif self.instance.pk and self.instance.game:
            self.fields['league'].queryset = League.objects.filter(game=self.instance.game)

2. 视图与模板

视图函数:

from django.shortcuts import render, redirect
from .forms import EventForm

def create_event(request):
    form = EventForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        form.save()
        return redirect('event-list')  # 替换为你的列表页路由
    return render(request, 'create_event.html', {'form': form})

模板create_event.html:

<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">保存赛事</button>
</form>

<script>
document.addEventListener('DOMContentLoaded', () => {
    const gameSelect = document.getElementById('id_game');
    const leagueSelect = document.getElementById('id_league');

    gameSelect.addEventListener('change', () => {
        const gameId = gameSelect.value;
        leagueSelect.innerHTML = '<option value="">---------</option>';
        
        if (gameId) {
            fetch(`/api/leagues/?game_id=${gameId}`)
                .then(res => res.json())
                .then(data => {
                    data.forEach(league => {
                        const option = document.createElement('option');
                        option.value = league.id;
                        option.textContent = league.league;
                        leagueSelect.appendChild(option);
                    });
                });
        }
    });

    // 页面加载时若已有选中Game,自动触发一次过滤
    if (gameSelect.value) {
        gameSelect.dispatchEvent(new Event('change'));
    }
});
</script>

同样需要配置之前的API接口和路由。


优化建议

给League模型添加__str__方法,让下拉选项显示更直观:

class League(models.Model):
    # 现有字段...
    def __str__(self):
        return self.league

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 07:55:41