diff --git a/docs/advanced_topics/boundblocks_and_values.md b/docs/advanced_topics/boundblocks_and_values.md index 98400ff9ad..82b0132d71 100644 --- a/docs/advanced_topics/boundblocks_and_values.md +++ b/docs/advanced_topics/boundblocks_and_values.md @@ -24,7 +24,7 @@ class BlogPage(Page): # ... ('heading', HeadingBlock()), # ... - ], use_json_field=True) + ]) ``` ```html+django diff --git a/docs/advanced_topics/streamfield_migrations.md b/docs/advanced_topics/streamfield_migrations.md index f5fe8b2d1e..6aa4067c79 100644 --- a/docs/advanced_topics/streamfield_migrations.md +++ b/docs/advanced_topics/streamfield_migrations.md @@ -59,7 +59,6 @@ class Migration(migrations.Migration): name="body", field=wagtail.fields.StreamField( [("rich_text", wagtail.blocks.RichTextBlock())], - use_json_field=True, ), ), ] @@ -200,7 +199,6 @@ class Migration(migrations.Migration): name="body", field=wagtail.fields.StreamField( [("rich_text", wagtail.blocks.RichTextBlock())], - use_json_field=True, ), ), ] diff --git a/docs/reference/streamfield/blocks.md b/docs/reference/streamfield/blocks.md index 7815b2091d..decba5ce2b 100644 --- a/docs/reference/streamfield/blocks.md +++ b/docs/reference/streamfield/blocks.md @@ -9,12 +9,11 @@ This document details the block types provided by Wagtail for use in [StreamFiel ``` ```{eval-rst} -.. class:: wagtail.fields.StreamField(blocks, use_json_field=None, blank=False, min_num=None, max_num=None, block_counts=None, collapsed=False) +.. class:: wagtail.fields.StreamField(blocks, blank=False, min_num=None, max_num=None, block_counts=None, collapsed=False) A model field for representing long-form content as a sequence of content blocks of various types. See :ref:`streamfield_topic`. :param blocks: A list of block types, passed as either a list of ``(name, block_definition)`` tuples or a ``StreamBlock`` instance. - :param use_json_field: Must be set to ``True``. This causes the field to use :class:`~django.db.models.JSONField` as its internal type, allowing the use of ``JSONField`` lookups and transforms. :param blank: When false (the default), at least one block must be provided for the field to be considered valid. :param min_num: Minimum number of sub-blocks that the stream must have. :param max_num: Maximum number of sub-blocks that the stream may have. @@ -30,9 +29,15 @@ body = StreamField([ ], block_counts={ 'heading': {'min_num': 1}, 'image': {'max_num': 5}, -}, use_json_field=True) +}) ``` +```{versionchanged} 6.0 + +The `use_json_field` argument is no longer required. +``` + + ## Block options All block definitions accept the following optional keyword arguments: @@ -414,7 +419,7 @@ All block definitions accept the following optional keyword arguments: ('photo', ImageChooserBlock(required=False)), ('biography', blocks.RichTextBlock()), ], icon='user')), - ], use_json_field=True) + ]) Alternatively, StructBlock can be subclassed to specify a reusable set of sub-blocks: @@ -443,7 +448,7 @@ All block definitions accept the following optional keyword arguments: ('paragraph', blocks.RichTextBlock()), ('image', ImageChooserBlock()), ('person', PersonBlock()), - ], use_json_field=True) + ]) The following additional options are available as either keyword arguments or Meta class attributes: @@ -466,7 +471,7 @@ All block definitions accept the following optional keyword arguments: body = StreamField([ # ... ('ingredients_list', blocks.ListBlock(blocks.CharBlock(label="Ingredient"))), - ], use_json_field=True) + ]) @@ -480,7 +485,7 @@ All block definitions accept the following optional keyword arguments: ('ingredient', blocks.CharBlock()), ('amount', blocks.CharBlock(required=False)), ]))), - ], use_json_field=True) + ]) The following additional options are available as either keyword arguments or Meta class attributes: @@ -512,7 +517,7 @@ All block definitions accept the following optional keyword arguments: ], icon='cogs' )), - ], use_json_field=True) + ]) As with StructBlock, the list of sub-blocks can also be provided as a subclass of StreamBlock: @@ -537,7 +542,6 @@ All block definitions accept the following optional keyword arguments: class HomePage(Page): carousel = StreamField( CarouselBlock(max_num=10, block_counts={'video': {'max_num': 2}}), - use_json_field=True ) ``StreamBlock`` accepts the following additional options as either keyword arguments or ``Meta`` properties: @@ -559,7 +563,7 @@ All block definitions accept the following optional keyword arguments: ('hashtag', blocks.CharBlock()), ('post_date', blocks.DateBlock()), ], form_classname='event-promotions')), - ], use_json_field=True) + ]) .. code-block:: python :emphasize-lines: 6 diff --git a/docs/topics/streamfield.md b/docs/topics/streamfield.md index 60eea696bd..179a57e535 100644 --- a/docs/topics/streamfield.md +++ b/docs/topics/streamfield.md @@ -28,7 +28,7 @@ class BlogPage(Page): ('heading', blocks.CharBlock(form_classname="title")), ('paragraph', blocks.RichTextBlock()), ('image', ImageChooserBlock()), - ], use_json_field=True) + ]) content_panels = Page.content_panels + [ FieldPanel('author'), @@ -124,7 +124,7 @@ body = StreamField([ ('heading', blocks.CharBlock(form_classname="title")), ('paragraph', blocks.RichTextBlock()), ('image', ImageChooserBlock()), -], use_json_field=True) +]) ``` When reading back the content of a StreamField (such as when rendering a template), the value of a StructBlock is a dict-like object with keys corresponding to the block names given in the definition: @@ -165,7 +165,7 @@ body = StreamField([ ('heading', blocks.CharBlock(form_classname="title")), ('paragraph', blocks.RichTextBlock()), ('image', ImageChooserBlock()), -], use_json_field=True) +]) ``` ### Block icons @@ -185,7 +185,7 @@ body = StreamField([ ('heading', blocks.CharBlock(form_classname="title")), ('paragraph', blocks.RichTextBlock()), ('image', ImageChooserBlock()), -], use_json_field=True) +]) ``` ```{code-block} python @@ -215,7 +215,7 @@ body = StreamField([ ('heading', blocks.CharBlock(form_classname="title")), ('paragraph', blocks.RichTextBlock()), ('image', ImageChooserBlock()), -], use_json_field=True) +]) ``` When reading back the content of a StreamField (such as when rendering a template), the value of a ListBlock is a list of child values: @@ -251,7 +251,7 @@ body = StreamField([ ('heading', blocks.CharBlock(form_classname="title")), ('paragraph', blocks.RichTextBlock()), ('image', ImageChooserBlock()), -], use_json_field=True) +]) ``` `StreamBlock` can also be subclassed in the same way as `StructBlock`, with the child blocks being specified as attributes on the class: @@ -275,7 +275,7 @@ class CommonContentBlock(blocks.StreamBlock): class BlogPage(Page): - body = StreamField(CommonContentBlock(), use_json_field=True) + body = StreamField(CommonContentBlock()) ``` When reading back the content of a StreamField, the value of a StreamBlock is a sequence of block objects with `block_type` and `value` properties, just like the top-level value of the StreamField itself. @@ -309,7 +309,7 @@ body = StreamField([ ('heading', blocks.CharBlock(form_classname="title")), ('paragraph', blocks.RichTextBlock()), ('image', ImageChooserBlock()), -], min_num=2, max_num=5, use_json_field=True) +], min_num=2, max_num=5) ``` Or equivalently: @@ -334,7 +334,7 @@ body = StreamField([ ('image', ImageChooserBlock()), ], block_counts={ 'heading': {'min_num': 1, 'max_num': 3}, -}, use_json_field=True) +}) ``` Or equivalently: diff --git a/wagtail/fields.py b/wagtail/fields.py index 278722b855..439c0d673c 100644 --- a/wagtail/fields.py +++ b/wagtail/fields.py @@ -1,6 +1,5 @@ import json -from django.core.exceptions import ImproperlyConfigured from django.core.serializers.json import DjangoJSONEncoder from django.core.validators import MaxLengthValidator from django.db import models @@ -83,7 +82,10 @@ class Creator: class StreamField(models.Field): - def __init__(self, block_types, use_json_field=None, **kwargs): + def __init__(self, block_types, use_json_field=True, **kwargs): + # use_json_field no longer has any effect but is recognised to support historical + # migrations + # extract kwargs that are to be passed on to the block, not handled by super block_opts = {} for arg in ["min_num", "max_num", "block_counts", "collapsed"]: @@ -97,8 +99,6 @@ class StreamField(models.Field): super().__init__(**kwargs) - self.use_json_field = use_json_field - if isinstance(block_types, Block): # use the passed block as the top-level block self.stream_block = block_types @@ -115,13 +115,6 @@ class StreamField(models.Field): def json_field(self): return models.JSONField(encoder=DjangoJSONEncoder) - def _check_json_field(self): - if self.use_json_field is not True: - # RemovedInWagtail60Warning - make use_json_field optional and default to True - raise ImproperlyConfigured( - "StreamField must explicitly set use_json_field=True" - ) - def get_internal_type(self): return "JSONField" @@ -135,7 +128,6 @@ class StreamField(models.Field): name, path, _, kwargs = super().deconstruct() block_types = list(self.stream_block.child_blocks.items()) args = [block_types] - kwargs["use_json_field"] = self.use_json_field return name, path, args, kwargs def to_python(self, value): @@ -273,11 +265,6 @@ class StreamField(models.Field): def contribute_to_class(self, cls, name, **kwargs): super().contribute_to_class(cls, name, **kwargs) - # Output error on missing use_json_field=True argument, unless this is a fake model - # for a migration - if cls.__module__ != "__fake__": - self._check_json_field() - # Add Creator descriptor to allow the field to be set from a list or a # JSON string. setattr(cls, self.name, Creator(self)) diff --git a/wagtail/test/streamfield_migrations/models.py b/wagtail/test/streamfield_migrations/models.py index 2a79372a05..32fec38cc3 100644 --- a/wagtail/test/streamfield_migrations/models.py +++ b/wagtail/test/streamfield_migrations/models.py @@ -42,8 +42,8 @@ class BaseStreamBlock(StreamBlock): class SampleModel(models.Model): - content = StreamField(BaseStreamBlock(), use_json_field=True) + content = StreamField(BaseStreamBlock()) class SamplePage(Page): - content = StreamField(BaseStreamBlock(), use_json_field=True) + content = StreamField(BaseStreamBlock()) diff --git a/wagtail/test/testapp/models.py b/wagtail/test/testapp/models.py index af44fbb070..c3cef9b4d1 100644 --- a/wagtail/test/testapp/models.py +++ b/wagtail/test/testapp/models.py @@ -1241,7 +1241,6 @@ class VariousOnDeleteModel(models.Model): ("image", ImageChooserBlock()), ("document", DocumentChooserBlock()), ], - use_json_field=True, ) rich_text = RichTextField(blank=True) @@ -1451,7 +1450,6 @@ class JSONStreamModel(models.Model): ("rich_text", RichTextBlock()), ("image", ImageChooserBlock()), ], - use_json_field=True, ) @@ -1464,7 +1462,6 @@ class JSONMinMaxCountStreamModel(models.Model): ], min_num=2, max_num=5, - use_json_field=True, ) @@ -1480,7 +1477,6 @@ class JSONBlockCountsStreamModel(models.Model): "rich_text": {"max_num": 1}, "image": {"min_num": 1, "max_num": 1}, }, - use_json_field=True, ) @@ -1530,7 +1526,6 @@ class StreamPage(Page): ListBlock(CharBlock()), ), ], - use_json_field=True, ) api_fields = ("body",) @@ -1551,7 +1546,6 @@ class DefaultStreamPage(Page): ("image", ImageChooserBlock()), ], default="", - use_json_field=True, ) content_panels = [ @@ -1789,7 +1783,6 @@ class DefaultRichBlockFieldPage(Page): [ ("rich_text", RichTextBlock()), ], - use_json_field=True, ) content_panels = Page.content_panels + [FieldPanel("body")] @@ -1809,7 +1802,6 @@ class CustomRichBlockFieldPage(Page): [ ("rich_text", RichTextBlock(editor="custom")), ], - use_json_field=True, ) content_panels = [ @@ -1855,7 +1847,6 @@ class InlineStreamPageSection(Orderable): ("rich_text", RichTextBlock()), ("image", ImageChooserBlock()), ], - use_json_field=True, ) panels = [FieldPanel("body")] @@ -1868,7 +1859,7 @@ class InlineStreamPage(Page): class TableBlockStreamPage(Page): - table = StreamField([("table", TableBlock())], use_json_field=True) + table = StreamField([("table", TableBlock())]) content_panels = [FieldPanel("table")] @@ -1911,15 +1902,15 @@ class AlwaysShowInMenusPage(Page): # test for AddField migrations on StreamFields using various default values class AddedStreamFieldWithoutDefaultPage(Page): - body = StreamField([("title", CharBlock())], use_json_field=True) + body = StreamField([("title", CharBlock())]) class AddedStreamFieldWithEmptyStringDefaultPage(Page): - body = StreamField([("title", CharBlock())], default="", use_json_field=True) + body = StreamField([("title", CharBlock())], default="") class AddedStreamFieldWithEmptyListDefaultPage(Page): - body = StreamField([("title", CharBlock())], default=[], use_json_field=True) + body = StreamField([("title", CharBlock())], default=[]) class SecretPage(Page): @@ -2047,7 +2038,6 @@ class DeadlyStreamPage(Page): [ ("title", DeadlyCharBlock()), ], - use_json_field=True, ) content_panels = Page.content_panels + [ FieldPanel("body"), diff --git a/wagtail/tests/test_streamfield.py b/wagtail/tests/test_streamfield.py index 2872fe05c0..9f74fa2a97 100644 --- a/wagtail/tests/test_streamfield.py +++ b/wagtail/tests/test_streamfield.py @@ -198,7 +198,6 @@ class TestSystemCheck(TestCase): ("heading", blocks.CharBlock()), ("rich text", blocks.RichTextBlock()), ], - use_json_field=True, ) errors = InvalidStreamModel.check() @@ -308,7 +307,6 @@ class TestRequiredStreamField(TestCase): field = StreamField( [("paragraph", blocks.CharBlock())], blank=False, - use_json_field=True, ) self.assertTrue(field.stream_block.required) with self.assertRaises(StreamBlockValidationError): @@ -321,7 +319,7 @@ class TestRequiredStreamField(TestCase): required = False # passing a block instance - field = StreamField(MyStreamBlock(), blank=False, use_json_field=True) + field = StreamField(MyStreamBlock(), blank=False) self.assertTrue(field.stream_block.required) with self.assertRaises(StreamBlockValidationError): field.stream_block.clean([]) @@ -329,21 +327,20 @@ class TestRequiredStreamField(TestCase): field = StreamField( MyStreamBlock(required=False), blank=False, - use_json_field=True, ) self.assertTrue(field.stream_block.required) with self.assertRaises(StreamBlockValidationError): field.stream_block.clean([]) # passing a block class - field = StreamField(MyStreamBlock, blank=False, use_json_field=True) + field = StreamField(MyStreamBlock, blank=False) self.assertTrue(field.stream_block.required) with self.assertRaises(StreamBlockValidationError): field.stream_block.clean([]) def test_blank_false_is_implied_by_default(self): # passing a block list - field = StreamField([("paragraph", blocks.CharBlock())], use_json_field=True) + field = StreamField([("paragraph", blocks.CharBlock())]) self.assertTrue(field.stream_block.required) with self.assertRaises(StreamBlockValidationError): field.stream_block.clean([]) @@ -355,18 +352,18 @@ class TestRequiredStreamField(TestCase): required = False # passing a block instance - field = StreamField(MyStreamBlock(), use_json_field=True) + field = StreamField(MyStreamBlock()) self.assertTrue(field.stream_block.required) with self.assertRaises(StreamBlockValidationError): field.stream_block.clean([]) - field = StreamField(MyStreamBlock(required=False), use_json_field=True) + field = StreamField(MyStreamBlock(required=False)) self.assertTrue(field.stream_block.required) with self.assertRaises(StreamBlockValidationError): field.stream_block.clean([]) # passing a block class - field = StreamField(MyStreamBlock, use_json_field=True) + field = StreamField(MyStreamBlock) self.assertTrue(field.stream_block.required) with self.assertRaises(StreamBlockValidationError): field.stream_block.clean([]) @@ -376,7 +373,6 @@ class TestRequiredStreamField(TestCase): field = StreamField( [("paragraph", blocks.CharBlock())], blank=True, - use_json_field=True, ) self.assertFalse(field.stream_block.required) field.stream_block.clean([]) # no validation error on empty stream @@ -388,18 +384,16 @@ class TestRequiredStreamField(TestCase): required = True # passing a block instance - field = StreamField(MyStreamBlock(), blank=True, use_json_field=True) + field = StreamField(MyStreamBlock(), blank=True) self.assertFalse(field.stream_block.required) field.stream_block.clean([]) # no validation error on empty stream - field = StreamField( - MyStreamBlock(required=True), blank=True, use_json_field=True - ) + field = StreamField(MyStreamBlock(required=True), blank=True) self.assertFalse(field.stream_block.required) field.stream_block.clean([]) # no validation error on empty stream # passing a block class - field = StreamField(MyStreamBlock, blank=True, use_json_field=True) + field = StreamField(MyStreamBlock, blank=True) self.assertFalse(field.stream_block.required) field.stream_block.clean([]) # no validation error on empty stream @@ -536,7 +530,7 @@ class TestStreamFieldCountValidation(TestCase): block_counts = {"heading": {"max_num": 1}} # args being picked up from the class definition - field = StreamField(TestStreamBlock, use_json_field=True) + field = StreamField(TestStreamBlock) self.assertEqual(field.stream_block.meta.min_num, 2) self.assertEqual(field.stream_block.meta.max_num, 5) self.assertEqual(field.stream_block.meta.block_counts["heading"]["max_num"], 1) @@ -547,7 +541,6 @@ class TestStreamFieldCountValidation(TestCase): min_num=3, max_num=6, block_counts={"heading": {"max_num": 2}}, - use_json_field=True, ) self.assertEqual(field.stream_block.meta.min_num, 3) self.assertEqual(field.stream_block.meta.max_num, 6) @@ -559,7 +552,6 @@ class TestStreamFieldCountValidation(TestCase): min_num=None, max_num=None, block_counts=None, - use_json_field=True, ) self.assertIsNone(field.stream_block.meta.min_num) self.assertIsNone(field.stream_block.meta.max_num) @@ -575,7 +567,7 @@ class TestJSONStreamField(TestCase): ) def test_internal_type(self): - json = StreamField([("paragraph", blocks.CharBlock())], use_json_field=True) + json = StreamField([("paragraph", blocks.CharBlock())]) self.assertEqual(json.get_internal_type(), "JSONField") def test_json_body_equals_to_text_body(self):