通过覆盖 __init__ 动态创建表单时 Django 表单验证失败

Django form validation fails when dynamically creating form by overwriting __init__

我正在用 Django 编写一个预测应用程序。在基本层面上,它从 API 中提取比赛,填充数据库,最后允许用户对尚未进行的比赛进行预测。因为我不知道比赛之前会有多少场比赛,所以我想创建一个表单,可以根据需要动态添加尽可能多的字段。

我目前有一个“工作”版本,但它会进一步产生问题。这是工作版本:(公平警告,这是我的测试,请原谅命名约定)

views.py

def ftest(request, contest_id):
    matches_list = Matches.objects.filter(
        matches_contest_pk__id = contest_id
    ).filter(
        matches_complete = False
    ).order_by(
        '-matches_start_time'
    )

    matches = [
        f'{match.matches_team_1} vs. {match.matches_team_2}'
        for match in matches_list
    ]

    if request.method == 'POST':
        form = TestForm(form_fields = matches, data=request.POST)

        if form.is_valid():
            return HttpResponseRedirect('/thanks/')
        else:
            print(form.errors)
    else:
        form = TestForm(form_fields = matches)

    context = {
        'form': form,
    }

    return render(request, 'predictions/test.html', context)

forms.py

from django import forms
from .models import Matches

class TestForm(forms.Form):
    def __init__(self, *args, **kwargs):
        form_fields = kwargs.pop('form_fields', None)
        super(TestForm, self).__init__(*args, **kwargs)
        for field in form_fields:
            self.fields[field] = forms.CharField(label = f'{field}', max_length = 100)

正如我所说,这可行,但理想情况下,我希望将每个字段的 ID 设置为数据库中的实际 ID。我对此的解决方案是更改视图以通过 form_fields 发送字典列表并更新表单以使用它。这就是它的样子:

views.py

def ftest(request, contest_id):
    matches_list = Matches.objects.filter(
        matches_contest_pk__id = contest_id
    ).filter(
        matches_complete = False
    ).order_by(
        '-matches_start_time'
    )

    matches = [
        { 'label': f'{match.matches_team_1} vs. {match.matches_team_2}', 'id': match.id, }
        for match in matches_list
    ]

    if request.method == 'POST':
        form = TestForm(form_fields = matches, data=request.POST)

        if form.is_valid():
            return HttpResponseRedirect('/thanks/')
        else:
            print(form.errors)
    else:
        form = TestForm(form_fields = matches)

    context = {
        'form': form,
    }

    return render(request, 'predictions/test.html', context)

forms.py

from django import forms
from .models import Matches

class TestForm(forms.Form):
    def __init__(self, *args, **kwargs):
        form_fields = kwargs.pop('form_fields', None)
        super(TestForm, self).__init__(*args, **kwargs)
        for field in form_fields:
            self.fields[field['id']] = forms.CharField(label = field['label'], max_length = 100)

但是,进行此更改后,表单未通过验证。它只是为每个字段抛出一个 This field is required. 错误。理想情况下,我希望用户返回到同一页面,并在字段中填写他们的预测。关于如何解决这个问题的任何建议?我是从错误的角度处理问题还是误解了表单在 Django 中的工作方式?

已解决。

问题出在我发送到表单创建方法的内容上。当我创建字典时,它是这样完成的:

    matches = [
        { 'label': f'{match.matches_team_1} vs. {match.matches_team_2}', 'id': match.id, }
        for match in matches_list
    ]

原来问题出在 'id' 键上。我给它分配了一个整数,它在表单创建中需要是一个字符串。所以解决方案看起来像:

    matches = [
        { 'label': f'{match.matches_team_1} vs. {match.matches_team_2}', 'id': str(match.id), }
        for match in matches_list
    ]