Обновление макетов на ходу¶
Макеты можно изменять, адаптировать и генерировать программно.
В следующих разделах будет рассказано, как выбирать части макета и обновлять их. Мы будем использовать этот 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>"))
Предупреждение
Всегда помните, что если вы собираетесь манипулировать помощником или макетом в представлении или любой части вашего кода, лучше использовать переменную уровня экземпляра.