Django 表单集有效,但返回空字典列表

问题描述 投票:0回答:1

我正在尝试呈现一个表单,该表单是一个表格,其中每行包含一个名称和一个复选框,以允许用户选择游戏的玩家。谷歌搜索后我意识到我需要生成一个表单集,以便表中的每一行都是一个单独的表单。

我终于正确渲染了表格并没有错误地发布,但我发现虽然表单集有效,但清理后的数据包含一个空字典列表。我发现一些类似的 StackOverflow 帖子说要创建自己的 Formset 类并设置 empty_permissed=True 但这并没有解决我的问题。

我似乎无法解决这个问题,因此感谢任何帮助。

代码片段

# forms.py
class InvitePlayerForm(forms.Form):
    """
    Form to invite players to the new game
    """
    invited = forms.CheckboxInput()



class InvitePlayerFormset(BaseFormSet):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for form in self.forms:
            form.empty_permitted = False
# views.py:

def create_game_players(request):
    """
    View to invite players to a game
    """
    context = dict()
    # if this is a POST request we need to process the form data
    if request.method == "POST":
        num_players = request.session["invites"]["num_players"]
        print(f"num_players: {num_players}")
        invite_formset = formset_factory(InvitePlayerForm, formset=InvitePlayerFormset, extra=num_players)
        invite_forms = invite_formset(data=request.POST)

        print(f"Formset valid: {invite_forms.is_valid()}")
        print(f"request.POST: {request.POST}")
        print(f"changed_data: {invite_forms.has_changed}")

        data = invite_forms.cleaned_data
        print(f"Cleaned data: {data}")

        return HttpResponseRedirect(reverse("game_list_view"))

    # if a GET (or any other method) we'll create a blank form
    else:
        player_list = list(CustomUser.objects.all())
        num_players = len(player_list)
        invite_formset = formset_factory(InvitePlayerForm, formset=InvitePlayerFormset, extra=num_players)
        # context["num_players"] = num_players
        context["first_names"] = [u.first_name for u in player_list]
        context["last_names"] = [u.last_name for u in player_list]
        context["invite_formset"] = invite_formset()
        request.session["invites"] = {"num_players": num_players}

    return render(request, "create_game_players.html", context)
# create_game_players.html template
<h4>Invite Players</h4>
          <div class="form-group">

            <form method="post">
              {% csrf_token %}

              {{ invite_formset.management_form.as_p }}

              <table class="table table-hover">
                <thead>
                    <tr>
                      <th>Name</th>
                      <th>Invited</th>
                    </tr>
                </thead>
                <tbody>
                  {% for invite_form in invite_formset.forms %}

                  <tr>
                    <td>{{first_names|get:forloop.counter0}} {{last_names|get:forloop.counter0}}</td>
                    <td>
                      <input type="checkbox" class="form-check-input" name="invited_{{forloop.counter0}}" id="{{forloop.counter0}}"/>
                    </td>
                  </tr>
                  {% endfor %}
                </tbody>
              </table>
              <button type="submit" class="btn btn-info btn-lg btn-block">Next</button>
            </form>

          </div>

控制台输出

[05/Apr/2024 14:32:31] "POST /create_game/ HTTP/1.1" 302 0
[05/Apr/2024 14:32:31] "GET /create_game/players HTTP/1.1" 200 8961
num_players: 21
Formset valid: True
request.POST: <QueryDict: {'csrfmiddlewaretoken': ['HzZu3tHpBIHDVSwPa4UJZTjWCNC52s7Njz2NRhTvJPXDzNKsSonFNRxyUNK1NtZu'], 'form-TOTAL_FORMS': ['21'], 'form-INITIAL_FORMS': ['0'], 'form-MIN_NUM_FORMS': ['0'], 'form-MAX_NUM_FORMS': ['1000'], 'invited_0': ['on'], 'invited_1': ['on'], 'invited_2': ['on']}>
changed_data: <bound method BaseFormSet.has_changed of <InvitePlayerFormFormSet: bound=True valid=True total_forms=21>>
Cleaned data: [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}]
[05/Apr/2024 14:32:39] "POST /create_game/players HTTP/1.1" 302 0
python django django-views django-forms django-templates
1个回答
0
投票

我认为您应该循环遍历表单集中的每个表单并检查

invite_forms.cleaned_data
字段的值,而不是访问
'invited'

尝试这个视图:

def create_game_players(request):
    """
    View to invite players to a game
    """
    context = dict()
    # if this is a POST request we need to process the form data
    if request.method == "POST":
        num_players = request.session["invites"]["num_players"]
        print(f"num_players: {num_players}")
        invite_formset = formset_factory(InvitePlayerForm, formset=InvitePlayerFormset, extra=num_players)
        invite_forms = invite_formset(data=request.POST)

        print(f"Formset valid: {invite_forms.is_valid()}")
        print(f"request.POST: {request.POST}")
        print(f"changed_data: {invite_forms.has_changed}")

        if invite_forms.is_valid():
            selected_players = []
            for form in invite_forms:
                if form.cleaned_data.get('invited'):
                    selected_players.append(form.cleaned_data.get('invited'))

            print(f"Selected players: {selected_players}")

        return HttpResponseRedirect(reverse("game_list_view"))

    # if a GET (or any other method) we'll create a blank form
    else:
        player_list = list(CustomUser.objects.all())
        num_players = len(player_list)
        invite_formset = formset_factory(InvitePlayerForm, formset=InvitePlayerFormset, extra=num_players)
        # context["num_players"] = num_players
        context["first_names"] = [u.first_name for u in player_list]
        context["last_names"] = [u.last_name for u in player_list]
        context["invite_formset"] = invite_formset()
        request.session["invites"] = {"num_players": num_players}

    return render(request, "create_game_players.html", context)

还要确保表单中的

'invited'
字段已正确定义并映射到模板中的复选框。

© www.soinside.com 2019 - 2024. All rights reserved.