Обновление макетов на ходу

Макеты можно изменять, адаптировать и генерировать программно.

В следующих разделах будет рассказано, как выбирать части макета и обновлять их. Мы будем использовать этот API от экземпляра FormHelper, а не от самого макета. Основное поведение этого API заключается в выборе части макета для манипулирования и последующем подключении методов, изменяющих ее.

Выбор объектов компоновки с помощью фрагментов

Вы можете получить фрагмент макета, используя знакомый оператор [] Python:

form.helper[1:3]
form.helper[2]
form.helper[:-1]

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

Layout(
    Div('email')
)

Вы можете получить доступ к строке 'email', делая:

form.helper[0][0]

обернуть

Одним из полезных действий, которое можно применить к срезу, является wrap, которое оборачивает каждое выделенное поле, используя тип объекта компоновки и переданные параметры. Рассмотрим пример. Если бы у нас был такой макет:

Layout(
    'field_1',
    'field_2',
    'field_3'
)

Мы можем сделать:

form.helper[1:3].wrap(Field, css_class="hello")

В итоге у нас получился такой расклад:

Layout(
    'field_1',
    Field('field_2', css_class='hello'),
    Field('field_3', css_class='hello')
)

Обратите внимание, что wrap влияет на каждый выбранный объект компоновки, если вы хотите обернуть field_2 и field_3 вместе в объект компоновки Field, вам придется использовать вместе.

Следует помнить, что срез [1:3] просматривает только первый уровень глубины макета. Поэтому если предыдущая раскладка была такой:

Layout(
    'field_1',
    Div('field_2'),
    'field_3'
)

helper[1:3] вернет такой расклад:

Layout(
    'field_1',
    Field(Div('field_2'), css_class="hello"),
    Field('field_3', css_class="hello")
)

Параметры, переданные в wrap или wrap_together, будут использованы для создания объекта макета, который оборачивает выбранные поля. Вы можете передать args и kwargs. Если вы используете объект компоновки типа Fieldset, который требует строку в качестве обязательного первого аргумента, обертка не будет работать так, как нужно, если вы не предоставите текст легенды в качестве аргумента для wrap. Рассмотрим пример:

form.helper[1:3].wrap(Fieldset, "legend of the fieldset")

Также вы можете передать args и kwargs:

form.helper[1:3].wrap(Fieldset, "legend of the fieldset", css_class="fieldsets")

вместе

wrap_together оборачивает весь фрагмент внутри типа объекта layout с переданными параметрами. Рассмотрим пример. Если бы у нас был такой макет:

Layout(
    'field_1',
    'field_2',
    'field_3'
)

Мы можем сделать:

form.helper[0:3].wrap_together(Field, css_class="hello")

В итоге у нас получился такой расклад:

Layout(
    Field(
        'field_1',
        'field_2',
        'field_3',
        css_class='hello'
    )
)

обновление_атрибутов

Обновляет атрибуты каждого объекта компоновки, содержащегося в срезе:

Layout(
    'field_1',
    Field('field_2'),
    Field('field_3')
)

Мы можем сделать:

form.helper[0:3].update_attributes(css_class="hello")

Макет превратится в:

Layout(
    'field_1',
    Field('field_2', css_class='hello'),
    Field('field_3', css_class='hello')
)

Мы также можем применить его к имени поля, обернутому в объект компоновки:

form.helper['field_2'].update_attributes(css_class="hello")

Однако следующее не будет правильным:

form.helper['field_1'].update_attributes(css_class="hello")

Потому что это изменит Layout attrs. Это ваша работа, чтобы обернуть его правильно.

все

Этот метод выбирает все объекты компоновки первого уровня глубины:

form.helper.all().wrap(Field, css_class="hello")

Выбор имени поля

Если вы передадите строку с именем поля, то это имя поля будет искаться жадно по всем уровням глубины макета. Представьте, что у нас есть такой макет:

Layout(
    'field_1',
    Div(
        Div('password')
    ),
    'field_3'
)

Если мы сделаем:

form.helper['password'].wrap(Field, css_class="hero")

Предыдущий макет станет:

Layout(
    'field_1',
    Div(
        Div(
            Field('password', css_class="hero")
        )
    ),
    'field_3'
)

фильтр

Этот метод позволит вам отфильтровать объекты компоновки по типу их класса, применяя к ним действия:

form.helper.filter(basestring).wrap(Field, css_class="hello")
form.helper.filter(Div).wrap(Field, css_class="hello")

Вы можете одновременно фильтровать несколько типов объектов компоновки:

form.helper.filter(basestring, Div).wrap(Div, css_class="hello")

