Skip to content

Instantly share code, notes, and snippets.

@zerolab
Created June 14, 2024 21:28
Show Gist options
  • Save zerolab/cbd19becd21a5ab12a711674d5979157 to your computer and use it in GitHub Desktop.
Save zerolab/cbd19becd21a5ab12a711674d5979157 to your computer and use it in GitHub Desktop.
Wagtail: Ignore StreamFields in migrations

Reference: wagtail/wagtail#4298

Add fields.py to your app. Replace usages of from wagtail.fields import StreamField with from the_app.fields import StreamField

import json
from wagtail.fields import StreamField as WagtailStreamfield
class StreamField(WagtailStreamfield):
def __init__(self, *args, **kwargs):
"""
Overrides StreamField.__init__() to account for `block_types` no longer
being received as an arg when migrating (because there is no longer a
`block_types` value in the migration to provide).
Usage:
import this StreamField instead of `from wagtail.fields import StreamField` for usage in your models
"""
if args:
block_types = args[0] or []
args = args[1:]
else:
block_types = kwargs.pop("block_types", [])
super().__init__(block_types, *args, **kwargs)
def deconstruct(self):
"""
Overrides StreamField.deconstruct() to remove `block_types` and
`verbose_name` values so that migrations remain smaller in size,
and changes to those attributes do not require a new migration.
"""
name, path, args, kwargs = super().deconstruct()
if args:
args = args[1:]
else:
kwargs.pop("block_types", None)
kwargs.pop("verbose_name", None)
return name, path, args, kwargs
def to_python(self, value):
"""
Overrides StreamField.to_python() to make the return value
(a `StreamValue`) more useful when migrating. When migrating, block
definitions are unavailable to the field's underlying StreamBlock,
causing self.stream_block.to_python() to not recognise any of the
blocks in the stored value.
"""
stream_value = super().to_python(value)
# There is no way to be absolutely sure this is a migration,
# but the combination of factors below is a pretty decent indicator
if not self.stream_block.child_blocks and value and not stream_value._raw_data:
stream_data = None
if isinstance(value, list):
stream_data = value
elif isinstance(value, str):
try:
stream_data = json.loads(value)
except ValueError:
stream_value.raw_text = value
if stream_data:
return type(stream_value)(self, stream_data, is_lazy=True)
return stream_value
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment