我正在尝试呈现一个表单,该表单是一个表格,其中每行包含一个名称和一个复选框,以允许用户选择游戏的玩家。谷歌搜索后我意识到我需要生成一个表单集,以便表中的每一行都是一个单独的表单。
我终于正确渲染了表格并没有错误地发布,但我发现虽然表单集有效,但清理后的数据包含一个空字典列表。我发现一些类似的 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
我认为您应该循环遍历表单集中的每个表单并检查
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'
字段已正确定义并映射到模板中的复选框。