【问题标题】:django crispy forms: Nesting a formset within a formdjango 脆表单:在表单中嵌套表单集
【发布时间】:2013-02-15 22:14:04
【问题描述】:

我有一个 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 函数中实例化它吗?我如何在那里引用它?

网上有other examples的form和formset组合,一个接一个地渲染,但我想知道我是否可以更好地控制它们如何与crispy的布局结合在一起。

【问题讨论】:

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


    【解决方案1】:

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

    我已经开始为{% crispy %} 标记开发此功能,并在功能分支中进行了说明:https://github.com/maraujop/django-crispy-forms/issues/144

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

    【讨论】:

      【解决方案2】:

      我通过创建一个呈现表单集的新字段类型,在不修改 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'),
          ),
      

      【讨论】:

      • 我建议的一个小修改是为formset_helper 添加一个可选参数到Formset LayoutObject。以同样的方式工作。这样你也可以传入自定义助手。
      • 这些代码放在哪些文件中?我在哪里放置 formset.html?什么是 Formset('education');它来自哪里?
      【解决方案3】:

      qris 对之前的答案稍作修改。

      此更新(如 Alejandro 所建议)将允许我们的自定义 Formset Layout Object 使用 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'),
          ),
      )
      

      【讨论】:

      • 谢谢你! :) 你会在哪里实例化my_formset_helper?我试过self.my_formset_helper(对于ModelForm的__init__以及my_formset_helper,但似乎没有什么坚持。干杯
      • @nuts 您可以在视图的get_context_data 中进行操作。 my_formset_helper 应该是一个上下文变量,它应该是用于表单集的助手。但是,我发现使用表单集的帮助器创建一个表单并将其传递给inlineformset_factory(..., form=MyInlineModelForm),然后在模板中包含{% crispy formset formset.form.helper %} 会更简洁。
      【解决方案4】:

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

      • 订单部分第 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"))
      

      Formset 辅助布局:

      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
      

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

      【讨论】:

        猜你喜欢
        • 2020-08-21
        • 2018-08-26
        • 1970-01-01
        • 1970-01-01
        • 2020-06-06
        • 2011-09-12
        • 2012-09-08
        • 2014-02-28
        • 1970-01-01
        相关资源
        最近更新 更多