【问题标题】:Is this the good way to handle multiple formsets in the same view?这是在同一视图中处理多个表单集的好方法吗?
【发布时间】:2019-05-21 07:04:00
【问题描述】:

首先我使用的是 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")

环境:
请求方法:POST
请求网址:http://127.0.0.1:8000/tournament/admin-1/manage-phase/30-matches/
Django 版本:2.2
Python 版本:3.7.3
已安装的应用程序:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users.apps.UsersConfig',
'crispy_forms',
'类别',
'匹配',
'阶段',
'游泳池',
'规则',
'集',
'运动',
'团队',
'锦标赛',
“页面”,
'体育馆',
'字段']
已安装的中间件:
['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']
追溯:
内部文件“C:\Users\33643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\core\handlers\exception.py”
34. response = get_response(请求)

_get_response
中的文件“C:\Users\33643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\core\handlers\base.py” 115. response = self.process_exception_by_middleware(e, request)

_get_response
中的文件“C:\Users\33643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\core\handlers\base.py” 113. response = Wrapped_callback(request, *callback_args, **callback_kwargs)

match_phase_view
中的文件“C:\Users\33643\Documents\Projets Django\TournamentManagerApp\src\pages\views.py” 370. 打印(表单集)


中的文件“C:\Users\33643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\utils\html.py” 388. klass.str = lambda self: mark_safe(klass_str(self))

str
中的文件“C:\Users\33643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\forms\formsets.py” 64. 返回 self.as_table()

as_table 中的文件“C:\Users\33643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\forms\formsets.py”
404. forms = ' '.join(form.as_table() for form in self)

iter
中的文件“C:\Users\33643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\forms\formsets.py” 68. 返回 iter(self.forms)

get
中的文件“C:\Users\33643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\utils\functional.py” 80. res = instance.dict[self.name] = self.func(instance)

表单中的文件“C:\Users\33643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\forms\formsets.py”
136. for i in range(self.total_form_count())]

total_form_count 中的文件“C:\Users\33643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\forms\formsets.py”
110. return min(self.management_form.cleaned_data[TOTAL_FORM_COUNT], self.absolute_max)

get
中的文件“C:\Users\33643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\utils\functional.py” 80. res = instance.dict[self.name] = self.func(instance)

management_form
中的文件“C:\Users\33643\Documents\Projets Django\TournamentManagerApp\lib\site-packages\django\forms\formsets.py” 92.code='missing_management_form',

异常类型:ValidationError at /tournament/admin-1/manage-phase/30-matches/
异常值:['ManagementForm 数据丢失或被篡改']

【问题讨论】:

  • 这取决于你!只需关注&gt;&gt;&gt; import this
  • 很抱歉,如果我理解您的回答,我不确定
  • 在python shell中运行import this可以展现python的禅意。 Django 的文档很漂亮:docs.djangoproject.com/en/2.2/topics/forms/formsets
  • 我已经看过这个文档,但我没有看到我们如何在同一个视图中处理多个表单集
  • 问题是你所有的表单集都有相同的前缀。查看生成的 HTML(在您的浏览器中),您会看到多个输入字段具有相同的 name 属性。因此,在您的循环中,您需要 add a prefix 到每个更改每次迭代的表单集。您可以通过将 prefix 参数传递给您的表单集初始化程序来添加前缀。

标签: python django formset


【解决方案1】:

检查您的 HTML 源代码,您会发现所有表单集都具有相同的前缀。例如,您将有多个输入字段,其中name 属性设置为form-0-match,对应于表单集中第一个表单(索引0)的match 字段。

在视图中使用多个表单集时(如here 所述),您需要确保每个表单集具有不同的prefix

您可以通过将prefix 参数传递给表单集初始化器来设置前缀,例如:

formset_set = MatchSetFormset(request.POST or None, instance=match, prefix=f"form{match.pk}")

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-14
  • 2019-10-21
相关资源
最近更新 更多