Lazy-loading StreamField and StreamBlock for Wagtail that defers block instantiation to prevent circular imports and reduces migration bloat
Project description
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file wagtail_lazy_streamfield-1.0.1.tar.gz.
File metadata
- Download URL: wagtail_lazy_streamfield-1.0.1.tar.gz
- Upload date:
- Size: 46.4 kB
- Tags: Source
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1771c8f7d7905436a9da31090655ee8f9a390d8d1d425ddaed23b17eb3c7f6a2
|
|
| MD5 |
94302415bc5b5e8c9ddfd5aaf83f68c7
|
|
| BLAKE2b-256 |
f65bcecf937fae18e9c685c7bb555bee34885f962c7191a7eebe68dd7ca93157
|
File details
Details for the file wagtail_lazy_streamfield-1.0.1-py3-none-any.whl.
File metadata
- Download URL: wagtail_lazy_streamfield-1.0.1-py3-none-any.whl
- Upload date:
- Size: 9.3 kB
- Tags: Python 3
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5bcb95329044a874ce90c9018f6d6ec90c673238d18ad15fc56211d714fef72c
|
|
| MD5 |
566701e45da238abebbba1056d713368
|
|
| BLAKE2b-256 |
1dccdf87ae63435a251007dfcb9c1bb19267f1742366cf11f00921cd46e00afb
|