Django хрустящие формы: вложение формы в форму
У меня есть 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? Как я могу там называть?
Есть другие примеры форм и форм комбо в Интернете, которые имеют один рендер после другого серийно, но мне интересно, могу ли я имеют больший контроль над тем, как они сочетаются с хрустящим макетом.
Ответы
Ответ 1
Я решил это, не изменяя Crispy Forms, создав новый тип поля, который отображает formet:
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>
Вы можете использовать его для встраивания набора форм в свои макеты, как и любой другой элемент хрустящего макета:
self.helper.layout = Layout(
MultiField(
"Education",
Formset('education'),
),
Ответ 2
В настоящее время это не поддерживается в хрустящих формах. Единственный вариант - использовать фильтр |as_crispy_field
(пока не задокументирован, извините).
Я начал разработку этой функции для тега {% crispy %}
и в ветки признака, здесь все объяснено: https://github.com/maraujop/django-crispy-forms/issues/144 p >
Я ищу обратную связь, поэтому, если вы все еще заинтересованы, не стесняйтесь публиковать сообщения.
Ответ 3
Небольшая модификация более раннего ответа 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'),
),
)
Ответ 4
Основываясь на вышеприведенном решении 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
Добавить/удалить встроенные строки, сохранить порядок при работе с наборами форм, как и следовало ожидать.