Джанго хрустящие формы: вложение набора форм в форму

У меня есть 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 %}.

Я хотел бы знать, как мне включить набор форм. Должен ли я создать экземпляр в приведенной выше функции инициализации? Как я могу обратиться к нему там?

Существуют другие примеры онлайн-комбинаций форм и наборов форм, которые имеют один рендеринг за другим последовательно, но мне интересно, могу ли я иметь больше контроля над тем, как они сочетаются с макетом хрустящего.


person Neil    schedule 01.03.2013    source источник


Ответы (4)


Я решил эту проблему, не изменяя 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'),
    ),
person qris    schedule 26.02.2014
comment
Одно небольшое изменение, которое я бы порекомендовал, это добавить необязательный параметр в Formset LayoutObject для formset_helper. Это работает так же. Таким образом, вы также можете передать пользовательский помощник. - person Alejandro; 10.07.2015
comment
В какие файлы помещаются эти коды? Где разместить formset.html? И что такое Formset('education'); откуда это идет? - person Gourav Chawla; 13.02.2017

Небольшая модификация предыдущего ответа qris.

Это обновление (предложенное Алехандро) позволит использовать наш собственный 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'),
    ),
)
person ch00kz    schedule 21.04.2017
comment
Спасибо тебе за это! :) Где бы вы создали экземпляр my_formset_helper? Я пробовал self.my_formset_helper (для ModelForm __init__, а также только my_formset_helper, но, кажется, ничего не прилипает. Ура - person nuts; 22.03.2019
comment
@nuts, вы можете сделать это в представлении get_context_data. my_formset_helper должна быть переменной контекста, и она должна быть помощником, используемым для набора форм. Однако мне показалось чище создать форму с помощником для набора форм и передать ее inlineformset_factory(..., form=MyInlineModelForm), а затем добавить {% crispy formset formset.form.helper %} в шаблон. - person vikki; 06.05.2019

В настоящее время это не поддерживается в хрустящих формах. Единственным вариантом будет использование фильтра |as_crispy_field (извините, еще не задокументировано).

Я начал разработку этой функции для тега {% crispy %} и в ветке функций, все это объясняется здесь: https://github.com/maraujop/django-crispy-forms/issues/144

Я ищу отзывы, поэтому, если вы все еще заинтересованы, не стесняйтесь публиковать.

person maraujop    schedule 12.05.2013

Основываясь на приведенном выше решении 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

Добавление/удаление встроенных строк, порядок сохранения с операциями набора форм работают должным образом.

person pthread    schedule 21.02.2018