在Django crispy form中使用formset_factory作为子表单



我已经创建了一个'address'模型,用于我的应用程序的许多不同部分。每个用户可以有多个只属于自己的地址。我已经创建了另一个类"PersonalDetails",它属于一个用户。我希望这个类至少有一个,但最多有六个属于用户的地址。

我目前的设置(包括脆表单)允许这样做,但地址字段是一个"选择"字段,包含当前属于该用户的地址列表。我希望用户能够使用现有的地址或创建新的地址,因为他们去。我该怎么做呢?我试过让'PersonalDetails'表单使用'AddressForm',但没有任何乐趣。

作为额外的奖励,我只希望最初呈现一个地址表单,但用户可以选择单击"添加更多地址"以获取更多地址字段。这听起来像是formsets的工作,但我不知道这将如何与crispy一起工作-任何帮助将是伟大的!

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']

我成功地使用了下面的小部件。

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>

正如你在我的模板中看到的,我允许人们在视图中添加更多行。如果你的要求。POST包含addrow,然后复制您的请求。POST到post_data,所以它是可变的。

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

就是增加TOTAL_FORMS变量并将表单原样返回给用户。

最新更新