wagtail-block-reference
BlockReference support for Wagtail - enables forward and cyclic block references.
Heads up: this package works by patching Wagtail internals (Python and JS) to support forward and cyclic block references, which Wagtail doesn't do out of the box. It exists because the upstream PR (wagtail#14279) may take a while to land (if it ever does), and waiting wasn't an option for me :). Relying on internals means relying on internals - so the test suite deliberately targets the patched code paths, and a future Wagtail upgrade that breaks something will fail loudly rather than silently misbehave.
Installation
pip install wagtail-block-reference
Add to INSTALLED_APPS:
INSTALLED_APPS = [
...
"wagtail_block_reference",
]
Usage
from wagtail import blocks
from wagtail_block_reference import BlockReference
class CommentBlock(blocks.StructBlock):
text = blocks.CharBlock()
replies = blocks.ListBlock(BlockReference(lambda: CommentBlock))
Targets must resolve to the same instance
A cycle ends when a walk meets a target it has already seen, and "already seen" means the same object. So a reference must resolve to the same block instance every time.
Declaring the target as a class, a dotted path, or a class attribute gives you that for free. A factory does not:
# Fine: one CommentBlock instance, memoised on the reference.
replies = blocks.ListBlock(BlockReference(lambda: CommentBlock))
# Broken: a new block on every resolution, so the graph never closes.
replies = blocks.ListBlock(BlockReference(lambda: registry.build("comment")))
The second form is an infinitely deep graph rather than a cyclic one, and no visited-set can bound it. It shows up most easily with a dynamic block registry - memoise the registry so a given name always yields the same instance.
This is caught rather than left to exhaust the stack: past
WAGTAIL_BLOCK_REFERENCE_MAX_WALK_DEPTH levels (default 100), check() reports
wagtailcore.E010 and other walks raise BlockReferenceDepthExceeded. Raise the setting
if a graph really is nested that deeply through references.
How it works
BlockReference is a lazy proxy that resolves its target block on first access. The target
can be a lambda (for forward/cyclic references), a dotted import path, or a block class.
The package ships two patches:
Python patch (patches.py, applied at import time): hooks into Wagtail's block
metaclass so that BlockReference attributes declared on a StructBlock are collected as
child blocks, and registers a telepath adapter so the block serialises as its resolved target.
Patches are applied with wrapt rather than
plain attribute assignment - it wraps the live descriptor (including the metaclass's
implicit-staticmethod __new__) and hands each wrapper the current original via wrapped,
which is more reliable than reassigning SomeClass.method = ... outright.
JS patch (patch.js, injected via insert_global_admin_js): waits for Wagtail to set
window.telepath, then replaces it outright with a vendored Telepath implementation
(wagtail/telepath-unpack#5) that has
cyclic/back-reference (_ref) support built into unpack itself, carrying over any
constructors already registered on the instance it replaces. It also wraps StructBlock and
StreamBlock prototypes with a lazy childBlockDefsByName getter - without that, the cyclic
telepath graph causes a Maximum call stack size exceeded crash when the editor tries to
build the block name map at construction time.
Supported versions
All Wagtail versions that are currently under active or security support: 7.0 LTS, 7.3, and 7.4 LTS. CI tests against each of these. Versions drop off the matrix as they go end-of-life.
CI also runs a non-blocking preview job against the latest Wagtail 8.0 pre-release (currently 8.0rc1), so breakage surfaces before the actual release rather than after.
Development
# Install dependencies
uv sync
# Lint, format & type-check
uv run ruff check .
uv run ruff format .
uv run ty check
# Run all tests (unit + E2E) against a single Wagtail version
uv run playwright install chromium
uv run pytest tests/
# Run all tests against all supported Wagtail versions (installs Chromium automatically)
uv run tox
# Run all tests against all supported Wagtail versions (installs Chromium automatically)
# and see visually what the browser is doing
uv run tox -- --headed --slowmo=500
# Run against a specific version
uv run tox -e wagtail74
# Run against a specific version and see visually what the browser is doing
uv run tox -e wagtail74 -- --headed --slowmo=500
License
MIT
Metadata
Release files for wagtail-block-reference 0.3.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| wagtail_block_reference-0.3.2.tar.gz | 71.0 kB | With provenance | Details for wagtail_block_reference-0.3.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| wagtail_block_reference-0.3.2-py3-none-any.whl | Python 3 | none | any | Details for wagtail_block_reference-0.3.2-py3-none-any.whl |
Total release size: 85.3 kB
Release files / wagtail_block_reference-0.3.2.tar.gz
| Download URL | wagtail_block_reference-0.3.2.tar.gz |
|---|---|
| Size | 71.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
45c0c1351e8da8e648d911278c029b00cce6c6dc130942d01cda98c0f7086ab2
|
|
BLAKE2b-256 checksum How to use checksums |
2243435c5ed5ff2ad6808e4eec32fc3c592cd52adab513ecddb8a0f151a80417
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 24, 2026.
Transparency logRelease files / wagtail_block_reference-0.3.2-py3-none-any.whl
| Download URL | wagtail_block_reference-0.3.2-py3-none-any.whl |
|---|---|
| Size | 14.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
904124a0adcab81eea660c564391fba25c06d6b1640ca1b17f4661aef7b97252
|
|
BLAKE2b-256 checksum How to use checksums |
dccf2dd4574d8a186f81dcadbef4c446e2097e78afdd6f8b4418d51069d8dea4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 24, 2026.
Transparency logSecurity for wagtail-block-reference 0.3.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for wagtail_block_reference-0.3.2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 24, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.