О блоках StreamField BoundBlocks и значениях
Все типы блоков StreamField принимают параметр template для определения того, как они будут отображаться на странице. Однако для блоков, обрабатывающих базовые типы данных Python, такие как CharBlock и IntegerBlock, существуют некоторые ограничения на область действия шаблона, поскольку эти встроенные типы (str, int и т. д.) не могут быть «обучены» своему отображению в шаблоне. В качестве примера рассмотрим следующее определение блока:
class HeadingBlock(blocks.CharBlock):
class Meta:
template = 'blocks/heading.html'
где blocks/heading.html состоит из:
<h1>{{ value }}</h1>
Это даёт нам блок, который ведет себя как обычное текстовое поле, но оборачивает свой вывод в теги <h1> всякий раз, когда он отображается:
class BlogPage(Page):
body = StreamField([
# ...
('heading', HeadingBlock()),
# ...
], use_json_field=True)
{% load wagtailcore_tags %}
{% for block in page.body %}
{% if block.block_type == 'heading' %}
{% include_block block %} {# This block will output its own <h1>...</h1> tags. #}
{% endif %}
{% endfor %}
Такая организация — значение, которое предположительно представляет собой обычную строку текста, но имеет собственное пользовательское HTML-представление при выводе в шаблоне — обычно является очень сложной задачей в Python, но здесь она работает, потому что элементы, получаемые при итерации по StreamField, на самом деле не являются «родными» значениями блоков. Вместо этого каждый элемент возвращается как экземпляр BoundBlock — объекта, представляющего пару значения и определения блока. Следя за определением блока, BoundBlock всегда знает, какой шаблон отобразить. Для получения базового значения — в данном случае текстового содержимого заголовка — необходимо обратиться к block.value. Действительно, если вы выведете {% include_block block.value %} на странице, вы увидите, что оно отображается как обычный текст без тегов <h1>.
(Точнее, элементы, возвращаемые при итерации по StreamField, являются экземплярами класса StreamChild, который предоставляет свойство block_type , а также value.)
Опытные разработчики Django могут найти полезным сравнение этого с классом BoundField в рамках фреймворка форм Django, который представляет собой пару значения поля формы и соответствующего определения поля формы и, следовательно, знает, как отобразить значение как поле формы HTML.
Большинство времени вам не нужно беспокоиться об этих внутренних деталях; Wagtail будет использовать отображение в шаблоне там, где вы этого ожидаете. Однако есть определённые случаи, когда иллюзия неполна — а именно, когда обращаются к дочерним элементам ListBlock или StructBlock. В этих случаях нет обертки BoundBlock, и поэтому элемент не может полагаться на знание собственного отображения в шаблоне. Например, рассмотрим следующую настройку, где наш HeadingBlock является дочерним элементом StructBlock:
class EventBlock(blocks.StructBlock):
heading = HeadingBlock()
description = blocks.TextBlock()
# ...
class Meta:
template = 'blocks/event.html'
В blocks/event.html:
{% load wagtailcore_tags %}
<div class="event {% if value.heading == 'Party!' %}lots-of-balloons{% endif %}">
{% include_block value.heading %}
- {% include_block value.description %}
</div>
В этом случае value.heading возвращает обычное строковое значение, а не BoundBlock; это необходимо, потому что в противном случае сравнение в {% if value.heading == 'Party!' %} никогда не выполнится. Это, в свою очередь, означает, что {% include_block value.heading %} отображается как обычная строка без тегов <h1>. Для получения HTML-отображения необходимо явно обратиться к экземпляру BoundBlock через value.bound_blocks.heading:
{% load wagtailcore_tags %}
<div class="event {% if value.heading == 'Party!' %}lots-of-balloons{% endif %}">
{% include_block value.bound_blocks.heading %}
- {% include_block value.description %}
</div>
На практике, вероятно, было бы более естественно и удобочитаемо сделать тег <h1> явным в шаблоне EventBlock:
{% load wagtailcore_tags %}
<div class="event {% if value.heading == 'Party!' %}lots-of-balloons{% endif %}">
<h1>{{ value.heading }}</h1>
- {% include_block value.description %}
</div>
Это ограничение не относится к значениям StructBlock и StreamBlock, являющимся дочерними элементами StructBlock, потому что Wagtail реализует их как сложные объекты, знающие собственное отображение в шаблоне, даже если они не обернуты в BoundBlock. Например, если StructBlock вложен в другой StructBlock, как в:
class EventBlock(blocks.StructBlock):
heading = HeadingBlock()
description = blocks.TextBlock()
guest_speaker = blocks.StructBlock([
('first_name', blocks.CharBlock()),
('surname', blocks.CharBlock()),
('photo', ImageChooserBlock()),
], template='blocks/speaker.html')
тогда {% include_block value.guest_speaker %} в шаблоне EventBlock получит отображение в шаблоне из blocks/speaker.html как и ожидалось.
В итоге, взаимодействия между BoundBlocks и простыми значениями работают по следующим правилам:
- При итерации по значению StreamField или StreamBlock (как в
{% for block in page.body %}) вы получите последовательность BoundBlocks. - Если у вас есть экземпляр BoundBlock, вы можете получить простое значение как
block.value. - Обращение к дочернему элементу StructBlock (как в
value.heading) вернёт простое значение; для получения BoundBlock используйтеvalue.bound_blocks.heading. - Аналогично, обращение к дочерним элементам ListBlock (например,
for item in value) вернёт простые значения; для получения BoundBlocks используйтеvalue.bound_blocks. - Значения StructBlock и StreamBlock всегда знают, как отобразить свои собственные шаблоны, даже если у вас есть только простое значение, а не BoundBlock.
Изменено в версии 2.16: Значение ListBlock теперь предоставляет свойство bound_blocks; ранее это был обычный список значений дочерних элементов Python.
© 2014-present Torchbox Ltd and individual contributors.
All rights are reserved.
Licensed under the BSD License.
https://docs.wagtail.org/en/stable/advanced_topics/boundblocks_and_values.html