将 form_list 表单实例保存到数据库的正确方法?

Posted

技术标签:

【中文标题】将 form_list 表单实例保存到数据库的正确方法?【英文标题】:Correct way to save form_list form instances to the database? 【发布时间】:2015-10-23 10:53:34 【问题描述】:

我正在使用 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' 对象没有属性'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,

【问题讨论】:

能否显示您的form 代码和调用done 的代码 【参考方案1】:

试试这个解决方案:

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],            
    )

【讨论】:

稍微解释一下问题和您的解决方案会很有帮助。【参考方案2】:

超级简单,超级笨拙的方式,因为我不知道你的数据模型:

# 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()

【讨论】:

嗨,我添加了我的 models.py 的示例。您的解决方案仅保存用户在最后一页提交的信息。之前页面中的数据必须被覆盖。【参考方案3】:

因此,最好的方法是创建我的 Person 模型的实例(我的所有表单数据都保存在其中)并在其上运行 save

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],            
    ) 

【讨论】:

【参考方案4】:

WizardView 有一个 get_form_instance() 方法,该方法返回要传递给特定表单的实例。您的问题源于每个表单接收一个空实例,这会导致所有表单在保存时创建一个新的单独的实例。

方法如下:

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__,但在您的情况下,将视图子类化并在调用__init__() 后立即将其设置在__init__() 中可能会更干净。@ 987654327@。

这比@Deepend 的解决方案更干净,因为它按预期重用内部实例传递机制,而不是稍后将实例注入表单。

【讨论】:

以上是关于将 form_list 表单实例保存到数据库的正确方法?的主要内容,如果未能解决你的问题,请参考以下文章

php通过post将表单数据保存到数据库实例

如何在Django中只在ModelForms表中正确提交一个表单?

表单提交后重定向的正确方法

django 在父子模型类之间保存实例

Twitter Bootstrap 模态表单提交

从 json 动态创建的 Javascript 表单输入,需要将对象映射到另一个对象的正确字段并保存