По умолчанию filter не является жадным, поэтому он ищет только на первом уровне глубины. Но вы можете настроить его на поиск на разных уровнях глубины с помощью kwarg max_level (по умолчанию установлен на 0). Давайте посмотрим несколько примеров, чтобы прояснить это. Представьте, что у нас есть такая схема:

Layout(
    'field_1',
    Div(
        Div('password')
    ),
    'field_3'
)

Если бы мы это сделали:

form.helper.filter(basestring).wrap(Field, css_class="hello")

Только field_1 и field_3 будут обернуты, что приведет к:

Layout(
    Field('field_1', css_class="hello"),
    Div(
        Div('password')
    ),
    Field('field_3', css_class="hello"),
)

Если бы мы хотели искать глубже, обернув password, нам нужно было бы установить max_level на 2 или более:

form.helper.filter(basestring, max_level=2).wrap(Field, css_class="hello")

Другими словами, max_level указывает количество переходов, которые crispy-forms может сделать в пределах объекта компоновки для сопоставления. В данном случае попадание в первый Div будет одним переходом, а попадание в следующий Div будет вторым переходом, таким образом max_level=2.

Мы можем сделать фильтр жадным, заставив его искать как можно глубже, установив greedy в True:

form.helper.filter(basestring, greedy=True).wrap(Div, css_class="hello")

Параметры:

  • max_level: Целое число, представляющее количество переходов, которые crispy-forms должны делать при фильтрации. По умолчанию 0.

  • greedy: Булево значение, указывающее, следует ли фильтровать жадно или нет. По умолчанию False.

filter_by_widget

Сопоставляет все поля типа виджета. Этот метод предполагает, что вы используете помощник с присоединенной формой, см. раздел FormHelper с прикрепленной формой (макет по умолчанию), вы можете фильтровать по типу виджета, делая:

form.helper.filter_by_widget(forms.PasswordInput).wrap(Field, css_class="hero")

filter_by_widget по умолчанию является жадным, поэтому он ищет в глубину. Рассмотрим пример использования, представим, что у нас есть такой Layout:

Layout(
    'username',
    Div('password1'),
    Div('password2')
)

Если предположить, что поля password1 и password2 используют виджет PasswordInput, то получится:

Layout(
    'username',
    Div(Field('password1', css_class="hero")),
    Div(Field('password2', css_class="hero"))
)

Интересным примером реального использования здесь было бы обернуть все SelectInputs пользовательским ChosenField, который отображает поле, используя поле, совместимое с chosenjs.

исключить_по_виджету

Исключает все поля типа виджета. Этот метод предполагает, что вы используете помощник с присоединенной формой, см. раздел FormHelper с прикрепленной формой (макет по умолчанию):

form.helper.exclude_by_widget(forms.PasswordInput).wrap(Field, css_class="hero")

exclude_by_widget по умолчанию является жадным, поэтому он ищет в глубину. Рассмотрим пример использования, представим, что у нас есть такой Layout:

Layout(
    'username',
    Div('password1'),
    Div('password2')
)

Если предположить, что поля password1 и password2 используют виджет PasswordInput, то получится:

Layout(
    Field('username', css_class="hero"),
    Div('password1'),
    Div('password2')
)

Манипулирование макетом

Помимо выбора объектов макета и применения к ним действий, вы также можете легко манипулировать самими макетами и объектами макета, как если бы это были списки. Мы будем делать это не из помощника, а из самого макета и объектов макета. Считайте это API более низкого уровня.

Все объекты макета, которые могут обертывать другие, содержат внутренний атрибут fields, который является списком, а не словарем, как в формах Django. Вы можете легко применять к ним любые списковые методы. Имейте в виду, что Layout ведет себя так же, как и другие объекты компоновки, такие как Div, с той лишь разницей, что он является корнем дерева.

Вот как можно заменить объект макета на другой:

layout[0][3][1] = Div('field_1')

Вот как можно добавить один объект макета в конец Layout:

layout.append(HTML("<p>whatever</p>"))

Вот как можно добавить один объект макета в конец другого объекта макета:

layout[0].append(HTML("<p>whatever</p>"))

Вот как можно добавить несколько объектов макета в макет:

layout.extend([
    HTML("<p>whatever</p>"),
    Div('add_field_on_the_go')
])

Вот как можно добавить несколько объектов макета к другому объекту макета:

layout[0][2].extend([
    HTML("<p>whatever</p>"),
    Div('add_field_on_the_go')
])

Вот как можно удалить второй объект макета в Layout:

layout.pop(1)

Вот как следует удалять второй объект макета внутри второго объекта макета:

layout[1].pop(1)

Вот как можно вставить объект макета во вторую позицию макета Layout:

layout.insert(1, HTML("<p>whatever</p>"))

Вот как можно вставить объект макета во вторую позицию второго объекта макета:

layout[1].insert(1, HTML("<p>whatever</p>"))

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

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

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