Макеты

Основы

Django-crispy-forms определяет еще один мощный класс под названием Layout, который позволяет вам изменять способ отображения полей формы. Это позволяет вам установить порядок полей, обернуть их в div’ы или другие структуры, добавить html, установить идентификаторы, классы или атрибуты на то, что вы хотите, и т.д. И все это без написания собственного шаблона формы, используя программные макеты. Просто прикрепите макет к хелперу, макеты необязательны, но, вероятно, это самое мощное, что может предложить django-crispy-forms.

Layout строится с помощью объектов компоновки, которые можно представить как компоненты формы.

Все эти компоненты будут описаны позже в Универсальные объекты компоновки, сейчас о них нужно знать то, что каждый компонент отображает свой шаблон и имеет свое назначение. Давайте напишем пару различных макетов для нашей формы, продолжая пример с классом формы (обратите внимание, что полная форма снова не показана).

Некоторые объекты макета специфичны для пакета шаблонов. Например, ButtonHolder предназначен для uni_form template_pack, а FormActions - для bootstrap template pack.

Давайте добавим макет в наш помощник:

from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Fieldset, ButtonHolder, Submit

class ExampleForm(forms.Form):
    [...]
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.layout = Layout(
            Fieldset(
                'first arg is the legend of the fieldset',
                'like_website',
                'favorite_number',
                'favorite_color',
                'favorite_food',
                'notes'
            ),
            ButtonHolder(
                Submit('submit', 'Submit', css_class='button white')
            )
        )

Когда мы отображаем форму сейчас, используя:

{% load crispy_forms_tags %}
{% crispy example_form %}

Мы получим поля, обернутые в набор полей, легенда которого будет установлена на „first arg - легенда набора полей“. Порядок полей будет следующим: like_website, favorite_number, favorite_color, favorite_food и notes. Мы также получаем кнопку отправки, обернутую в <div class="buttonHolder">, которую uni-form CSS позиционирует приятным образом. Для этой кнопки установлен класс CSS button white.

Это только вершина айсберга: теперь представьте, что вы хотите добавить объяснение того, что такое заметки, вы можете использовать HTML layout object:

Layout(
    Fieldset(
        'Tell us your favorite stuff {{ username }}',
        'like_website',
        'favorite_number',
        'favorite_color',
        'favorite_food',
        HTML("""
            <p>We use notes to get better, <strong>please help us {{ username }}</strong></p>
        """),
        'notes'
    )
)

Как вы заметили, легенда набора полей учитывает контекст, и вы можете написать ее так, как если бы это был фрагмент шаблона, в котором будет отображаться форма. Объект HTML добавит сообщение перед вводом примечаний, и он также учитывает контекст. Обратите внимание, как можно обернуть объекты макета в другие объекты макета. Объекты макета Fieldset, Div, MultiField и ButtonHolder могут содержать внутри себя другие объекты макета. Давайте сделаем альтернативный макет для той же формы:

Layout(
    MultiField(
        'Tell us your favorite stuff {{ username }}',
        Div(
            'like_website',
            'favorite_number',
            css_id = 'special-fields'
        ),
        'favorite_color',
        'favorite_food',
        'notes'
    )
)

На этот раз мы используем MultiField, который представляет собой объект макета, который, как правило, можно использовать в тех же местах, что и Fieldset. Основное отличие заключается в том, что здесь все поля обернуты в div, и при возникновении ошибок в отправке формы они отображаются в списке, а не вокруг каждого поля. Иногда лучший способ понять, что делают объекты компоновки, - это просто попробовать их и немного поиграть с ними.

Атрибуты объектов макета

Всем объектам макета можно задать kwargs, которые будут использоваться в качестве атрибутов HTML. Например, если вы хотите отключить автозаполнение для поля, вы можете сделать следующее:

Field('field_name', autocomplete='off')

Если вы хотите задать html-атрибуты, разделяя слова дефисами, например data-name, поскольку Python не поддерживает дефисы в аргументах ключевых слов, а дефисы являются обычным обозначением в HTML, подчеркивания будут переведены в дефисы, поэтому вы сделаете:

Field('field_name', data_name="whatever")

Поскольку class является зарезервированным ключевым словом в Python, для него нужно использовать css_class. Например:

Field('field_name', css_class="black-fields")

А атрибут id устанавливается с помощью css_id:

Field('field_name', css_id="custom_field_id")

Универсальные объекты компоновки

Они находятся в модуле crispy_forms.layout. Это объекты верстки, которые не являются специфическими для пакета шаблонов. Мы будем рассматривать их по одному, показывая примеры использования:

  • Div: Он оборачивает поля в <div>:

    Div('form_field_1', 'form_field_2', 'form_field_3', ...)
    

