7

Building a custom template for the wagtail StreamField block I found myself in the situation that I need somehow pass the ID of the current block to the other views.

For instance when the URL is clicked in the particular block, the landing page view must know exactly in which of the blocks the URL has been clicked. Then the view can extract other information which is associated with the particular block but not necessarily visually present to the user.

My current strategy is using the snippets, so I can pass the ID of the snippet and the view may obtain related but beforehand hidden data.

This works not so bad, but people have to edit the content in two places and I have to look at their sad faces.

It seems that the value variable in the block template context is an instance of the wagtail.core.blocks.struct_block.StructValue, which gives me access to all the fields of the block but it doesn't seem to reveal its footprint in the DB.

Further value has an interesting attribute: value.block, which seems like it's an instance of the actual model used to construct the block, but again I can't find anything useful like id or pk which would allow to identify that instance in the database.

Is there a way?

NarūnasK
  • 4,564
  • 8
  • 50
  • 76

2 Answers2

7

The block IDs you see in the database representation of a StreamField are a detail implemented by the enclosing StreamBlock, so that we can keep track of each block's history as it gets added / moved / deleted from the stream. The items within the stream do not know their own ID - this is because they could be any possible data type (for example, a CharBlock produces a string value, and you can't attach an ID to a string). As a result, the block template doesn't have access to the ID either.

To access the ID, you'll need to make use of the BoundBlock (or, more precisely, StreamChild) object that's returned whenever you iterate over the StreamField value (or access it by index, e.g. page.body[0] or page.body.0 within template code); this object is a wrapper around the block value which knows the block's type and ID. (More background on BoundBlock in the docs here: http://docs.wagtail.io/en/v2.0/topics/streamfield.html#boundblocks-and-values)

{% for block in page.body %}
    {% include_block block with id=block.id %}
{% endfor %}

Here block is an instance of StreamChild, which has 'value', 'block_type' and 'id' properties. Normally, the {% include_block %} tag will just pass on the value variable to the block template, but here we're passing id as an additional variable that will now be available within that block template.

StreamField blocks are not 'real' database objects, so to retrieve the value again based on the ID you'll need to scan through the StreamField, using code such as:

value = None
for block in page.body:
    if block.id == requested_id:
        value = block.value
        break
gasman
  • 23,691
  • 1
  • 38
  • 56
  • Thanks, I was I able to retrieve something that look like block ID (it looks like `uuid4`). Can you elaborate more, how can I use it to extract field values? Furthermore, is iteration through the `StreamField` necessary? It seems absolutely counter intuitive thing to do when you just need to insert one item, but I assume `include_block` does that internaly when it detects the `StreamField`? – NarūnasK Mar 21 '18 at 15:46
  • You don't have to iterate through the `StreamField` - if you know the specific block you're interested in, you can access it by index, e.g. `page.body[0]` (or `page.body.0` within template code). Have updated my answer to cover retrieving the value at the other end. – gasman Mar 21 '18 at 20:33
  • Thanks, it's useful to know. But still I don't see how can I use that ID to extract field values from the `blocks.StructBlock`? The `view` is outside the `wagtail` if you like and must query database on its own by using the *block_id* which has been passed to it. – NarūnasK Mar 23 '18 at 11:40
  • What about Jinja2? The trick with `include_block with…` doesn’t work, even if we use `include_block with context` wrapped inside a `with block_id=block.id` :( – Bertrand Bordage Jun 25 '18 at 12:29
0

In the HTML file that displays your block, try adding

{% with block.id|stringformat:"s" as block_id %}
 {{ block_id }}
{% endwith %}
  • Welcome to SO! Before answering an old question (here over 2 years old) that already have an accepted answer (that's the case here) please ask yourself: Do I really have something substantially new to add? If not, please refrain from answering. – Timus Oct 10 '20 at 11:41