这是在同一视图中处理多个表单集的好方法吗?
Is this the good way to handle multiple formsets in the same view?
首先我使用 Python 3.7.3 和 Django 2.2
我在处理同一视图中的多个表单集时遇到问题。在我看来,我在 for 循环中创建我的表单集(请在下面查看),然后将它们添加到列表中。在我的模板中,我在该列表上执行 for 循环并在模板中显示表单集。我认为我所做的工作很好,但是当表单集无效时会发生错误(因为我也做了一些验证)。当一个表单集无效时(因为不接受值),其他表单集也将使用这些值进行初始化。为什么?
models.py
class Set(models.Model):
timeSet = models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True)
scoreTeam1 = models.IntegerField(null=True)
scoreTeam2 = models.IntegerField(null=True)
match = models.ForeignKey(Match, default=None, on_delete=models.CASCADE)
class Match(models.Model):
isFinished = models.BooleanField(default=False)
team1Win = models.BooleanField(default=False)
team2Win = models.BooleanField(default=False)
phase = models.ForeignKey(Phase, default=None, on_delete=models.CASCADE)
teams = models.ManyToManyField(Team, default=None, blank=True)
tournament_manage_phase_matches.html
{% for match in matches %}
{% if match.teams.first.pool == pool %}
<div class="col-lg-12">
{% if match.isFinished == False %}
<div class="btn-group dropright">
<button type="button" class="btn btn-secondary">
Field number : {{ pool.field.numField }} | {{ match.teams.first.name }} VS {{ match.teams.last.name }}
</button>
<button type="button" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="sr-only">Toggle Dropright</span>
</button>
<div class="dropdown-menu">
{% for formset_set in list_formset_set %}
{% if formset_set.match == match %}
<form class="px-4 py-3" method="post">
{% csrf_token %}
{{ formset_set|crispy }}
{{ formset_set.management_form }}
<button type="submit" class="btn btn-primary" value="{{ match.pk }}" name="match_pk">Save score</button>
</form>
{% endif %}
{% endfor %}
</div>
</div>
{% else %}
{% if match.team1Win == False and match.team2Win == False %}
<label>{{ match.teams.first.name }} VS {{ match.teams.last.name }} : Match nul</label>
{% else %}
{% if match.team1Win == True and match.team2Win == False %}
<label>{{ match.teams.first.name }} VS {{ match.teams.last.name }} : Victory {{ match.teams.first.name }}</label>
{% endif %}
{% if match.team1Win == False and match.team2Win == True %}
<label>{{ match.teams.first.name }} VS {{ match.teams.last.name }} : Victory {{ match.teams.last.name }}</label>
{% endif %}
{% endif %}
{% endif %}
</div>
{% endif %}
{% endfor %}
所以回顾一下,当我提交一个表单集时,如果它无效,所有其他表单集都会变得像我刚刚提交的表单集,我不明白为什么。
如果您发现我所做的有问题,请也告诉我:)
编辑:
views.py
...
#creation of forms
list_formset_set = []
for match in matches:
if match.isFinished == False:
formset_set = MatchSetFormset(request.POST or None, instance=match, prefix="form-" + str(match.pk) + "-match")
formset_set.match = match
list_formset_set.append(formset_set)
for formset_set in list_formset_set:
id_match_submit = request.POST.get("match_pk")
str_id_match_formet_set = str(formset_set.match.pk)
if id_match_submit == str_id_match_formet_set:
if formset_set.is_valid():
formset_set.save()
nb_set_winner_t1 = 0
nb_set_winner_t2 = 0
for set_match in formset_set:
if set_match.cleaned_data.get('scoreTeam1') == set_match.cleaned_data.get('scoreTeam2'):
nb_set_winner_t1 += 0
nb_set_winner_t2 -= 0
else:
if set_match.cleaned_data.get('scoreTeam1') > set_match.cleaned_data.get('scoreTeam2'):
nb_set_winner_t1 += 1
nb_set_winner_t2 -= 1
else:
nb_set_winner_t1 -= 1
nb_set_winner_t2 += 1
match = formset_set.cleaned_data[0].get('match')
team1 = formset_set.cleaned_data[0].get('match').teams.first()
team2 = formset_set.cleaned_data[0].get('match').teams.last()
if nb_set_winner_t1 == nb_set_winner_t2:
team1.totalpoints += sport.nbPointPerDraw
team2.totalpoints += sport.nbPointPerDraw
team1.save()
team2.save()
match.isFinished = True
match.save()
else:
if nb_set_winner_t1 > nb_set_winner_t2:
team1.totalpoints += sport.nbPointPerVictory
team1.nbVictory += 1
team2.totalpoints += sport.nbPointPerDefeat
team2.nbDefeat += 1
team1.save()
team2.save()
match.team1Win = True
match.isFinished = True
match.save()
else:
team1.totalpoints += sport.nbPointPerDefeat
team1.nbDefeat += 1
team2.totalpoints += sport.nbPointPerVictory
team2.nbVictory += 1
team1.save()
team2.save()
match.team2Win = True
match.isFinished = True
match.save()
teams = Team.objects.filter(pool__in=pools).order_by('-totalpoints') #"-" means descending
for index, team in enumerate(teams):
team.position = index + 1
return redirect('matches_phase_manage_tournament', id=id, id_phase=id_phase)
else:
# reload formsets
print("invalid")
Environment:
Request Method: POST
Request URL: http://127.0.0.1:8000/tournament/admin-1/manage-phase/30-matches/
Django Version: 2.2
Python Version: 3.7.3
Installed Applications:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users.apps.UsersConfig',
'crispy_forms',
'categories',
'matches',
'phases',
'pools',
'rules',
'sets',
'sports',
'teams',
'tournaments',
'pages',
'gymnasiums',
'fields']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']
Traceback:
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\core\handlers\exception.py" in inner
34. response = get_response(request)
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\core\handlers\base.py" in _get_response
115. response = self.process_exception_by_middleware(e, request)
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\core\handlers\base.py" in _get_response
113. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\src\pages\views.py" in matches_phase_view
370. print(formset)
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\utils\html.py" in
388. klass.str = lambda self: mark_safe(klass_str(self))
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\forms\formsets.py" in str
64. return self.as_table()
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\forms\formsets.py" in as_table
404. forms = ' '.join(form.as_table() for form in self)
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\forms\formsets.py" in iter
68. return iter(self.forms)
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\utils\functional.py" in get
80. res = instance.dict[self.name] = self.func(instance)
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\forms\formsets.py" in forms
136. for i in range(self.total_form_count())]
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\forms\formsets.py" in total_form_count
110. return min(self.management_form.cleaned_data[TOTAL_FORM_COUNT], self.absolute_max)
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\utils\functional.py" in get
80. res = instance.dict[self.name] = self.func(instance)
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\forms\formsets.py" in management_form
92. code='missing_management_form',
Exception Type: ValidationError at /tournament/admin-1/manage-phase/30-matches/
Exception Value: ['ManagementForm data is missing or has been tampered with']
检查您的 HTML 来源,您会发现所有的表单集都具有相同的前缀。因此,例如,您将有多个 name
属性设置为 form-0-match
的输入字段,它对应于表单集中第一个表单(索引 0)的 match
字段。
在一个视图中使用多个表单集时(如here), you need to make sure each formset has a different prefix所述。
您可以通过将 prefix
参数传递给表单集初始值设定项来设置前缀,例如:
formset_set = MatchSetFormset(request.POST or None, instance=match, prefix=f"form{match.pk}")
首先我使用 Python 3.7.3 和 Django 2.2
我在处理同一视图中的多个表单集时遇到问题。在我看来,我在 for 循环中创建我的表单集(请在下面查看),然后将它们添加到列表中。在我的模板中,我在该列表上执行 for 循环并在模板中显示表单集。我认为我所做的工作很好,但是当表单集无效时会发生错误(因为我也做了一些验证)。当一个表单集无效时(因为不接受值),其他表单集也将使用这些值进行初始化。为什么?
models.py
class Set(models.Model):
timeSet = models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True)
scoreTeam1 = models.IntegerField(null=True)
scoreTeam2 = models.IntegerField(null=True)
match = models.ForeignKey(Match, default=None, on_delete=models.CASCADE)
class Match(models.Model):
isFinished = models.BooleanField(default=False)
team1Win = models.BooleanField(default=False)
team2Win = models.BooleanField(default=False)
phase = models.ForeignKey(Phase, default=None, on_delete=models.CASCADE)
teams = models.ManyToManyField(Team, default=None, blank=True)
tournament_manage_phase_matches.html
{% for match in matches %}
{% if match.teams.first.pool == pool %}
<div class="col-lg-12">
{% if match.isFinished == False %}
<div class="btn-group dropright">
<button type="button" class="btn btn-secondary">
Field number : {{ pool.field.numField }} | {{ match.teams.first.name }} VS {{ match.teams.last.name }}
</button>
<button type="button" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="sr-only">Toggle Dropright</span>
</button>
<div class="dropdown-menu">
{% for formset_set in list_formset_set %}
{% if formset_set.match == match %}
<form class="px-4 py-3" method="post">
{% csrf_token %}
{{ formset_set|crispy }}
{{ formset_set.management_form }}
<button type="submit" class="btn btn-primary" value="{{ match.pk }}" name="match_pk">Save score</button>
</form>
{% endif %}
{% endfor %}
</div>
</div>
{% else %}
{% if match.team1Win == False and match.team2Win == False %}
<label>{{ match.teams.first.name }} VS {{ match.teams.last.name }} : Match nul</label>
{% else %}
{% if match.team1Win == True and match.team2Win == False %}
<label>{{ match.teams.first.name }} VS {{ match.teams.last.name }} : Victory {{ match.teams.first.name }}</label>
{% endif %}
{% if match.team1Win == False and match.team2Win == True %}
<label>{{ match.teams.first.name }} VS {{ match.teams.last.name }} : Victory {{ match.teams.last.name }}</label>
{% endif %}
{% endif %}
{% endif %}
</div>
{% endif %}
{% endfor %}
所以回顾一下,当我提交一个表单集时,如果它无效,所有其他表单集都会变得像我刚刚提交的表单集,我不明白为什么。 如果您发现我所做的有问题,请也告诉我:)
编辑:
views.py
...
#creation of forms
list_formset_set = []
for match in matches:
if match.isFinished == False:
formset_set = MatchSetFormset(request.POST or None, instance=match, prefix="form-" + str(match.pk) + "-match")
formset_set.match = match
list_formset_set.append(formset_set)
for formset_set in list_formset_set:
id_match_submit = request.POST.get("match_pk")
str_id_match_formet_set = str(formset_set.match.pk)
if id_match_submit == str_id_match_formet_set:
if formset_set.is_valid():
formset_set.save()
nb_set_winner_t1 = 0
nb_set_winner_t2 = 0
for set_match in formset_set:
if set_match.cleaned_data.get('scoreTeam1') == set_match.cleaned_data.get('scoreTeam2'):
nb_set_winner_t1 += 0
nb_set_winner_t2 -= 0
else:
if set_match.cleaned_data.get('scoreTeam1') > set_match.cleaned_data.get('scoreTeam2'):
nb_set_winner_t1 += 1
nb_set_winner_t2 -= 1
else:
nb_set_winner_t1 -= 1
nb_set_winner_t2 += 1
match = formset_set.cleaned_data[0].get('match')
team1 = formset_set.cleaned_data[0].get('match').teams.first()
team2 = formset_set.cleaned_data[0].get('match').teams.last()
if nb_set_winner_t1 == nb_set_winner_t2:
team1.totalpoints += sport.nbPointPerDraw
team2.totalpoints += sport.nbPointPerDraw
team1.save()
team2.save()
match.isFinished = True
match.save()
else:
if nb_set_winner_t1 > nb_set_winner_t2:
team1.totalpoints += sport.nbPointPerVictory
team1.nbVictory += 1
team2.totalpoints += sport.nbPointPerDefeat
team2.nbDefeat += 1
team1.save()
team2.save()
match.team1Win = True
match.isFinished = True
match.save()
else:
team1.totalpoints += sport.nbPointPerDefeat
team1.nbDefeat += 1
team2.totalpoints += sport.nbPointPerVictory
team2.nbVictory += 1
team1.save()
team2.save()
match.team2Win = True
match.isFinished = True
match.save()
teams = Team.objects.filter(pool__in=pools).order_by('-totalpoints') #"-" means descending
for index, team in enumerate(teams):
team.position = index + 1
return redirect('matches_phase_manage_tournament', id=id, id_phase=id_phase)
else:
# reload formsets
print("invalid")
Environment:
Request Method: POST
Request URL: http://127.0.0.1:8000/tournament/admin-1/manage-phase/30-matches/
Django Version: 2.2
Python Version: 3.7.3
Installed Applications:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users.apps.UsersConfig',
'crispy_forms',
'categories',
'matches',
'phases',
'pools',
'rules',
'sets',
'sports',
'teams',
'tournaments',
'pages',
'gymnasiums',
'fields']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']
Traceback:
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\core\handlers\exception.py" in inner
34. response = get_response(request)
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\core\handlers\base.py" in _get_response
115. response = self.process_exception_by_middleware(e, request)
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\core\handlers\base.py" in _get_response
113. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\src\pages\views.py" in matches_phase_view
370. print(formset)
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\utils\html.py" in
388. klass.str = lambda self: mark_safe(klass_str(self))
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\forms\formsets.py" in str
64. return self.as_table()
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\forms\formsets.py" in as_table
404. forms = ' '.join(form.as_table() for form in self)
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\forms\formsets.py" in iter
68. return iter(self.forms)
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\utils\functional.py" in get
80. res = instance.dict[self.name] = self.func(instance)
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\forms\formsets.py" in forms
136. for i in range(self.total_form_count())]
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\forms\formsets.py" in total_form_count
110. return min(self.management_form.cleaned_data[TOTAL_FORM_COUNT], self.absolute_max)
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\utils\functional.py" in get
80. res = instance.dict[self.name] = self.func(instance)
File "C:\Users643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\forms\formsets.py" in management_form
92. code='missing_management_form',
Exception Type: ValidationError at /tournament/admin-1/manage-phase/30-matches/
Exception Value: ['ManagementForm data is missing or has been tampered with']
检查您的 HTML 来源,您会发现所有的表单集都具有相同的前缀。因此,例如,您将有多个 name
属性设置为 form-0-match
的输入字段,它对应于表单集中第一个表单(索引 0)的 match
字段。
在一个视图中使用多个表单集时(如here), you need to make sure each formset has a different prefix所述。
您可以通过将 prefix
参数传递给表单集初始值设定项来设置前缀,例如:
formset_set = MatchSetFormset(request.POST or None, instance=match, prefix=f"form{match.pk}")