【问题标题】:Use formset_factory as sub-form in Django crispy form使用 formset_factory 作为 Django 脆皮表单中的子表单
【发布时间】:2015-10-08 19:39:15
【问题描述】:

我创建了一个“地址”模型,供我的应用程序的许多不同部分使用。每个用户可以拥有多个专属于他们的地址。我创建了另一个属于用户的类 - 'PersonalDetails'。我希望这个类至少有一个,但最多有六个属于用户的地址。

我当前的设置(涉及清晰的表单)允许这样做,但地址字段是一个“选择”字段,其中包含当前属于该用户的地址列表。我希望用户能够使用现有地址或随时创建新地址。我该如何做到这一点?我尝试让“PersonalDetails”表单使用“AddressForm”,但没有任何乐趣。

作为额外的奖励,我最初只想呈现一个地址表单,但用户可以选择单击“添加更多地址”以获取更多地址字段。这听起来像是表单集的工作,但我不知道如何使用脆皮 - 任何帮助都会很棒!

Address_models.py

class Address(models.Model):

    # Relations

    profile = models.ForeignKey(
            Profile,
            verbose_name = "user"
    )

    # Attributes - Mandatory

    address_line1 = models.CharField(
            max_length = 45,
            verbose_name = "Address line 1"
    )
    city = models.CharField(
            max_length = 50, 
    )
    postcode = models.CharField(
            max_length = 8,
            verbose_name = "Postcode",
            help_text = "Enter your postcode",
            blank = False,
            validators = [RegexValidator(
                "[A-Z]{1,2}[0-9R][0-9A-Z]? [0-9][A-Z]{2}")]
    )

    # Attributes - Optional

    start_date = models.DateField(
            verbose_name = "Start Date", 
            blank = True, 
            null = True,
            help_text = "Move in date"
    )
    end_date = models.DateField(
            verbose_name = "End Date", 
            blank = True, 
            null = True,
            help_text = "Move out date"
    )

    address_line2 = models.CharField(
            verbose_name = "Address line 2",
            max_length = 45,
            blank = True,
            null = True
    )
    county = models.CharField(
            verbose_name = "County", 
            max_length = 40, 
            blank = True, 
            null = True
    )

PersonalDetails_models.py

class PersonalDetails(models.Model):

    # Relations
    profile = models.ForeignKey(
            Profile,
            verbose_name = "user"
    )

    # Attributes - Mandatory

    ni_number = models.CharField(
            max_length = 9,
            verbose_name = "National Insurance Number",
            help_text = "Enter your National Insurance number",
            validators = [RegexValidator(
                "^[a-zA-Z]{2}[0-9]{6}[A-Z]$")]
    )

    last_address = models.ForeignKey(
            Address
    )

    # Attributes - Optional

    prior_1_address = models.ForeignKey(
            Address,
            blank = True,
            null = True,
            related_name = "prior_1_address"
    )

    prior_2_address = models.ForeignKey(
            Address,
            blank = True,
            null = True,
            related_name = "prior_2_address"
    )

    prior_3_address = models.ForeignKey(
            Address,
            blank = True,
            null = True,
            related_name = "prior_3_address"
    )

    prior_4_address = models.ForeignKey(
            Address,
            blank = True,
            null = True,
            related_name = "prior_4_address"
    )

    prior_5_address = models.ForeignKey(
            Address,
            blank = True,
            null = True,
            related_name = "prior_5_address"
    )

    prior_6_address = models.ForeignKey(
            Address,
            blank = True,
            null = True,
            related_name = "prior_6_address"
    )

Address_forms.py

class AddressForm(forms.ModelForm):

    class Meta:
        model = Address
        fields = '__all__'

PersonalDetails_forms.py

class PersonalDetailsForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super(PersonalDetailsForm, self).__init__(*args, **kwargs)
        self.helper = FormHelper(self)
        self.helper.form_tag = False

    class Meta:
        model = PersonalDetailsForm
        fields = ['ni_number', 'last_address', 'prior_1_address', 'prior_2_address', 'prior_3_address', 'prior_4_address', 'prior_5_address', 'prior_6_address']

【问题讨论】:

    标签: django django-forms django-crispy-forms


    【解决方案1】:

    我正在成功使用下面的小部件。

    class Formset(LayoutObject):
        """
        Layout object. It renders an entire formset, as though it were a Field.
    
        Example::
    
        Formset("attached_files_formset")
        """
        template = "%s/formset.html" % TEMPLATE_PACK
        def __init__(self, formset_name_in_context, template=None,**kwargs):
            self.formset_name_in_context = formset_name_in_context
            # crispy_forms/layout.py:302 requires us to have a fields property
            self.fields = []
            # Overrides class variable with an instance level variable
            if template:
                self.template = template
    
        def render(self, form, form_style, context, template_pack=TEMPLATE_PACK):
            formset = context[self.formset_name_in_context]
            html = ''
            return render_to_string(self.template, Context({'wrapper': self,
                'formset': formset}))
    

    在您的应用程序中的某处添加这个小部件,并像使用其他字段一样使用它。 假设你的模块是crispy_layouts

    helper_content.append(
                          crispy_layouts.Formset('address_form')
                          )
    helper = FormHelper()
    helper.layout = Layout(
        helper_content,
    )
    

    address_form 在您的上下文中是有效的表单集。 它用 默认为“%s/formset.html”模板,您可以根据需要自定义。

    我正在使用下面的模板和 bootstrap3

    <div class="formset">
        <div class="table-scrollable">
            {{ formset.non_form_errors.as_ul }}
            <table class="table table-hover">
                {% for form in formset.forms %}
                {% if forloop.first %}
                <thead>
                    <tr>
                        {% for field in form.visible_fields %}
                        <th>{{ field.label|capfirst }}</th>
                        {% endfor %}
                    </tr>
                </thead>
                {% endif %}
                <tr>
                    {% for field in form.visible_fields %}
                    <td> {# Include the hidden fields in the form #}
                    {% if forloop.first %}
                    {% for hidden in form.hidden_fields %}
                    {{ hidden }}
                    {% endfor %}
                    {% endif %}
    
                    {{ field | as_crispy_field}}
                    </td>
                    {% endfor %}
                </tr>
                {% endfor %}
            </table>
            <div class="row">
                <div class="col-md-12">
                    <input type="submit" class="btn btn-primary pull-right" value="Add Row" name="addrow">
                </div>
            </div>
        </div>
    </div>
    

    正如您在我的模板中看到的那样,我允许人们在您的视图中添加更多行来执行此操作。如果您的 request.POST 包含 addrow 则将您的 request.POST 复制到 post_data,因此它将是可变的。

    post_data['addressform-TOTAL_FORMS'] = int(post_data['addressform-TOTAL_FORMS'])+ 1
    

    您基本上增加了 TOTAL_FORMS 变量并按原样将该表单再次返回给用户。

    这是在没有 javascript 的情况下向表单集添加更多行的方法。

    【讨论】:

      猜你喜欢
      • 2020-08-21
      • 2014-02-28
      • 2014-12-17
      • 1970-01-01
      • 1970-01-01
      • 2015-03-19
      • 2017-03-19
      • 2014-10-27
      • 2015-01-07
      相关资源
      最近更新 更多