NOTE В основном во всех объектах макета можно задать kwargs, которые будут использоваться в качестве атрибутов HTML. Поскольку class является зарезервированным ключевым словом в Python, для него нужно использовать css_class. Например:

Div('form_field_1', style="background: white;", title="Explication title", css_class="bigdivs")
  • HTML: Очень мощный объект компоновки. Используйте его для рендеринга чистого html-кода. Фактически он ведет себя как шаблон Django и имеет доступ ко всему контексту страницы, на которой отображается форма. Этот объект макета не принимает никаких дополнительных параметров, кроме html для рендеринга, вы не можете установить атрибуты html, как в Div:

    HTML("{% if success %} <p>Operation was successful</p> {% endif %}")
    

Предупреждение

Обратите внимание, что это отображается в отдельном шаблоне, поэтому если вы используете пользовательские теги шаблонов или фильтры, не забудьте добавить свои {% load custom_tags %}.

  • Поле: Чрезвычайно полезный объект компоновки. Вы можете использовать его для установки атрибутов поля или отображения конкретного поля с помощью пользовательского шаблона. Таким образом вы избежите необходимости явно переопределять виджет поля и передавать уродливый словарь attrs:

    Field('password', id="password-field", css_class="passwordfields", title="Explanation")
    Field('slider', template="custom-slider.html")
    

Этот объект компоновки можно использовать для простого расширения виджетов Django. Если вы хотите отобразить поле формы Django как скрытое, вы можете просто сделать:

Field('field_name', type="hidden")

Если вам нужны атрибуты HTML5, вы можете легко сделать это, используя подчеркивание data_name kwarg здесь превратится в data-name в вашем сгенерированном html:

Field('field_name', data_name="special")

Поля в bootstrap обернуты в <div class="control-group">. Вы можете захотеть установить дополнительные классы в этом div, для этого сделайте:

Field('field_name', wrapper_class="extra-class")
  • Submit: Используется для создания кнопки отправки. Первый параметр - атрибут name кнопки, второй параметр - атрибут value:

    Submit('search', 'SEARCH')
    

Переводится как:

<input type="submit" name="search" value="SEARCH" class="submit submitButton" id="submit-id-search" />
  • Скрытый: Используется для создания скрытого ввода:

    Hidden('name', 'value')
    
  • Кнопка: Создает кнопку:

    Button('name', 'value')
    
  • Сброс: Используется для создания входа сброса:

    reset = Reset('name', 'value')
    
  • Fieldset: Он оборачивает поля в <fieldset>. Первый параметр - текст для легенды набора полей, как мы уже говорили, он ведет себя как шаблон Django:

    Fieldset("Text for the legend {{ username }}",
        'form_field_1',
        'form_field_2'
    )
    

Объекты макета униформы

Эти живут в модуле crispy_forms.layout. Возможно, в будущем они будут перенесены в модуль uni_form:

  • ButtonHolder: Он оборачивает поля в <div class=”buttonHolder”>, которые uni-form позиционирует красивым образом. Именно сюда помещаются кнопки отправки формы в uni-form:

    ButtonHolder(
        HTML('<span class="hidden">✓ Saved data</span>'),
        Submit('save', 'Save')
    )
    
  • MultiField: Обертывает поля в <div> с меткой сверху. При возникновении ошибок при отправке формы отображает их в виде списка, а не каждую из них вокруг поля:

    MultiField("Text for the label {{ username }}",
        'form_field_1',
        'form_field_2'
    )
    

Объекты макета Bootstrap

Эти живут под модулем crispy_forms.bootstrap.

  • FormActions: Обертывает поля в <div class="form-actions">. Обычно используется для обертывания кнопок формы:

    FormActions(
        Submit('save', 'Save changes'),
        Button('cancel', 'Cancel')
    )
    
../_images/form_actions.png
  • AppendedText: Эта функция отображает вводимый в bootstrap добавляемый текст. Первым параметром является имя поля, к которому нужно добавить добавляемый текст, затем добавляемый текст, который может быть HTML. Есть дополнительный параметр active'', по умолчанию установленный в ``False'', который вы можете установить в булево значение, чтобы сделать добавляемый текст активным. Смотрите ``input_size, чтобы изменить размер этого ввода:

    AppendedText('field_name', 'appended text to show')
    AppendedText('field_name', '$', active=True)
    
