I have a django Formset that I'd like to layout in the middle of another form. I'm using django-crispy-forms to set the layout in the parent form's __init__
:
我有一个django Formset,我想在另一个窗体中间进行布局。我正在使用django-crispy窗体来设置父窗体的__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'))
My template simply renders the form using the {% crispy %}
tag.
我的模板只是使用{% crispy %}标签呈现表单。
I'd like to know how I should incorporate the formset. Should I instantiate it in the above init function? How do I refer to it there?
我想知道我该如何合并这张表格。我应该在上面的init函数中实例化它吗?我怎么说呢?
There are other examples of form and formset combos online that have one render after the other serially, but I'm wondering whether I can have more control over how they fit together with crispy's layout.
还有其他的表单和formset combos在线的例子,它们有一个接一个的呈现,但是我想知道我是否可以更好地控制它们如何与crispy的布局结合在一起。
4 个解决方案
#1
16
I solved this without modifying Crispy Forms, by creating a new field type that renders a formset:
我没有修改Crispy表单就解决了这个问题,创建了一个新的字段类型,它呈现了一个formset:
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}))
It needs a template to render the formset, which gives you control over exactly how it's rendered:
它需要一个模板来呈现格式集,它可以让你精确地控制它是如何呈现的:
{% load crispy_forms_tags %}
<div class="formset">
{% crispy formset %}
<input type="button" name="add" value="Add another" />
</div>
You can use it to embed a formset in your layouts just like any other Crispy layout element:
您可以使用它在布局中嵌入一个formset,就像其他的Crispy布局元素一样:
self.helper.layout = Layout(
MultiField(
"Education",
Formset('education'),
),
#2
3
This is currently not supported in crispy-forms. Your only option would be to use |as_crispy_field
filter (not documented yet, sorry).
这是目前不支持的保鲜格式。您唯一的选择是使用|as_crispy_field过滤器(还没有文档说明,抱歉)。
I have started development of this feature for {% crispy %}
tag and in a feature branch, it's all explained here: https://github.com/maraujop/django-crispy-forms/issues/144
我已经开始为{% crispy %}标签开发这个特性,并且在一个特性分支中,这里解释了它:https://github.com/maraujop/django-crispy-forms/issues/144
I'm looking for feedback, so if you are still interested, feel free to post.
我正在寻找反馈,所以如果你仍然感兴趣的话,可以发表意见。
#3
2
A slight modification to the earlier answer by qris.
qris对之前的答案做了一点修改。
This update (as suggested by Alejandro) will allow for our custom Formset Layout Object to use a FormHelper object to control how the formset's fields are rendered.
这个更新(正如Alejandro建议的)将允许我们的自定义Formset布局对象使用FormHelper对象来控制Formset字段的呈现方式。
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())
Template (used to render formset):
模板(用于呈现formset):
{% load crispy_forms_tags %}
<div class="formset">
{% if helper %}
{% crispy formset helper %}
{% else %}
{{ formset|crispy }}
{% endif %}
</div>
Now it can be used in any layout just like any other crispy forms layout object.
现在,它可以在任何布局中使用,就像任何其他酥脆的表单布局对象一样。
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'),
),
)
#4
0
Basing on above solution Formset(LayoutObject), you would combine django-dynamic-formset & crispy. On my order page I have:
基于以上的解决方案格式集(LayoutObject),您可以将django动态格式集和脆饼组合在一起。在我的订单页面上,我有:
- order's section part 1
- 第1部分订单的部分
- order's inline formset with dynamic-add forms
- order的内联形式与动态添加表单。
- order's section part N
- 订单的部分一部分N
Now it is simple and clear, ModelForms are:
现在它简单明了,模型形式如下:
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 helper layout:
形态层组辅助布局:
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
Add/delete inlines, saving order with formset operations work as expected.
添加/删除inlines,并按照预期的方式使用formset操作保存订单。
#1
16
I solved this without modifying Crispy Forms, by creating a new field type that renders a formset:
我没有修改Crispy表单就解决了这个问题,创建了一个新的字段类型,它呈现了一个formset:
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}))
It needs a template to render the formset, which gives you control over exactly how it's rendered:
它需要一个模板来呈现格式集,它可以让你精确地控制它是如何呈现的:
{% load crispy_forms_tags %}
<div class="formset">
{% crispy formset %}
<input type="button" name="add" value="Add another" />
</div>
You can use it to embed a formset in your layouts just like any other Crispy layout element:
您可以使用它在布局中嵌入一个formset,就像其他的Crispy布局元素一样:
self.helper.layout = Layout(
MultiField(
"Education",
Formset('education'),
),
#2
3
This is currently not supported in crispy-forms. Your only option would be to use |as_crispy_field
filter (not documented yet, sorry).
这是目前不支持的保鲜格式。您唯一的选择是使用|as_crispy_field过滤器(还没有文档说明,抱歉)。
I have started development of this feature for {% crispy %}
tag and in a feature branch, it's all explained here: https://github.com/maraujop/django-crispy-forms/issues/144
我已经开始为{% crispy %}标签开发这个特性,并且在一个特性分支中,这里解释了它:https://github.com/maraujop/django-crispy-forms/issues/144
I'm looking for feedback, so if you are still interested, feel free to post.
我正在寻找反馈,所以如果你仍然感兴趣的话,可以发表意见。
#3
2
A slight modification to the earlier answer by qris.
qris对之前的答案做了一点修改。
This update (as suggested by Alejandro) will allow for our custom Formset Layout Object to use a FormHelper object to control how the formset's fields are rendered.
这个更新(正如Alejandro建议的)将允许我们的自定义Formset布局对象使用FormHelper对象来控制Formset字段的呈现方式。
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())
Template (used to render formset):
模板(用于呈现formset):
{% load crispy_forms_tags %}
<div class="formset">
{% if helper %}
{% crispy formset helper %}
{% else %}
{{ formset|crispy }}
{% endif %}
</div>
Now it can be used in any layout just like any other crispy forms layout object.
现在,它可以在任何布局中使用,就像任何其他酥脆的表单布局对象一样。
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'),
),
)
#4
0
Basing on above solution Formset(LayoutObject), you would combine django-dynamic-formset & crispy. On my order page I have:
基于以上的解决方案格式集(LayoutObject),您可以将django动态格式集和脆饼组合在一起。在我的订单页面上,我有:
- order's section part 1
- 第1部分订单的部分
- order's inline formset with dynamic-add forms
- order的内联形式与动态添加表单。
- order's section part N
- 订单的部分一部分N
Now it is simple and clear, ModelForms are:
现在它简单明了,模型形式如下:
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 helper layout:
形态层组辅助布局:
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
Add/delete inlines, saving order with formset operations work as expected.
添加/删除inlines,并按照预期的方式使用formset操作保存订单。