mirror of
https://github.com/wagtail/wagtail.git
synced 2024-11-24 10:58:52 +01:00
Remove use_json_field parameter from StreamField
This commit is contained in:
parent
df44cf7998
commit
c49ec1bb06
@ -24,7 +24,7 @@ class BlogPage(Page):
|
||||
# ...
|
||||
('heading', HeadingBlock()),
|
||||
# ...
|
||||
], use_json_field=True)
|
||||
])
|
||||
```
|
||||
|
||||
```html+django
|
||||
|
@ -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,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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))
|
||||
|
@ -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())
|
||||
|
@ -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"),
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user