../_images/appended_text.png
  • PrependedText: Создает вводимый в bootstrap текст. Первым параметром является имя поля, к которому нужно добавить добавляемый текст, затем добавляемый текст, который может быть HTML. Есть дополнительный параметр active'', по умолчанию установленный в ``False'', который вы можете установить в булево значение, чтобы сделать добавляемый текст активным. Смотрите ``input_size, чтобы изменить размер этого ввода:

    PrependedText('field_name', '<b>Prepended text</b> to show')
    PrependedText('field_name', '@', placeholder="username")
    
../_images/prepended_text.png
  • PrependedAppendedText: Выводит комбинированный предактивированный и апплицированный текст. Первым параметром является имя поля, затем препарированный текст и, наконец, добавленный текст. Смотрите input_size, чтобы изменить размер этого ввода:

    PrependedAppendedText('field_name', '$', '.00'),
    
../_images/appended_prepended_text.png
  • InlineCheckboxes: Он отображает поле Django forms.MultipleChoiceField, используя встроенные флажки:

    InlineCheckboxes('field_name')
    
../_images/inline_checkboxes.png
  • InlineRadios: Он отображает поле Django forms.ChoiceField с его виджетом, установленным на forms.RadioSelect, используя встроенные радиокнопки:

    InlineRadios('field_name')
    
../_images/inline_radios.jpg
  • StrictButton: Он отображает кнопку, используя <button> html, а не input. По умолчанию type устанавливается в button, а class устанавливается в btn:

    StrictButton("Button's content", name="go", value="go", css_class="extra")
    StrictButton('Success', css_class="btn-success")
    
../_images/strict_button.png
  • Поле с кнопками: Вы можете создать вход, связанный с кнопками:

    FieldWithButtons('field_name', StrictButton("Go!"))
    
../_images/field_with_buttons.png
  • Таб и TabHolder: Tab отображает вкладку, различные вкладки должны быть обернуты в TabHolder для автоматического функционирования JavaScript, также вам понадобится bootstrap-tab.js, включенный в ваши статические файлы:

    TabHolder(
        Tab('First Tab',
            'field_name_1',
            Div('field_name_2')
        ),
        Tab('Second Tab',
            Field('field_name_3', css_class="extra")
        )
    )
    
../_images/tab_and_tabholder.jpg
  • Accordion и AccordionGroup: AccordionGroup отображает панель аккордеона, различные группы должны быть обернуты в Accordion для автоматического функционирования JavaScript, также вам понадобится bootstrap-tab.js, включенный в ваши статические файлы:

    Accordion(
        AccordionGroup('First Group',
            'radio_buttons'
        ),
        AccordionGroup('Second Group',
            Field('field_name_3', css_class="extra")
        )
    )
    
../_images/accordiongroup_and_accordion.jpg
  • Алерт: Alert генерирует разметку в виде диалога предупреждения:

    Alert(content="<strong>Warning!</strong> Best check yo self, you're not looking too good.")
    
../_images/alert.png
  • UneditableField: UneditableField отображает поле с возможностью отключения, используя bootstrap uneditable-input class:

    UneditableField('text_input', css_class='form-control-lg')
    
../_images/field_disabled.png

Размер группы полей

Размер группы полей: По умолчанию используются стандартные размеры входных групп Bootstrap. Чтобы изменить размер группы ввода (AppendedText, PrependedText, PrependedAppendedText), добавьте соответствующий CSS-класс:

# Bootstrap 3 - Inputs and spans need size class. Use `css_class`.
PrependedText('field_name', StrictButton("Go!"), css_class="input-sm")
PrependedText('field_name', StrictButton("Go!"), css_class="input-lg")

# Bootstrap 4 - Wrapping div needs size class. Use `input_size`.
PrependedText('field_name', StrictButton("Go!"), input_size="input-group-sm")
PrependedText('field_name', StrictButton("Go!"), input_size="input-group-lg")

Переопределение шаблонов объектов компоновки

Упомянутый набор Универсальные объекты компоновки был тщательно разработан, чтобы быть гибким, совместимым со стандартами и поддерживать возможности формы Django. Каждый объект Layout связан с отдельным шаблоном, который находится в каталоге templates/{{ TEMPLATE_PACK_NAME }}/layout/.

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

  • Глобально: Вы переопределяете шаблон объекта макета, для всех экземпляров этого объекта макета вы используете:

    from crispy_forms.layout import Div
    Div.template = 'my_div_template.html'
    
  • Индивидуально: Вы можете переопределить шаблон для определенного объекта макета в макете:

    Layout(
        Div(
            'field1',
            'field2',
            template='my_div_template.html'
        )
    )
    
  • Переопределение каталога шаблонов: Это означает имитацию структуры каталога crispy-forms в вашем проекте, а затем копирование туда шаблонов, которые вы хотите переопределить, и окончательное редактирование этих копий. Если вы используете этот подход, лучше просто скопировать и отредактировать шаблоны, которые вы будете настраивать, а не все.

