15

我有一个 django Formset,我想在另一个表单的中间进行布局。我正在使用django-crispy-forms在父表单中设置布局__init__

from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit, Layout, Field, Div
def __init__(self, *args, **kwargs):
    self.helper = FormHelper()
    self.helper.layout = Layout(
        Div(
            Div(Field('foo'), css_class='span3'),
            Div(Field('bar'), css_class='span4'),
            css_class='row'
            ),
        Field('baz', css_class='span1'),
            ...
            )
    self.helper.add_input(Submit('submit', 'Submit', css_class='btn btn-primary offset4'))

{% crispy %}我的模板只是使用标签呈现表单。

我想知道我应该如何合并表单集。我应该在上面的 init 函数中实例化它吗?我如何在那里引用它?

网上还有其他表单和表单集组合的示例,它们依次呈现一个又一个渲染,但我想知道我是否可以更好地控制它们如何与脆皮的布局结合在一起。

4

4 回答 4

26

我在不修改 Crispy Forms 的情况下解决了这个问题,方法是创建一个呈现表单集的新字段类型:

from crispy_forms.layout import LayoutObject, TEMPLATE_PACK

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):
        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]
        return render_to_string(self.template, Context({'wrapper': self,
            'formset': formset}))

它需要一个模板来呈现表单集,这使您可以准确控制它的呈现方式:

{% load crispy_forms_tags %}

<div class="formset">
    {% crispy formset %}
    <input type="button" name="add" value="Add another" />
</div>

您可以使用它在布局中嵌入表单集,就像任何其他 Crispy 布局元素一样:

self.helper.layout = Layout(
    MultiField(
        "Education",
        Formset('education'),
    ),
于 2014-02-26T21:40:10.467 回答
8

qris对先前答案的轻微修改。

此更新(由Alejandro建议)将允许我们的自定义Formset 布局对象使用FormHelper对象来控制表单集字段的呈现方式。

from crispy_forms.layout import LayoutObject

from django.template.loader import render_to_string


class Formset(LayoutObject):
    """ 
    Renders an entire formset, as though it were a Field.
    Accepts the names (as a string) of formset and helper as they
    are defined in the context

    Examples:
        Formset('contact_formset')
        Formset('contact_formset', 'contact_formset_helper')
    """

    template = "forms/formset.html"

    def __init__(self, formset_context_name, helper_context_name=None,
                 template=None, label=None):

        self.formset_context_name = formset_context_name
        self.helper_context_name = helper_context_name

        # 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, **kwargs):
        formset = context.get(self.formset_context_name)
        helper = context.get(self.helper_context_name)
        # closes form prematurely if this isn't explicitly stated
        if helper:
            helper.form_tag = False

        context.update({'formset': formset, 'helper': helper})
        return render_to_string(self.template, context.flatten())

模板(用于渲染表单集):

{% load crispy_forms_tags %}

<div class="formset">
  {% if helper %}
    {% crispy formset helper %}
  {% else %}
    {{ formset|crispy }}
  {% endif %}
</div>

现在它可以在任何布局中使用,就像任何其他清晰的表单布局对象一样。

self.helper.layout = Layout(
    Div(
        Field('my_field'),
        Formset('my_formset'),
        Button('Add New', 'add-extra-formset-fields'),
    ),
)

# or with a helper
self.helper.layout = Layout(
    Div(
        Field('my_field'),
        Formset('my_formset', 'my_formset_helper'),
        Button('Add New', 'add-extra-formset-fields'),
    ),
)
于 2017-04-21T18:30:06.977 回答
5

脆皮形式目前不支持此功能。您唯一的选择是使用|as_crispy_field过滤器(尚未记录,抱歉)。

我已经开始为{% crispy %}标签和功能分支开发此功能,这一切都在这里解释:https ://github.com/maraujop/django-crispy-forms/issues/144

我正在寻找反馈,所以如果您仍然感兴趣,请随时发布。

于 2013-05-12T12:49:31.090 回答
0

基于上述解决方案 Formset(LayoutObject),您将结合 django-dynamic-formset &crispy。在我的订单页面上,我有:

  • 订单部分第 1 部分
  • 带有动态添加表单的订单内联表单集
  • 订单的第 N 部分

现在简单明了,ModelForms 是:

class OrderTestForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
    super(OrderTestForm, self).__init__(*args, **kwargs)
    self.helper = FormHelper(self)
    self.helper.form_tag = True
    self.helper.html5_required = True
    self.helper.form_action = 'test_main'
    self.helper.layout = Layout(
        'product_norms', #section 1
        'reference_other', #section 1
        # rest of the section 1 fields
        Formset('samples', 'helper'), # inline dynamic forms
        'checkbox_is_required' # start of section N
        # other order sections fields
    )
    self.helper.add_input(Submit("submit", "Save order"))

表单集助手布局:

class SamplesFormSetHelper(FormHelper):
def __init__(self, *args, **kwargs):
    super(SamplesFormSetHelper, self).__init__(*args, **kwargs)
    self.form_method = 'post'
    self.html5_required = True
    self.layout = Layout(
        Fieldset('',
        'description',
        'product', # foreign key
        'DELETE', # delete django-dynamic-formset
        css_class="formset_row"), # add-rows 
    )
    self.form_tag = False
    self.render_required_fields = False

添加/删除内联,使用表单集操作保存订单按预期工作。

于 2018-02-21T02:23:06.583 回答