Django FormWizard - 如何根据上一步动态创建表单集

Posted

技术标签:

【中文标题】Django FormWizard - 如何根据上一步动态创建表单集【英文标题】:Django FormWizard - How do you dynamically create a formset based on previous step 【发布时间】:2011-12-31 15:00:38 【问题描述】:

我看过this 的帖子,但它不起作用(部分原因是它已经过时了)。我还研究了源代码树但无济于事(测试有所帮助),但我找不到答案。我要做的是在form0('start')中获取一组种子数据,它将为step2动态构建一个formset。第 2 步只是一个验证步骤。

    'start' - 用户输入细分 (subA)、邮政编码 (12345) 和批次的 csv (51,52,53) 'step2' - 创建一个动态表单(modelformset),其中 3 个表单分别代表 51,52,53 用户点击,模型构建完成

data = [  'subdivision': <subA>, 'zipcode': '12345', 'lot_number': '51'
          'subdivision': <subA>, 'zipcode': '12345', 'lot_number': '52'
          'subdivision': <subA>, 'zipcode': '12345', 'lot_number': '53' ]

我的尝试

在实施解决方案here 时,我只得到data=None。这是过时的,并且查看源代码我认为“正确”的方法是简单地覆盖get_form_instance 方法并提供它get_cleaned_data_for_step,但这似乎重新验证并做了很多比我认为它需要的更多的东西(而且它没有工作)。

所以.. 我正在寻找的是两件事。

    获取以前的表单数据的正确方法是什么。 如何获取这些数据并使用它来创建 n 个表单集。

FWIW 我正在使用 Django 1.4-alpha formset 向导。

这就是我所拥有的。

# urls.py
    url(r'homes/bulk/$', TestWizard.as_view([('start', BulkHomeForm0),
                                             ('step2', HomeFormSet)])),

# Models.py
class Subdivision(models.Model):
    name = models.CharField(max_length=64)

class Home(models.Model):
    lot_number = models.CharField(max_length=16)
    subdivision = models.ForeignKey(Subdivision)
    zipcode = models.IntegerField(validators=[validate_zipcode], null=True)

# Forms
class BulkHomeForm0(forms.Form):
    subdivision = forms.ModelChoiceField(queryset=Subdivision.objects.all(), required=True)
    zipcode = USZipCodeField(required=True)
    lots = forms.CharField(max_length=5000, widget=forms.Textarea()

    def clean(self):
        subdivision = self.cleaned_data.get('subdivision', False)
        zipcode = self.cleaned_data.get('zipcode', False)
        final_data = []
        for item in self.cleaned_data.get('lots', "").split(",")
            final_data.append('subdivision':subdivision, 
                               'zipcode':zipcode, 
                               'lot_number':item)
        self.cleaned_data['homes'] = final_data

class BulkHomeForm1(forms.ModelForm):
    class Meta:
        model = Home

HomeFormSet = modelformset_factory(Home, form=BulkHomeForm1, extra=2)

# Views.py
class TestWizard(WizardView):
    storage_name = 'django.contrib.formtools.wizard.storage.session.SessionStorage'

    def get_form(self, step=None, data=None, files=None):
        form = super(TestWizard, self).get_form(step=step, data=data, files=files)
        return form

    def done(self, form_list, **kwargs):
        return render_to_response('done.html', 
            'form_data': [form.cleaned_data for form in form_list],
        )

【问题讨论】:

【参考方案1】:

这是我想出的..

我似乎无法让 modelForm 很好地工作,所以我将两者分开并将它们合并到 done。它还不完美,但它正在接近..

class BulkHomeForm1(forms.Form):

    lot_number = forms.CharField(max_length=16, required=True)
    street_line1 = forms.CharField(max_length=100, required=True)
    floorplan = forms.ModelChoiceField(queryset=Floorplan.objects.filter(is_active=True), required=False)
    start_date = forms.DateField(required=False)

temp_storage_location = tempfile.mkdtemp(dir=settings.MEDIA_ROOT, prefix="bulk_homes_")
os.chmod(temp_storage_location,  02775) # TODO FIX ME
temp_storage = FileSystemStorage(location=temp_storage_location)

class BulkHomeWizard(SessionWizardView):
    file_storage = temp_storage

    def get_form(self, step=None, data=None, files=None):

        form = super(BulkHomeWizard, self).get_form(step=step, data=data, files=files)
        if self.steps.current == 'start' and form.prefix != "step2":
            # Limit the subdivisions down to the specifics
            sub_qs = Subdivision.objects.filter(is_active=True)
            if self.request.user.company_type == "rater":
                sub_qs = sub_qs.filter(rater_orgs=self.request.user.company.id)
            elif self.request.user.company_type == "eep":
                sub_qs = sub_qs.filter(eep_orgs=self.request.user.company.id)
            form.fields['subdivision'].queryset = sub_qs
        return form

    def get_context_data(self, form, **kwargs):
        context = super(BulkHomeWizard, self).get_context_data(form, **kwargs)
        self.template_name = 'axis/bulk_%s.html' %  self.steps.current
        return context

    def get_form_initial(self, step):
        """This is used to seed the model set with information from the previous step"""
        if step == 'step2':
            log.info("Into Step 2")
            data = self.get_cleaned_data_for_step('start')['homes']
            return data
        return self.initial_dict.get(step, )

    def done(self, form_list, **kwargs):

        cleaned_data = [form.cleaned_data for form in form_list]

        subdivision = cleaned_data[0].get('subdivision')
        city = subdivision.city
        state = subdivision.state
        zipcode = cleaned_data[0].get('zipcode')

        for form in cleaned_data[1]:
            data = Home.objects.get_or_create(lot_number = form.get('lot_number'),
                                              floorplan = form.get('floorplan', None),
                                              street_line1 = form.get('street_line1', None),
                                              subdivision = subdivision,
                                              city = subdivision.city, state=subdivision.state,
                                              zipcode=zipcode,
                                              start_date = form.get('start_date', None),)
            obj, created = data
            obj.clean()
            obj.save()
            if created:
                log.info("Create new Home")

        return HttpResponseRedirect(reverse("subdivision_view", kwargs='subdivision_id': subdivision.id))

【讨论】:

以上是关于Django FormWizard - 如何根据上一步动态创建表单集的主要内容,如果未能解决你的问题,请参考以下文章

Django:如何使用 ModelFormSet 和 FormWizard 过滤 ForeignKey 选择(例如使用 request.user)?

Django FormWizard如何动态更改form_list

django manytomany 字段使用 through 和 formwizard

Django 的 FormWizard 中的空 ModelFormset

使用 formwizard-django 创建用户后登录

带有动态表单的 Django FormWizard