Переопределение шаблонов проекта

Вам нужно различать шаблоны объектов макета и шаблоны django-crispy-forms. Есть некоторые шаблоны, которые живут в templates/{{ TEMPLATE_PACK_NAME }}, которые определяют структуру формы/формсета, как отображаются поля или ошибки и т.д. Они добавляют очень мало логики и являются базовыми обертками для django-crispy-forms. Они добавляют очень мало логики и являются практически базовыми обертками для остальных возможностей django-crispy-forms. Чтобы переопределить эти параметры, у вас есть две возможности:

  • Атрибуты template и field_template в FormHelper: Начиная с версии 1.3.0 вы можете переопределить шаблон формы/формсета и шаблон поля с помощью вспомогательных атрибутов, см. раздел Атрибуты помощника, которые вы можете установить. С помощью этого вы можете изменить одну конкретную форму или все формы вашего проекта (например, создав пользовательский базовый класс FormHelper).

  • Переопределение каталога шаблонов: Это работает так же, как описано в разделе Переопределение шаблонов объектов компоновки. Если вы адаптируете шаблоны crispy-forms к популярному пакету шаблонов с открытым исходным кодом, который вы используете, отправьте его, чтобы больше людей могли воспользоваться им.

  • Создание пакета шаблонов: Возможно, вы захотите использовать crispy-формы с вашим любимым CSS-фреймворком или CSS вашей компании. Для этого вам нужно быть хорошо знакомым с crispy-forms, объектами макета и их шаблонами. Вы, вероятно, захотите начать с одного из существующих пакетов шаблонов, вероятно, bootstrap. Представьте, что ваш пакет шаблонов называется chocolate, это означает, что вы, вероятно, захотите, чтобы ваш корневой каталог был назван так же. Для использования вашего пакета шаблонов, вам нужно будет установить переменную CRISPY_TEMPLATE_PACK = 'chocolate' в вашем файле настроек, а также установить CRISPY_ALLOWED_TEMPLATE_PACKS = ('bootstrap', 'chocolate'). Таким образом, crispy-forms будет знать, что вы хотите использовать свой собственный пакет шаблонов, что это разрешенный пакет и где его искать.

Создание собственных объектов макета

Универсальные объекты компоновки, поставляемые в комплекте с django-crispy-forms, представляют собой набор наиболее заметных компонентов, которые строят форму. Вы, вероятно, сможете сделать все, что вам нужно, комбинируя их. В любом случае, вы можете захотеть создать свои собственные компоненты, для этого вам понадобится хорошее знание django-crispy-forms. Каждый объект макета должен иметь метод render. Его прототип должен быть:

def render(self, form, form_style, context):

Официальные объекты макета находятся в layout.py и bootstrap.py, вы можете взглянуть на них, чтобы полностью понять, как действовать дальше. Но в общих чертах, объект макета - это шаблон, отображаемый с некоторыми переданными параметрами.

Если вам пришла в голову хорошая идея и вы разработали объект компоновки, который, по вашему мнению, может быть полезен другим, пожалуйста, откройте проблему или отправьте запрос на исправление, чтобы django-crispy-forms стал лучше.

Составление макетов

Представьте, что у вас есть несколько форм, которые используют большой кусок одного и того же макета. Есть простой способ создать Layout, повторно использовать и расширить его. Вы можете иметь Layout как компонент другого Layout. Вы можете создать этот общий кусок разными способами. Как отдельный класс:

class CommonLayout(Layout):
    def __init__(self, *args, **kwargs):
        super().__init__(
            MultiField("User data",
                'username',
                'lastname',
                'age'
            )
        )

Может быть, достаточно экземпляра объекта:

common_layout = Layout(
    MultiField("User data",
        'username',
        'lastname',
        'age'
    )
)

Тогда вы можете сделать следующее:

helper.layout = Layout(
    CommonLayout(),
    Div(
        'favorite_food',
        'favorite_bread',
        css_id = 'favorite-stuff'
    )
)

Или:

helper.layout = Layout(
    common_layout,
    Div(
        'professional_interests',
        'job_description',
    )
)

Мы определили макет и использовали его как часть другого макета, что означает, что эти два макета будут начинаться одинаково, а затем расширять макет разными способами.

Вернуться на верх