具有动态表单的 Django FormView
Django FormView with dynamic forms
我创建了下面的 FormView,它将根据用户所处流程的哪个步骤动态 return 表单 class。我在使用 get_form
时遇到了问题方法。它 return 是 get 请求中 class 的正确形式,但 post 请求无效。
tournament_form_dict = {
'1':TournamentCreationForm,
'2':TournamentDateForm,
'3':TournamentTimeForm,
'4':TournamentLocationForm,
'5':TournamentRestrictionForm,
'6':TournamentSectionForm,
'7':TournamentSectionRestrictionForm,
'8':TournamentSectionRoundForm,}
class CreateTournament(FormView):
template_name = 'events/create_tournament_step.html'
def __init__(self, *args, **kwargs):
form_class = self.get_form()
success_url = self.get_success_url()
super(CreateTournament, self).__init__(*args, **kwargs)
def get_form(self, **kwargs):
if 'step' not in kwargs:
step = '1'
else:
step = kwargs['step']
return tournament_form_dict[step]
def get_success_url(self, **kwargs):
if 'step' not in kwargs:
step = 1
else:
step = int(kwargs['step'])
step += 1
if 'record_id' not in kwargs:
record_id = 0
else:
record_id = int(kwargs['record_id'])
return 'events/tournaments/create/%d/%d/' % (record_id, step)
post 请求在 get_form
行的 django\views\generic\edit.py
处失败,我意识到这是因为我在我的 FormView:
中覆盖了它
def post(self, request, *args, **kwargs):
"""
Handle POST requests: instantiate a form instance with the passed
POST variables and then check if it's valid.
"""
form = self.get_form()
if form.is_valid(): …
return self.form_valid(form)
else:
return self.form_invalid(form)
但是,当我将自定义 get_form
方法的名称更改为 gen_form
时,如下所示:
def __init__(self, *args, **kwargs):
form_class = self.gen_form()
success_url = self.get_success_url()
super(CreateTournament, self).__init__(*args, **kwargs)
def gen_form(self, **kwargs):
if 'step' not in kwargs:
step = '1'
else:
step = kwargs['step']
return tournament_form_dict[step]
我的表单 class 未在 get 请求中得到处理,计算结果为 None
。我绞尽脑汁想知道为什么当我重写 get_form
方法时它有效,但我自己命名的方法却不起作用?有谁知道这个缺陷可能是什么?
Django 的 FormMixin
[Django-doc] defines a get_form
function [Django-doc]。因此,您在这里基本上是 class 子 FormView
和 "patched" get_form
方法。
你对 gen_form
的尝试不起作用,因为你只定义了 local 变量,因此无论如何都没有太大区别,只有 super(..)
调用会有一些副作用。其他命令将使 CPU 忙一段时间,但最后只会将引用分配给 Form
对 form_class
变量的调用,但由于它是本地的,你会扔掉的。
也就是说,您的函数包含一些错误。例如 **kwargs
通常包含 至多 一个参数:form_class
。所以 step
s 不会做太多。您可以通过 self.args
和 self.kwargs
访问 URL 参数,通过 self.request.GET
访问查询字符串参数。此外,您可能想要修补 get_form_class
function,因为您 return 是对 class 的引用,而不是,据我所知,是对初始化表单的引用。
通过字符串处理构造 URLs 可能也不是一个好主意,因为如果您(稍微)更改 URL 模式,那么您很可能会忘记替换 success_url
,因此您将引用不再存在的路径。使用 reverse
function 是一种更安全的方法,因为您传递了视图的名称和参数,然后此函数将 "calculate" 正确 URL。这基本上就是 Django 模板中 {% url ... %}
模板标签背后的机制。
更好的方法是:
from django.urls import reverse
class CreateTournament(FormView):
template_name = 'events/create_tournament_step.html'
def <b>get_form_class</b>(self):
return tournament_form_dict[<b>self.kwargs.get('step', '1')</b>]
def get_success_url(self):
new_step = int(self.kwargs.get('step', 1)) + 1
# use a reverse
return <b>reverse(</b>'name_of_view', kwargs={'step': new_step}<b>)</b>
我创建了下面的 FormView,它将根据用户所处流程的哪个步骤动态 return 表单 class。我在使用 get_form
时遇到了问题方法。它 return 是 get 请求中 class 的正确形式,但 post 请求无效。
tournament_form_dict = {
'1':TournamentCreationForm,
'2':TournamentDateForm,
'3':TournamentTimeForm,
'4':TournamentLocationForm,
'5':TournamentRestrictionForm,
'6':TournamentSectionForm,
'7':TournamentSectionRestrictionForm,
'8':TournamentSectionRoundForm,}
class CreateTournament(FormView):
template_name = 'events/create_tournament_step.html'
def __init__(self, *args, **kwargs):
form_class = self.get_form()
success_url = self.get_success_url()
super(CreateTournament, self).__init__(*args, **kwargs)
def get_form(self, **kwargs):
if 'step' not in kwargs:
step = '1'
else:
step = kwargs['step']
return tournament_form_dict[step]
def get_success_url(self, **kwargs):
if 'step' not in kwargs:
step = 1
else:
step = int(kwargs['step'])
step += 1
if 'record_id' not in kwargs:
record_id = 0
else:
record_id = int(kwargs['record_id'])
return 'events/tournaments/create/%d/%d/' % (record_id, step)
post 请求在 get_form
行的 django\views\generic\edit.py
处失败,我意识到这是因为我在我的 FormView:
def post(self, request, *args, **kwargs):
"""
Handle POST requests: instantiate a form instance with the passed
POST variables and then check if it's valid.
"""
form = self.get_form()
if form.is_valid(): …
return self.form_valid(form)
else:
return self.form_invalid(form)
但是,当我将自定义 get_form
方法的名称更改为 gen_form
时,如下所示:
def __init__(self, *args, **kwargs):
form_class = self.gen_form()
success_url = self.get_success_url()
super(CreateTournament, self).__init__(*args, **kwargs)
def gen_form(self, **kwargs):
if 'step' not in kwargs:
step = '1'
else:
step = kwargs['step']
return tournament_form_dict[step]
我的表单 class 未在 get 请求中得到处理,计算结果为 None
。我绞尽脑汁想知道为什么当我重写 get_form
方法时它有效,但我自己命名的方法却不起作用?有谁知道这个缺陷可能是什么?
Django 的 FormMixin
[Django-doc] defines a get_form
function [Django-doc]。因此,您在这里基本上是 class 子 FormView
和 "patched" get_form
方法。
你对 gen_form
的尝试不起作用,因为你只定义了 local 变量,因此无论如何都没有太大区别,只有 super(..)
调用会有一些副作用。其他命令将使 CPU 忙一段时间,但最后只会将引用分配给 Form
对 form_class
变量的调用,但由于它是本地的,你会扔掉的。
也就是说,您的函数包含一些错误。例如 **kwargs
通常包含 至多 一个参数:form_class
。所以 step
s 不会做太多。您可以通过 self.args
和 self.kwargs
访问 URL 参数,通过 self.request.GET
访问查询字符串参数。此外,您可能想要修补 get_form_class
function,因为您 return 是对 class 的引用,而不是,据我所知,是对初始化表单的引用。
通过字符串处理构造 URLs 可能也不是一个好主意,因为如果您(稍微)更改 URL 模式,那么您很可能会忘记替换 success_url
,因此您将引用不再存在的路径。使用 reverse
function 是一种更安全的方法,因为您传递了视图的名称和参数,然后此函数将 "calculate" 正确 URL。这基本上就是 Django 模板中 {% url ... %}
模板标签背后的机制。
更好的方法是:
from django.urls import reverse
class CreateTournament(FormView):
template_name = 'events/create_tournament_step.html'
def <b>get_form_class</b>(self):
return tournament_form_dict[<b>self.kwargs.get('step', '1')</b>]
def get_success_url(self):
new_step = int(self.kwargs.get('step', 1)) + 1
# use a reverse
return <b>reverse(</b>'name_of_view', kwargs={'step': new_step}<b>)</b>