使用formset_factory作为Django crispy形式的子表单

时间:2015-07-19 17:01:20

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

我创建了一个地址'我的应用程序的许多不同部分使用的模型。每个用户可以拥有多个专属于他们的地址。我创建了另一个类 - ' 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']

1 个答案:

答案 0 :(得分:0)

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

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是您上下文中的有效formset。 它用 "%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的情况下向formset添加更多行的方法。