kopia lustrzana https://github.com/wagtail/wagtail
Add section about BoundBlocks and values
rodzic
2c765a7462
commit
0e34282646
|
@ -471,6 +471,130 @@ To pass additional context variables to the template, block subclasses can overr
|
|||
In this example, the variable ``is_happening_today`` will be made available within the block template.
|
||||
|
||||
|
||||
BoundBlocks and values
|
||||
----------------------
|
||||
|
||||
As you've seen above, it's possible to assign a particular template rendering to a block. This can be done on any block type, not just StructBlocks - however, there are some extra details to be aware of. Consider the following block definition:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class HeadingBlock(blocks.CharBlock):
|
||||
class Meta:
|
||||
template = 'blocks/heading.html'
|
||||
|
||||
where blocks/heading.html consists of:
|
||||
|
||||
.. code-block:: html+django
|
||||
|
||||
<h1>{{ value }}</h1>
|
||||
|
||||
This gives us a block that behaves as an ordinary text field, but wraps its output in ``<h1>`` tags whenever it is rendered:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class BlogPage(Page):
|
||||
body = StreamField([
|
||||
# ...
|
||||
'heading': HeadingBlock(),
|
||||
# ...
|
||||
])
|
||||
|
||||
.. code-block:: html+django
|
||||
|
||||
{% for block in page.body %}
|
||||
{% if block.block_type == 'heading' %}
|
||||
{{ block }} {# this block will output its own <h1>...</h1> tags #}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
This is a powerful feature, but it involves some complexity behind the scenes to make it work. Effectively, HeadingBlock has a double identity - logically it represents a plain Python string value, but in circumstances such as this it needs to yield a 'magic' object that knows its own custom HTML representation. This 'magic' object is an instance of ``BoundBlock`` - an object that represents the pairing of a value and its block definition. (Django developers may recognise this as the same principle behind ``BoundField`` in Django's forms framework.)
|
||||
|
||||
Most of the time, you won't need to worry about whether you're dealing with a plain value or a BoundBlock; you can trust Wagtail to do the right thing. However, there are certain cases where the distinction becomes important. For example, consider the following setup:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class EventBlock(blocks.StructBlock):
|
||||
heading = HeadingBlock()
|
||||
description = blocks.TextBlock()
|
||||
# ...
|
||||
|
||||
class Meta:
|
||||
template = 'blocks/event.html'
|
||||
|
||||
where blocks/event.html is:
|
||||
|
||||
.. code-block:: html+django
|
||||
|
||||
<div class="event {% if value.heading == 'Party!' %}lots-of-balloons{% endif %}">
|
||||
{{ value.heading }}
|
||||
- {{ value.description }}
|
||||
</div>
|
||||
|
||||
In this case, ``value.heading`` returns the plain string value; if this weren't the case, the comparison in ``{% if value.heading == 'Party!' %}`` would never succeed. This in turn means that ``{{ value.heading }}`` renders as the plain string, without the ``<h1>`` tags.
|
||||
|
||||
Interactions between BoundBlocks and plain values work according to the following rules:
|
||||
|
||||
1. When iterating over the value of a StreamField or StreamBlock (as in ``{% for block in page.body %}``), you will get back a sequence of BoundBlocks.
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This means that ``{{ block }}`` will always render using the block's own template, if one is supplied. More specifically, these ``block`` objects will be instances of StreamChild, which additionally provides the ``block_type`` property.
|
||||
|
||||
2. If you have a BoundBlock instance, you can access the plain value as ``block.value``.
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For example, if you had a particular page template where you wanted HeadingBlock to display as ``<h2>`` rather than ``<h1>``, you could write:
|
||||
|
||||
.. code-block:: html+django
|
||||
|
||||
{% for block in page.body %}
|
||||
{% if block.block_type == 'heading' %}
|
||||
<h2>{{ block.value }}</h2>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
3. Accessing a child of a StructBlock (as in ``value.heading``) will return a plain value; to retrieve the BoundBlock instead, use ``value.bound_blocks.heading``.
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This ensures that template tags such as ``{% if value.heading == 'Party!' %}`` and ``{% image value.photo fill-320x200 %}`` work as expected. The event template above could be rewritten as follows to access the HeadingBlock content as a BoundBlock and use its own HTML representation (with ``<h1>`` tags included):
|
||||
|
||||
.. code-block:: html+django
|
||||
|
||||
<div class="event {% if value.heading == 'Party!' %}lots-of-balloons{% endif %}">
|
||||
{{ value.bound_block.heading }}
|
||||
{{ value.description }}
|
||||
</div>
|
||||
|
||||
However, in this case it's probably more readable to make the ``<h1>`` tag explicit in the EventBlock's template:
|
||||
|
||||
.. code-block:: html+django
|
||||
|
||||
<div class="event {% if value.heading == 'Party!' %}lots-of-balloons{% endif %}">
|
||||
<h1>{{ value.heading }}</h1>
|
||||
{{ value.description }}
|
||||
</div>
|
||||
|
||||
4. The value of a ListBlock is a plain Python list; iterating over it returns plain child values.
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
5. StructBlock and StreamBlock values always know how to render their own templates, even if you only have the plain value rather than the BoundBlock.
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is possible because the HTML rendering behaviour of these blocks does not interfere with their main role as a container for data - there's no "double identity" as there is for blocks like CharBlock. For example, if a StructBlock is nested in another StructBlock, as in:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
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')
|
||||
|
||||
then writing ``{{ value.guest_speaker }}`` within the EventBlock's template will use the template rendering from blocks/speaker.html for that field.
|
||||
|
||||
|
||||
Custom block types
|
||||
------------------
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue