将 form_list 表单实例保存到数据库的正确方法?
Correct way to save form_list form instances to the database?
我正在使用 SessionWizardView 将小型调查分成多个页面。
我的问题是调查的每一页的数据都保存在我数据库的不同行中,其中一页上有多个输入字段,信息被保存在同一行中。这使我的数据库非常麻烦。
我将问题隔离到我的 save()
函数,每当我的 form_list
中有一个 form
时,该函数就会执行,因此每次都会在数据库中添加新行。
def done(self, form_list, **kwargs):
for form in form_list:
form.save()
return render(self.request, 'Return_to_Prolific_Academic.html', {
'form_data': [form.cleaned_data for form in form_list],
})
我试图通过 form_list.save()
直接在 form_list 上执行保存,但出现错误:
'list' object has no attribute 'save'
我也尝试了 join()
和 chain()
看看我是否可以将所有 form
实例连接在一起,看看我是否可以同时保存它们但是不成功。
我想简化我当前的解决方案,以便每个参与者的数据都保存在数据库中的一行中,谁能告诉我该怎么做?谢谢
编辑
我的一些例子 models.py 和 forms.py
models.py
class Person(models.Model):
....
....
sender = models.EmailField(null=True, blank=True, verbose_name='What is your email address?')
birthdate = models.DateField(null=True, blank=True) #overwritten in forms.py so passed no more arguments
SEX = (
('1', 'Male'),
('2', 'Female'))
sex = models.CharField(null=True, blank=True, max_length=100, choices=SEX, verbose_name='What sex are you?')
MARITAL_STATUS = (
('SINGLE', "Single"),
('INARELATIONSHIP', "In a relationship"),
('MARRIED', "Married"),
('DIVORCED', "Divorced"),
('SEPARATED', "Separated"),
('WIDOWED', "Widowed"),)
marital_status = models.CharField(null=True, blank=True, max_length=100, choices=MARITAL_STATUS, verbose_name='What is your marital status?')
INTERNET_USAGE = (
('INTERNET_LESS_THAN_ONE_HOUR_A_DAY', 'Less than one hour a day'),
('INTERNET_ONE_TO_TWO_HOURS_A_DAY', '1 - 2 hours a day'),
('INTERNET_TWO_TO_FOUR_HOURS_A_DAY', '2 - 4 hours a day'),
('INTERNET_FOUR_TO_SIX_HOURS_A_DAY', '4 - 6 hours a Day'),
('INTERNET_SIX_TO_EIGHT_HOURS_A_DAY', '6 - 8 hours a day'),
('INTERNET_EIGHT_PLUS_HOURS_A_DAY', '8 + hours a day'), )
internet_usage = models.CharField(null=True, blank=True, max_length=100, default=None, choices=INTERNET_USAGE)
SMART_PHONE_OWNERSHIP = (
('YES_SMARTPHONE', 'Yes'),
('NO_SMARTPHONE', 'No'),)
smart_phone_ownership = models.CharField(null=True, max_length=100, blank=True, choices=SMART_PHONE_OWNERSHIP, verbose_name='Do you own a Smartphone?')
forms.py
class SurveyFormIT1(forms.ModelForm):
class Meta:
model = Person
fields = ['instruction_task_one_value', 'instruction_task_one_image']
widgets = {'instruction_task_one_value' : forms.HiddenInput,
'instruction_task_one_image' : forms.HiddenInput}
class SurveyFormIT2(forms.ModelForm):
class Meta:
model = Person
fields = ['instruction_task_two_value', 'instruction_task_two_image']
widgets = {'instruction_task_two_value' : forms.HiddenInput,
'instruction_task_two_image' : forms.HiddenInput}
class Start(forms.ModelForm):
class Meta:
model = Person
fields = ['start']
widgets = {'start' : forms.HiddenInput}
class SurveyFormA(forms.ModelForm):
birthdate = forms.DateField(widget=extras.SelectDateWidget(years=range(1920, 1998)), required=True)
class Meta:
model = Person
fields = ['prolific_academic_ID', 'sender', 'birthdate', 'sex', 'marital_status', 'state']
class SurveyFormB(forms.ModelForm):
class Meta:
model = Person
fields = ['internet_usage', 'smart_phone_ownership', 'smart_phone_usage']
widgets = {'internet_usage' : RadioSelectNotNull,
'smart_phone_ownership' : RadioSelectNotNull,
'smart_phone_usage' : RadioSelectNotNull,}
超级简单,超级笨拙的方式,因为我不知道你的数据模型:
# Accumulate all the filled-in data:
data = {}
for form in form_list:
for k,v in form.__dict__.items():
data[k] = v if v else data.get(k)
# Save it to the first form
form_list[0].__dict__.update(data)
form_list[0].save()
所以最好的方法是创建我的 Person 模型的实例(我所有的表单数据都保存在其中)并 运行 在上面保存
def done(self, form_list, **kwargs):
instance = Person()
for form in form_list:
for key, value in form.cleaned_data.iteritems():
setattr(instance, key, value)
instance.save()
return render(self.request, 'Return_to_Prolific_Academic.html', {
'form_data': [form.cleaned_data for form in form_list],
})
WizardView
有一个 get_form_instance()
方法,该方法 returns 将实例传递给特定的表单。您的问题源于每个表单收到一个空实例,这导致所有表单在保存时创建一个新的单独表单。
该方法如下所示:
def get_form_instance(self, step):
"""
Returns an object which will be passed to the form for `step`
as `instance`. If no instance object was provided while initializing
the form wizard, None will be returned.
"""
return self.instance_dict.get(step, None)
虽然您可以将 instance_dict
传递给向导的 __init__
,但在您的情况下,将视图子类化并在调用 [=16= 后立即将其设置在 __init__()
中可能更清晰].
这比@Deepend 的解决方案更清晰,因为它按预期重用了内部实例传递机制,而不是稍后将实例注入表单。
试试这个解决方案:
def done(self, form_list, **kwargs):
data = {k: v for form in form_list for k, v in form.cleaned_data.items()}
instance = Person.objects.create(**data)
return render(self.request, 'Return_to_Prolific_Academic.html', {
'form_data': [form.cleaned_data for form in form_list],
})
我正在使用 SessionWizardView 将小型调查分成多个页面。
我的问题是调查的每一页的数据都保存在我数据库的不同行中,其中一页上有多个输入字段,信息被保存在同一行中。这使我的数据库非常麻烦。
我将问题隔离到我的 save()
函数,每当我的 form_list
中有一个 form
时,该函数就会执行,因此每次都会在数据库中添加新行。
def done(self, form_list, **kwargs):
for form in form_list:
form.save()
return render(self.request, 'Return_to_Prolific_Academic.html', {
'form_data': [form.cleaned_data for form in form_list],
})
我试图通过 form_list.save()
直接在 form_list 上执行保存,但出现错误:
'list' object has no attribute 'save'
我也尝试了 join()
和 chain()
看看我是否可以将所有 form
实例连接在一起,看看我是否可以同时保存它们但是不成功。
我想简化我当前的解决方案,以便每个参与者的数据都保存在数据库中的一行中,谁能告诉我该怎么做?谢谢
编辑
我的一些例子 models.py 和 forms.py
models.py
class Person(models.Model):
....
....
sender = models.EmailField(null=True, blank=True, verbose_name='What is your email address?')
birthdate = models.DateField(null=True, blank=True) #overwritten in forms.py so passed no more arguments
SEX = (
('1', 'Male'),
('2', 'Female'))
sex = models.CharField(null=True, blank=True, max_length=100, choices=SEX, verbose_name='What sex are you?')
MARITAL_STATUS = (
('SINGLE', "Single"),
('INARELATIONSHIP', "In a relationship"),
('MARRIED', "Married"),
('DIVORCED', "Divorced"),
('SEPARATED', "Separated"),
('WIDOWED', "Widowed"),)
marital_status = models.CharField(null=True, blank=True, max_length=100, choices=MARITAL_STATUS, verbose_name='What is your marital status?')
INTERNET_USAGE = (
('INTERNET_LESS_THAN_ONE_HOUR_A_DAY', 'Less than one hour a day'),
('INTERNET_ONE_TO_TWO_HOURS_A_DAY', '1 - 2 hours a day'),
('INTERNET_TWO_TO_FOUR_HOURS_A_DAY', '2 - 4 hours a day'),
('INTERNET_FOUR_TO_SIX_HOURS_A_DAY', '4 - 6 hours a Day'),
('INTERNET_SIX_TO_EIGHT_HOURS_A_DAY', '6 - 8 hours a day'),
('INTERNET_EIGHT_PLUS_HOURS_A_DAY', '8 + hours a day'), )
internet_usage = models.CharField(null=True, blank=True, max_length=100, default=None, choices=INTERNET_USAGE)
SMART_PHONE_OWNERSHIP = (
('YES_SMARTPHONE', 'Yes'),
('NO_SMARTPHONE', 'No'),)
smart_phone_ownership = models.CharField(null=True, max_length=100, blank=True, choices=SMART_PHONE_OWNERSHIP, verbose_name='Do you own a Smartphone?')
forms.py
class SurveyFormIT1(forms.ModelForm):
class Meta:
model = Person
fields = ['instruction_task_one_value', 'instruction_task_one_image']
widgets = {'instruction_task_one_value' : forms.HiddenInput,
'instruction_task_one_image' : forms.HiddenInput}
class SurveyFormIT2(forms.ModelForm):
class Meta:
model = Person
fields = ['instruction_task_two_value', 'instruction_task_two_image']
widgets = {'instruction_task_two_value' : forms.HiddenInput,
'instruction_task_two_image' : forms.HiddenInput}
class Start(forms.ModelForm):
class Meta:
model = Person
fields = ['start']
widgets = {'start' : forms.HiddenInput}
class SurveyFormA(forms.ModelForm):
birthdate = forms.DateField(widget=extras.SelectDateWidget(years=range(1920, 1998)), required=True)
class Meta:
model = Person
fields = ['prolific_academic_ID', 'sender', 'birthdate', 'sex', 'marital_status', 'state']
class SurveyFormB(forms.ModelForm):
class Meta:
model = Person
fields = ['internet_usage', 'smart_phone_ownership', 'smart_phone_usage']
widgets = {'internet_usage' : RadioSelectNotNull,
'smart_phone_ownership' : RadioSelectNotNull,
'smart_phone_usage' : RadioSelectNotNull,}
超级简单,超级笨拙的方式,因为我不知道你的数据模型:
# Accumulate all the filled-in data:
data = {}
for form in form_list:
for k,v in form.__dict__.items():
data[k] = v if v else data.get(k)
# Save it to the first form
form_list[0].__dict__.update(data)
form_list[0].save()
所以最好的方法是创建我的 Person 模型的实例(我所有的表单数据都保存在其中)并 运行 在上面保存
def done(self, form_list, **kwargs):
instance = Person()
for form in form_list:
for key, value in form.cleaned_data.iteritems():
setattr(instance, key, value)
instance.save()
return render(self.request, 'Return_to_Prolific_Academic.html', {
'form_data': [form.cleaned_data for form in form_list],
})
WizardView
有一个 get_form_instance()
方法,该方法 returns 将实例传递给特定的表单。您的问题源于每个表单收到一个空实例,这导致所有表单在保存时创建一个新的单独表单。
该方法如下所示:
def get_form_instance(self, step):
"""
Returns an object which will be passed to the form for `step`
as `instance`. If no instance object was provided while initializing
the form wizard, None will be returned.
"""
return self.instance_dict.get(step, None)
虽然您可以将 instance_dict
传递给向导的 __init__
,但在您的情况下,将视图子类化并在调用 [=16= 后立即将其设置在 __init__()
中可能更清晰].
这比@Deepend 的解决方案更清晰,因为它按预期重用了内部实例传递机制,而不是稍后将实例注入表单。
试试这个解决方案:
def done(self, form_list, **kwargs):
data = {k: v for form in form_list for k, v in form.cleaned_data.items()}
instance = Person.objects.create(**data)
return render(self.request, 'Return_to_Prolific_Academic.html', {
'form_data': [form.cleaned_data for form in form_list],
})