wagtail-lazy-streamfield
This module provides a lazy-loading StreamField for Wagtail. Resolves circular import issues between blocks and models, and keeps migrations free of block structure bloat.
The Circular Import Problem
In Wagtail projects, blocks often become interdependent. A CardBlock might reference a page model that has a StreamField using CardBlock. This causes ImportError at startup:
# Fails if CardBlock imports this file
from .blocks import CardBlock
class MyPage(Page):
body = StreamField([
('card', CardBlock()), # Instantiation requires immediate import
])
With LazyStreamField, block paths are strings. Resolution happens at runtime, not import time:
class MyPage(Page):
body = LazyStreamField(StreamBlockDefinition(
('card', 'myapp.blocks.CardBlock'),
))
The Migration Bloat Problem
Wagtail freezes the entire StreamField block structure into migration files. Complex sites can have migrations exceeding 10,000 lines. This makes migrations slow to generate, hard to review, and prone to merge conflicts.
LazyStreamField excludes block definitions from migration serialization:
# migrations/0001_initial.py
operations = [
migrations.AddField(
model_name='blogpage',
name='content',
field=lazy_streamfield.streamfield.LazyStreamField(blank=True, null=True),
),
]
Installation
pip install wagtail-lazy-streamfield
Usage
Define Blocks
Use StreamBlockDefinition with string paths instead of instantiating blocks directly:
# blocks.py
from lazy_streamfield import StreamBlockDefinition
BASE_BLOCKS = StreamBlockDefinition(
("text", "myapp.blocks.TextBlock"),
("image", "myapp.blocks.ImageBlock"),
)
# Combine definitions with |
MEDIA_BLOCKS = StreamBlockDefinition(
("video", "myapp.blocks.VideoBlock"),
)
ALL_BLOCKS = BASE_BLOCKS | MEDIA_BLOCKS
Use in Models
Replace StreamField with LazyStreamField:
# models.py
from wagtail.models import Page
from lazy_streamfield import LazyStreamField
from .blocks import ALL_BLOCKS
class BlogPage(Page):
content = LazyStreamField(ALL_BLOCKS, blank=True)
Nested Blocks
Use LazyStreamBlock inside a StructBlock to prevent recursion or break import cycles:
# blocks.py
from wagtail.blocks import StructBlock
from lazy_streamfield import LazyStreamBlock, StreamBlockDefinition
NESTED_BLOCKS = StreamBlockDefinition(
("card", "myapp.blocks.CardBlock"),
)
class CardBlock(StructBlock):
content = LazyStreamBlock(NESTED_BLOCKS)
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[1.0.1] - 2026-01-19
Fixed
- Fixed type annotations in
StreamBlockDefinitionto useBlockinstead ofBaseBlock(which is a metaclass).
Added
- Added
tytype checker to CI andruntests.sh. - Added pre-commit configuration with ruff, ty, and standard hooks.
- Added
[dependency-groups]for dev tools.
Changed
- Consolidated CI lint checks into single step using project dependencies.
- Cleaned up ruff configuration, removed unnecessary ignore rules.
- PyPI description now includes both README and CHANGELOG via
hatch-fancy-pypi-readme.
[1.0.0] - 2026-01-17
Added
- Initial release of
wagtail-lazy-streamfield. LazyStreamFieldfor deferring block instantiation in Page models.LazyStreamBlockfor lazy loading within nested blocks (e.g.,StructBlock).StreamBlockDefinitionhelper for defining block import paths.- Prevention of circular imports via runtime importing.
- Exclusion of block definitions from Django migrations to reduce bloat.
- Comprehensive test suite and type hints.
Metadata
Release files for wagtail-lazy-streamfield 1.0.1
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_lazy_streamfield-1.0.1.tar.gz | 46.4 kB | Without provenance | Details for wagtail_lazy_streamfield-1.0.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| wagtail_lazy_streamfield-1.0.1-py3-none-any.whl | Python 3 | none | any | Details for wagtail_lazy_streamfield-1.0.1-py3-none-any.whl |
Total release size: 55.7 kB
Release files / wagtail_lazy_streamfield-1.0.1.tar.gz
| Download URL | wagtail_lazy_streamfield-1.0.1.tar.gz |
|---|---|
| Size | 46.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1771c8f7d7905436a9da31090655ee8f9a390d8d1d425ddaed23b17eb3c7f6a2
|
|
BLAKE2b-256 checksum How to use checksums |
f65bcecf937fae18e9c685c7bb555bee34885f962c7191a7eebe68dd7ca93157
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / wagtail_lazy_streamfield-1.0.1-py3-none-any.whl
| Download URL | wagtail_lazy_streamfield-1.0.1-py3-none-any.whl |
|---|---|
| Size | 9.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5bcb95329044a874ce90c9018f6d6ec90c673238d18ad15fc56211d714fef72c
|
|
BLAKE2b-256 checksum How to use checksums |
1dccdf87ae63435a251007dfcb9c1bb19267f1742366cf11f00921cd46e00afb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for wagtail-lazy-streamfield 1.0.1
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A 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.