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

Django modelform_factory生成的表单提交时报选项无效错误如何解决

问题根源

  1. ganador是关联Jugador表的外键字段,你在模型层写死的choices值是固定字符串Jugador A/Jugador B,但外键字段校验需要接收Jugador表的主键ID值,类型不匹配直接触发校验失败。
  2. 每个对局的参赛者都不一样,你全局生成的表单没有动态适配当前对局的两个玩家作为可选选项。
  3. 提交表单时没有指定要更新的对局ID,表单保存时会直接新建对局而不是修改已有对局的胜者。
  4. 模板中所有对局共用同一个表单实例,无法对应各自的参赛者选项。

修改步骤

1. 修正models.py的字段配置

删除ganador字段写死的choices,因为每个对局的可选胜者是动态变化的,不能在模型层固定配置:

class Juego(models.Model):
    torneo = models.ForeignKey(Torneo, on_delete=models.CASCADE, null=False)
    ronda = models.ForeignKey(Ronda, on_delete=models.CASCADE, null=False)
    jugadorA = models.ForeignKey(Jugador, on_delete=models.SET_NULL, null=True, related_name='jugadorA')
    jugadorB = models.ForeignKey(Jugador, on_delete=models.SET_NULL, null=True, related_name='jugadorB')
    puntuacionA = models.IntegerField(default=0)
    puntuacionB = models.IntegerField(default=0)
    # 删掉固定choices配置
    ganador = models.ForeignKey(Jugador, on_delete=models.SET_NULL, null=True, default=None)

2. 自定义动态表单类

替换原来的modelform_factory生成的静态表单,动态适配每个对局的参赛者作为可选胜者:

from django import forms
from .models import Juego

class GanadorForm(forms.ModelForm):
    class Meta:
        model = Juego
        fields = ['ganador']
    
    def __init__(self, *args, **kwargs):
        # 接收当前对局实例
        juego = kwargs.pop('juego', None)
        super().__init__(*args, **kwargs)
        if juego:
            # 动态设置可选选项为当前对局的两名玩家,值为玩家主键,显示名为玩家姓名
            self.fields['ganador'].choices = [
                (juego.jugadorA.id, f"{juego.jugadorA.nombre} {juego.jugadorA.apellido}"),
                (juego.jugadorB.id, f"{juego.jugadorB.nombre} {juego.jugadorB.apellido}")
            ]

3. 调整视图逻辑

修正表单绑定、更新逻辑,给每个对局生成单独的表单实例:

def detalleTorneo(request, id):
    torneo = Torneo.objects.get(pk=id)
    jugadoresPares = False
    jugadores = torneo.jugadores.all()
    no_jugadores = jugadores.count()
    rondas = Ronda.objects.filter(torneo=torneo)
    juegos = Juego.objects.filter(torneo=torneo)

    if request.method == 'POST':
        # 从请求中获取要更新的对局ID
        juego_id = request.POST.get('juego_id')
        juego = Juego.objects.get(pk=juego_id)
        # 绑定表单时传入对局实例、提交数据、要更新的对局对象
        formaGanador = GanadorForm(request.POST, juego=juego, instance=juego)
        if formaGanador.is_valid():
            formaGanador.save()

    # 给每个对局绑定对应的表单实例
    for juego in juegos:
        juego.forma = GanadorForm(juego=juego, instance=juego)

    if (no_jugadores % 2) == 0:
        jugadoresPares = True

    return render(request, 'torneos/detalle.html', {
        'juegos': juegos, 
        'torneo': torneo, 
        'no_jugadores': no_jugadores, 
        'jugadoresPares': jugadoresPares, 
        'rondas': rondas
    })

4. 修改模板渲染逻辑

每个对局使用对应表单,添加隐藏字段传递对局ID:

{% for ronda in rondas %}
    <h3>Ronda {{ronda.numero}}</h3>
    <ul>
        {% for juego in juegos %}
            {% if juego.ronda == ronda %}
            <li>
                <form method="post">
                    {% csrf_token %}
                    <!-- 隐藏字段传递当前对局ID -->
                    <input type="hidden" name="juego_id" value="{{juego.id}}">
                    <strong>{{juego.jugadorA.nombre}} {{juego.jugadorA.apellido}} vs {{juego.jugadorB.nombre}} {{juego.jugadorB.apellido}}</strong> 
                    | {{juego.forma}} 
                    <button type="submit">Ingresar Resultados</button>
                    <br>
                </form>
            </li>
            {% endif %}
        {% endfor %}
    </ul>
{% endfor %}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 08:15:03