浏览代码

Remove support for use_json_field=False StreamFields

use_json_field=False (outside of migrations) now throws an ImproperlyConfigured exception. In Wagtail 6.0 this will be changed to default to True, and user code can remove it - we cannot do this in 5.0 because this would mean that site owners who have not already generated the migrations to change legacy StreamFields to use_json_field=True will be unable to do so (since makemigrations will see no changes).
Matt Westcott 2 年之前
父节点
当前提交
5c653002a2

+ 13 - 25
wagtail/fields.py

@@ -1,6 +1,6 @@
 import json
-import warnings
 
+from django.core.exceptions import ImproperlyConfigured
 from django.core.serializers.json import DjangoJSONEncoder
 from django.core.validators import MaxLengthValidator
 from django.db import models
@@ -13,7 +13,6 @@ from wagtail.rich_text import (
     extract_references_from_rich_text,
     get_text_for_indexing,
 )
-from wagtail.utils.deprecation import RemovedInWagtail50Warning
 
 
 class RichTextField(models.TextField):
@@ -117,25 +116,20 @@ class StreamField(models.Field):
         return models.JSONField(encoder=DjangoJSONEncoder)
 
     def _check_json_field(self):
-        if type(self.use_json_field) is not bool:
-            warnings.warn(
-                f"StreamField must explicitly set use_json_field argument to True/False instead of {self.use_json_field}.",
-                RemovedInWagtail50Warning,
-                stacklevel=3,
+        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" if self.use_json_field else "TextField"
+        return "JSONField"
 
     def get_lookup(self, lookup_name):
-        if self.use_json_field:
-            return self.json_field.get_lookup(lookup_name)
-        return super().get_lookup(lookup_name)
+        return self.json_field.get_lookup(lookup_name)
 
     def get_transform(self, lookup_name):
-        if self.use_json_field:
-            return self.json_field.get_transform(lookup_name)
-        return super().get_transform(lookup_name)
+        return self.json_field.get_transform(lookup_name)
 
     def deconstruct(self):
         name, path, _, kwargs = super().deconstruct()
@@ -167,12 +161,7 @@ class StreamField(models.Field):
                 return StreamValue(self.stream_block, [])
 
             return self.stream_block.to_python(unpacked_value)
-        elif (
-            self.use_json_field
-            and value
-            and isinstance(value, list)
-            and isinstance(value[0], dict)
-        ):
+        elif value and isinstance(value, list) and isinstance(value[0], dict):
             # The value is already unpacked since JSONField-based StreamField should
             # accept deserialised values (no need to call json.dumps() first).
             # In addition, the value is not a list of (block_name, value) tuples
@@ -204,9 +193,8 @@ class StreamField(models.Field):
             # for reverse migrations that convert StreamField data back into plain text
             # fields.)
             return value.raw_text
-        elif isinstance(value, StreamValue) or not self.use_json_field:
+        elif isinstance(value, StreamValue):
             # StreamValue instances must be prepared first.
-            # Before use_json_field was implemented, this is also the value used in queries.
             return json.dumps(
                 self.stream_block.get_prep_value(value), cls=DjangoJSONEncoder
             )
@@ -217,7 +205,7 @@ class StreamField(models.Field):
             return self.json_field.get_prep_value(value)
 
     def get_db_prep_value(self, value, connection, prepared=False):
-        if self.use_json_field and not isinstance(value, StreamValue):
+        if not isinstance(value, StreamValue):
             # When querying with JSONField features, the rhs might not be a StreamValue.
             # As of Django 4.2, JSONField value serialisation is handled in
             # get_db_prep_value instead of get_prep_value.
@@ -225,7 +213,7 @@ class StreamField(models.Field):
         return super().get_db_prep_value(value, connection, prepared)
 
     def from_db_value(self, value, expression, connection):
-        if self.use_json_field and isinstance(expression, KeyTransform):
+        if isinstance(expression, KeyTransform):
             # This could happen when using JSONField key transforms,
             # e.g. Page.object.values('body__0').
             try:
@@ -266,7 +254,7 @@ class StreamField(models.Field):
     def contribute_to_class(self, cls, name, **kwargs):
         super().contribute_to_class(cls, name, **kwargs)
 
-        # Output deprecation warning on missing use_json_field argument, unless this is a fake model
+        # 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()

+ 139 - 0
wagtail/test/testapp/migrations/0019_remove_non_json_field_streamfields.py

@@ -0,0 +1,139 @@
+# Generated by Django 4.1.5 on 2023-01-31 15:35
+
+from django.db import migrations
+import wagtail.blocks
+import wagtail.contrib.table_block.blocks
+import wagtail.fields
+import wagtail.images.blocks
+import wagtail.test.testapp.models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("tests", "0018_alter_streampage_body"),
+    ]
+
+    operations = [
+        migrations.DeleteModel(
+            name="BlockCountsStreamModel",
+        ),
+        migrations.DeleteModel(
+            name="MinMaxCountStreamModel",
+        ),
+        migrations.DeleteModel(
+            name="StreamModel",
+        ),
+        migrations.AlterField(
+            model_name="addedstreamfieldwithemptylistdefaultpage",
+            name="body",
+            field=wagtail.fields.StreamField(
+                [("title", wagtail.blocks.CharBlock())], default=[], use_json_field=True
+            ),
+        ),
+        migrations.AlterField(
+            model_name="addedstreamfieldwithemptystringdefaultpage",
+            name="body",
+            field=wagtail.fields.StreamField(
+                [("title", wagtail.blocks.CharBlock())], default="", use_json_field=True
+            ),
+        ),
+        migrations.AlterField(
+            model_name="addedstreamfieldwithoutdefaultpage",
+            name="body",
+            field=wagtail.fields.StreamField(
+                [("title", wagtail.blocks.CharBlock())], use_json_field=True
+            ),
+        ),
+        migrations.AlterField(
+            model_name="customrichblockfieldpage",
+            name="body",
+            field=wagtail.fields.StreamField(
+                [("rich_text", wagtail.blocks.RichTextBlock(editor="custom"))],
+                use_json_field=True,
+            ),
+        ),
+        migrations.AlterField(
+            model_name="deadlystreampage",
+            name="body",
+            field=wagtail.fields.StreamField(
+                [("title", wagtail.test.testapp.models.DeadlyCharBlock())],
+                use_json_field=True,
+            ),
+        ),
+        migrations.AlterField(
+            model_name="defaultrichblockfieldpage",
+            name="body",
+            field=wagtail.fields.StreamField(
+                [("rich_text", wagtail.blocks.RichTextBlock())], use_json_field=True
+            ),
+        ),
+        migrations.AlterField(
+            model_name="defaultstreampage",
+            name="body",
+            field=wagtail.fields.StreamField(
+                [
+                    ("text", wagtail.blocks.CharBlock()),
+                    ("rich_text", wagtail.blocks.RichTextBlock()),
+                    ("image", wagtail.images.blocks.ImageChooserBlock()),
+                ],
+                default="",
+                use_json_field=True,
+            ),
+        ),
+        migrations.AlterField(
+            model_name="inlinestreampagesection",
+            name="body",
+            field=wagtail.fields.StreamField(
+                [
+                    ("text", wagtail.blocks.CharBlock()),
+                    ("rich_text", wagtail.blocks.RichTextBlock()),
+                    ("image", wagtail.images.blocks.ImageChooserBlock()),
+                ],
+                use_json_field=True,
+            ),
+        ),
+        migrations.AlterField(
+            model_name="streampage",
+            name="body",
+            field=wagtail.fields.StreamField(
+                [
+                    ("text", wagtail.blocks.CharBlock()),
+                    ("rich_text", wagtail.blocks.RichTextBlock()),
+                    ("image", wagtail.test.testapp.models.ExtendedImageChooserBlock()),
+                    (
+                        "product",
+                        wagtail.blocks.StructBlock(
+                            [
+                                ("name", wagtail.blocks.CharBlock()),
+                                ("price", wagtail.blocks.CharBlock()),
+                            ]
+                        ),
+                    ),
+                    ("raw_html", wagtail.blocks.RawHTMLBlock()),
+                    (
+                        "books",
+                        wagtail.blocks.StreamBlock(
+                            [
+                                ("title", wagtail.blocks.CharBlock()),
+                                ("author", wagtail.blocks.CharBlock()),
+                            ]
+                        ),
+                    ),
+                    (
+                        "title_list",
+                        wagtail.blocks.ListBlock(wagtail.blocks.CharBlock()),
+                    ),
+                ],
+                use_json_field=True,
+            ),
+        ),
+        migrations.AlterField(
+            model_name="tableblockstreampage",
+            name="table",
+            field=wagtail.fields.StreamField(
+                [("table", wagtail.contrib.table_block.blocks.TableBlock())],
+                use_json_field=True,
+            ),
+        ),
+    ]

+ 10 - 50
wagtail/test/testapp/models.py

@@ -1327,17 +1327,6 @@ class CustomDocumentWithAuthor(AbstractDocument):
     admin_form_fields = Document.admin_form_fields + ("author",)
 
 
-class StreamModel(models.Model):
-    body = StreamField(
-        [
-            ("text", CharBlock()),
-            ("rich_text", RichTextBlock()),
-            ("image", ImageChooserBlock()),
-        ],
-        use_json_field=False,
-    )
-
-
 class JSONStreamModel(models.Model):
     body = StreamField(
         [
@@ -1349,19 +1338,6 @@ class JSONStreamModel(models.Model):
     )
 
 
-class MinMaxCountStreamModel(models.Model):
-    body = StreamField(
-        [
-            ("text", CharBlock()),
-            ("rich_text", RichTextBlock()),
-            ("image", ImageChooserBlock()),
-        ],
-        min_num=2,
-        max_num=5,
-        use_json_field=False,
-    )
-
-
 class JSONMinMaxCountStreamModel(models.Model):
     body = StreamField(
         [
@@ -1375,22 +1351,6 @@ class JSONMinMaxCountStreamModel(models.Model):
     )
 
 
-class BlockCountsStreamModel(models.Model):
-    body = StreamField(
-        [
-            ("text", CharBlock()),
-            ("rich_text", RichTextBlock()),
-            ("image", ImageChooserBlock()),
-        ],
-        block_counts={
-            "text": {"min_num": 1},
-            "rich_text": {"max_num": 1},
-            "image": {"min_num": 1, "max_num": 1},
-        },
-        use_json_field=False,
-    )
-
-
 class JSONBlockCountsStreamModel(models.Model):
     body = StreamField(
         [
@@ -1453,7 +1413,7 @@ class StreamPage(Page):
                 ListBlock(CharBlock()),
             ),
         ],
-        use_json_field=False,
+        use_json_field=True,
     )
 
     api_fields = ("body",)
@@ -1474,7 +1434,7 @@ class DefaultStreamPage(Page):
             ("image", ImageChooserBlock()),
         ],
         default="",
-        use_json_field=False,
+        use_json_field=True,
     )
 
     content_panels = [
@@ -1708,7 +1668,7 @@ class DefaultRichBlockFieldPage(Page):
         [
             ("rich_text", RichTextBlock()),
         ],
-        use_json_field=False,
+        use_json_field=True,
     )
 
     content_panels = Page.content_panels + [FieldPanel("body")]
@@ -1728,7 +1688,7 @@ class CustomRichBlockFieldPage(Page):
         [
             ("rich_text", RichTextBlock(editor="custom")),
         ],
-        use_json_field=False,
+        use_json_field=True,
     )
 
     content_panels = [
@@ -1774,7 +1734,7 @@ class InlineStreamPageSection(Orderable):
             ("rich_text", RichTextBlock()),
             ("image", ImageChooserBlock()),
         ],
-        use_json_field=False,
+        use_json_field=True,
     )
     panels = [FieldPanel("body")]
 
@@ -1787,7 +1747,7 @@ class InlineStreamPage(Page):
 
 
 class TableBlockStreamPage(Page):
-    table = StreamField([("table", TableBlock())], use_json_field=False)
+    table = StreamField([("table", TableBlock())], use_json_field=True)
 
     content_panels = [FieldPanel("table")]
 
@@ -1830,15 +1790,15 @@ class AlwaysShowInMenusPage(Page):
 
 # test for AddField migrations on StreamFields using various default values
 class AddedStreamFieldWithoutDefaultPage(Page):
-    body = StreamField([("title", CharBlock())], use_json_field=False)
+    body = StreamField([("title", CharBlock())], use_json_field=True)
 
 
 class AddedStreamFieldWithEmptyStringDefaultPage(Page):
-    body = StreamField([("title", CharBlock())], default="", use_json_field=False)
+    body = StreamField([("title", CharBlock())], default="", use_json_field=True)
 
 
 class AddedStreamFieldWithEmptyListDefaultPage(Page):
-    body = StreamField([("title", CharBlock())], default=[], use_json_field=False)
+    body = StreamField([("title", CharBlock())], default=[], use_json_field=True)
 
 
 class SecretPage(Page):
@@ -1966,7 +1926,7 @@ class DeadlyStreamPage(Page):
         [
             ("title", DeadlyCharBlock()),
         ],
-        use_json_field=False,
+        use_json_field=True,
     )
     content_panels = Page.content_panels + [
         FieldPanel("body"),

+ 37 - 138
wagtail/tests/test_streamfield.py

@@ -1,6 +1,5 @@
 # -*- coding: utf-8 -*
 import json
-from unittest import skip
 
 from django.apps import apps
 from django.db import connection, models
@@ -16,18 +15,14 @@ from wagtail.images.tests.utils import get_test_image_file
 from wagtail.rich_text import RichText
 from wagtail.signal_handlers import disable_reference_index_auto_update
 from wagtail.test.testapp.models import (
-    BlockCountsStreamModel,
     JSONBlockCountsStreamModel,
     JSONMinMaxCountStreamModel,
     JSONStreamModel,
-    MinMaxCountStreamModel,
-    StreamModel,
 )
-from wagtail.utils.deprecation import RemovedInWagtail50Warning
 
 
 class TestLazyStreamField(TestCase):
-    model = StreamModel
+    model = JSONStreamModel
 
     def setUp(self):
         self.image = Image.objects.create(
@@ -183,13 +178,7 @@ class TestLazyStreamField(TestCase):
                 instance.save()
 
 
-class TestJSONLazyStreamField(TestLazyStreamField):
-    model = JSONStreamModel
-
-
 class TestSystemCheck(TestCase):
-    use_json_field = False
-
     def tearDown(self):
         # unregister InvalidStreamModel from the overall model registry
         # so that it doesn't break tests elsewhere
@@ -207,7 +196,7 @@ class TestSystemCheck(TestCase):
                     ("heading", blocks.CharBlock()),
                     ("rich text", blocks.RichTextBlock()),
                 ],
-                use_json_field=self.use_json_field,
+                use_json_field=True,
             )
 
         errors = InvalidStreamModel.check()
@@ -217,57 +206,18 @@ class TestSystemCheck(TestCase):
         self.assertEqual(errors[0].obj, InvalidStreamModel._meta.get_field("body"))
 
 
-class TestJSONSystemCheck(TestSystemCheck):
-    use_json_field = True
-
-
-class TestUseJsonFieldWarning(TestCase):
-    def tearDown(self):
-        # unregister DeprecatedStreamModel from the overall model registry
-        # so that it doesn't break tests elsewhere
-        for package in ("wagtailcore", "wagtail.tests"):
-            try:
-                del apps.all_models[package]["deprecatedstreammodel"]
-            except KeyError:
-                pass
-        apps.clear_cache()
-
-    def test_system_check_validates_block(self):
-        message = "StreamField must explicitly set use_json_field argument to True/False instead of None."
-        with self.assertWarnsMessage(RemovedInWagtail50Warning, message):
-
-            class DeprecatedStreamModel(models.Model):
-                body = StreamField(
-                    [
-                        ("heading", blocks.CharBlock()),
-                        ("rich text", blocks.RichTextBlock()),
-                    ],
-                )
-
-
 class TestStreamValueAccess(TestCase):
-    model = StreamModel
-
     def setUp(self):
-        self.json_body = self.model.objects.create(
+        self.json_body = JSONStreamModel.objects.create(
             body=json.dumps([{"type": "text", "value": "foo"}])
         )
 
-    def test_can_read_non_json_content(self):
-        """StreamField columns should handle non-JSON database content gracefully"""
-        nonjson_body = self.model.objects.create(body="<h1>hello world</h1>")
-        self.assertIsInstance(nonjson_body.body, StreamValue)
-        # the main list-like content of the StreamValue should be blank
-        self.assertFalse(nonjson_body.body)
-        # the unparsed text content should be available in raw_text
-        self.assertEqual(nonjson_body.body.raw_text, "<h1>hello world</h1>")
-
     def test_can_assign_as_list(self):
         self.json_body.body = [("rich_text", RichText("<h2>hello world</h2>"))]
         self.json_body.save()
 
         # the body should now be a stream consisting of a single rich_text block
-        fetched_body = self.model.objects.get(id=self.json_body.id).body
+        fetched_body = JSONStreamModel.objects.get(id=self.json_body.id).body
         self.assertIsInstance(fetched_body, StreamValue)
         self.assertEqual(len(fetched_body), 1)
         self.assertIsInstance(fetched_body[0].value, RichText)
@@ -277,7 +227,7 @@ class TestStreamValueAccess(TestCase):
         self.json_body.body.append(("text", "bar"))
         self.json_body.save()
 
-        fetched_body = self.model.objects.get(id=self.json_body.id).body
+        fetched_body = JSONStreamModel.objects.get(id=self.json_body.id).body
         self.assertIsInstance(fetched_body, StreamValue)
         self.assertEqual(len(fetched_body), 2)
         self.assertEqual(fetched_body[0].block_type, "text")
@@ -286,16 +236,8 @@ class TestStreamValueAccess(TestCase):
         self.assertEqual(fetched_body[1].value, "bar")
 
 
-class TestJSONStreamValueAccess(TestStreamValueAccess):
-    model = JSONStreamModel
-
-    @skip("JSONField-based StreamField does not support storing non-json content.")
-    def test_can_read_non_json_content(self):
-        pass
-
-
 class TestStreamFieldRenderingBase(TestCase):
-    model = StreamModel
+    model = JSONStreamModel
 
     def setUp(self):
         self.image = Image.objects.create(
@@ -336,10 +278,6 @@ class TestStreamFieldRendering(TestStreamFieldRenderingBase):
         self.assertIsInstance(rendered, SafeString)
 
 
-class TestJSONStreamFieldRendering(TestStreamFieldRendering):
-    model = JSONStreamModel
-
-
 class TestStreamFieldDjangoRendering(TestStreamFieldRenderingBase):
     def render(self, string, context):
         return Template(string).render(Context(context))
@@ -349,10 +287,6 @@ class TestStreamFieldDjangoRendering(TestStreamFieldRenderingBase):
         self.assertHTMLEqual(rendered, self.expected)
 
 
-class TestJSONStreamFieldDjangoRendering(TestStreamFieldDjangoRendering):
-    model = JSONStreamModel
-
-
 class TestStreamFieldJinjaRendering(TestStreamFieldRenderingBase):
     def setUp(self):
         super().setUp()
@@ -366,19 +300,13 @@ class TestStreamFieldJinjaRendering(TestStreamFieldRenderingBase):
         self.assertHTMLEqual(rendered, self.expected)
 
 
-class TestJSONStreamFieldJinjaRendering(TestStreamFieldJinjaRendering):
-    model = JSONStreamModel
-
-
 class TestRequiredStreamField(TestCase):
-    use_json_field = False
-
     def test_non_blank_field_is_required(self):
         # passing a block list
         field = StreamField(
             [("paragraph", blocks.CharBlock())],
             blank=False,
-            use_json_field=self.use_json_field,
+            use_json_field=True,
         )
         self.assertTrue(field.stream_block.required)
         with self.assertRaises(StreamBlockValidationError):
@@ -391,9 +319,7 @@ class TestRequiredStreamField(TestCase):
                 required = False
 
         # passing a block instance
-        field = StreamField(
-            MyStreamBlock(), blank=False, use_json_field=self.use_json_field
-        )
+        field = StreamField(MyStreamBlock(), blank=False, use_json_field=True)
         self.assertTrue(field.stream_block.required)
         with self.assertRaises(StreamBlockValidationError):
             field.stream_block.clean([])
@@ -401,25 +327,21 @@ class TestRequiredStreamField(TestCase):
         field = StreamField(
             MyStreamBlock(required=False),
             blank=False,
-            use_json_field=self.use_json_field,
+            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=self.use_json_field
-        )
+        field = StreamField(MyStreamBlock, blank=False, use_json_field=True)
         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=self.use_json_field
-        )
+        field = StreamField([("paragraph", blocks.CharBlock())], use_json_field=True)
         self.assertTrue(field.stream_block.required)
         with self.assertRaises(StreamBlockValidationError):
             field.stream_block.clean([])
@@ -431,20 +353,18 @@ class TestRequiredStreamField(TestCase):
                 required = False
 
         # passing a block instance
-        field = StreamField(MyStreamBlock(), use_json_field=self.use_json_field)
+        field = StreamField(MyStreamBlock(), use_json_field=True)
         self.assertTrue(field.stream_block.required)
         with self.assertRaises(StreamBlockValidationError):
             field.stream_block.clean([])
 
-        field = StreamField(
-            MyStreamBlock(required=False), use_json_field=self.use_json_field
-        )
+        field = StreamField(MyStreamBlock(required=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, use_json_field=self.use_json_field)
+        field = StreamField(MyStreamBlock, use_json_field=True)
         self.assertTrue(field.stream_block.required)
         with self.assertRaises(StreamBlockValidationError):
             field.stream_block.clean([])
@@ -454,7 +374,7 @@ class TestRequiredStreamField(TestCase):
         field = StreamField(
             [("paragraph", blocks.CharBlock())],
             blank=True,
-            use_json_field=self.use_json_field,
+            use_json_field=True,
         )
         self.assertFalse(field.stream_block.required)
         field.stream_block.clean([])  # no validation error on empty stream
@@ -466,35 +386,23 @@ class TestRequiredStreamField(TestCase):
                 required = True
 
         # passing a block instance
-        field = StreamField(
-            MyStreamBlock(), blank=True, use_json_field=self.use_json_field
-        )
+        field = StreamField(MyStreamBlock(), blank=True, use_json_field=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=self.use_json_field
+            MyStreamBlock(required=True), blank=True, use_json_field=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=self.use_json_field
-        )
+        field = StreamField(MyStreamBlock, blank=True, use_json_field=True)
         self.assertFalse(field.stream_block.required)
         field.stream_block.clean([])  # no validation error on empty stream
 
 
-class TestJSONRequiredStreamField(TestRequiredStreamField):
-    use_json_field = True
-
-
 class TestStreamFieldCountValidation(TestCase):
-    min_max_count_model = MinMaxCountStreamModel
-    block_counts_model = BlockCountsStreamModel
-    use_json_field = False
-
     def setUp(self):
         self.image = Image.objects.create(
             title="Test image", file=get_test_image_file()
@@ -505,14 +413,14 @@ class TestStreamFieldCountValidation(TestCase):
         self.text_body = {"type": "text", "value": "Hello, World!"}
 
     def test_minmax_pass_to_block(self):
-        instance = self.min_max_count_model.objects.create(body=json.dumps([]))
+        instance = JSONMinMaxCountStreamModel.objects.create(body=json.dumps([]))
         internal_block = instance.body.stream_block
 
         self.assertEqual(internal_block.meta.min_num, 2)
         self.assertEqual(internal_block.meta.max_num, 5)
 
     def test_counts_pass_to_block(self):
-        instance = self.block_counts_model.objects.create(body=json.dumps([]))
+        instance = JSONBlockCountsStreamModel.objects.create(body=json.dumps([]))
         block_counts = instance.body.stream_block.meta.block_counts
 
         self.assertEqual(block_counts.get("text"), {"min_num": 1})
@@ -522,7 +430,7 @@ class TestStreamFieldCountValidation(TestCase):
     def test_minimum_count(self):
         # Single block should fail validation
         body = [self.rich_text_body]
-        instance = self.min_max_count_model.objects.create(body=json.dumps(body))
+        instance = JSONMinMaxCountStreamModel.objects.create(body=json.dumps(body))
         with self.assertRaises(StreamBlockValidationError) as catcher:
             instance.body.stream_block.clean(instance.body)
         self.assertEqual(
@@ -531,18 +439,18 @@ class TestStreamFieldCountValidation(TestCase):
 
         # 2 blocks okay
         body = [self.rich_text_body, self.text_body]
-        instance = self.min_max_count_model.objects.create(body=json.dumps(body))
+        instance = JSONMinMaxCountStreamModel.objects.create(body=json.dumps(body))
         self.assertTrue(instance.body.stream_block.clean(instance.body))
 
     def test_maximum_count(self):
         # 5 blocks okay
         body = [self.rich_text_body] * 5
-        instance = self.min_max_count_model.objects.create(body=json.dumps(body))
+        instance = JSONMinMaxCountStreamModel.objects.create(body=json.dumps(body))
         self.assertTrue(instance.body.stream_block.clean(instance.body))
 
         # 6 blocks should fail validation
         body = [self.rich_text_body, self.text_body] * 3
-        instance = self.min_max_count_model.objects.create(body=json.dumps(body))
+        instance = JSONMinMaxCountStreamModel.objects.create(body=json.dumps(body))
         with self.assertRaises(StreamBlockValidationError) as catcher:
             instance.body.stream_block.clean(instance.body)
         self.assertEqual(
@@ -550,10 +458,10 @@ class TestStreamFieldCountValidation(TestCase):
         )
 
     def test_block_counts_minimums(self):
-        instance = self.block_counts_model.objects.create(body=json.dumps([]))
+        instance = JSONBlockCountsStreamModel.objects.create(body=json.dumps([]))
 
         # Zero blocks should fail validation (requires one text, one image)
-        instance = self.block_counts_model.objects.create(body=json.dumps([]))
+        instance = JSONBlockCountsStreamModel.objects.create(body=json.dumps([]))
         with self.assertRaises(StreamBlockValidationError) as catcher:
             instance.body.stream_block.clean(instance.body)
         errors = list(catcher.exception.params["__all__"])
@@ -564,7 +472,7 @@ class TestStreamFieldCountValidation(TestCase):
 
         # One plain text should fail validation
         body = [self.text_body]
-        instance = self.block_counts_model.objects.create(body=json.dumps(body))
+        instance = JSONBlockCountsStreamModel.objects.create(body=json.dumps(body))
         with self.assertRaises(StreamBlockValidationError) as catcher:
             instance.body.stream_block.clean(instance.body)
         self.assertEqual(
@@ -574,15 +482,15 @@ class TestStreamFieldCountValidation(TestCase):
 
         # One text, one image should be okay
         body = [self.text_body, self.image_body]
-        instance = self.block_counts_model.objects.create(body=json.dumps(body))
+        instance = JSONBlockCountsStreamModel.objects.create(body=json.dumps(body))
         self.assertTrue(instance.body.stream_block.clean(instance.body))
 
     def test_block_counts_maximums(self):
-        instance = self.block_counts_model.objects.create(body=json.dumps([]))
+        instance = JSONBlockCountsStreamModel.objects.create(body=json.dumps([]))
 
         # Base is one text, one image
         body = [self.text_body, self.image_body]
-        instance = self.block_counts_model.objects.create(body=json.dumps(body))
+        instance = JSONBlockCountsStreamModel.objects.create(body=json.dumps(body))
         self.assertTrue(instance.body.stream_block.clean(instance.body))
 
         # Two rich text should error
@@ -592,14 +500,14 @@ class TestStreamFieldCountValidation(TestCase):
             self.rich_text_body,
             self.rich_text_body,
         ]
-        instance = self.block_counts_model.objects.create(body=json.dumps(body))
+        instance = JSONBlockCountsStreamModel.objects.create(body=json.dumps(body))
 
         with self.assertRaises(StreamBlockValidationError):
             instance.body.stream_block.clean(instance.body)
 
         # Two images should error
         body = [self.text_body, self.image_body, self.image_body]
-        instance = self.block_counts_model.objects.create(body=json.dumps(body))
+        instance = JSONBlockCountsStreamModel.objects.create(body=json.dumps(body))
 
         with self.assertRaises(StreamBlockValidationError) as catcher:
             instance.body.stream_block.clean(instance.body)
@@ -610,7 +518,7 @@ class TestStreamFieldCountValidation(TestCase):
 
         # One text, one rich, one image should be okay
         body = [self.text_body, self.image_body, self.rich_text_body]
-        instance = self.block_counts_model.objects.create(body=json.dumps(body))
+        instance = JSONBlockCountsStreamModel.objects.create(body=json.dumps(body))
         self.assertTrue(instance.body.stream_block.clean(instance.body))
 
     def test_streamfield_count_argument_precedence(self):
@@ -624,7 +532,7 @@ class TestStreamFieldCountValidation(TestCase):
                 block_counts = {"heading": {"max_num": 1}}
 
         # args being picked up from the class definition
-        field = StreamField(TestStreamBlock, use_json_field=self.use_json_field)
+        field = StreamField(TestStreamBlock, use_json_field=True)
         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)
@@ -635,7 +543,7 @@ class TestStreamFieldCountValidation(TestCase):
             min_num=3,
             max_num=6,
             block_counts={"heading": {"max_num": 2}},
-            use_json_field=self.use_json_field,
+            use_json_field=True,
         )
         self.assertEqual(field.stream_block.meta.min_num, 3)
         self.assertEqual(field.stream_block.meta.max_num, 6)
@@ -647,19 +555,13 @@ class TestStreamFieldCountValidation(TestCase):
             min_num=None,
             max_num=None,
             block_counts=None,
-            use_json_field=self.use_json_field,
+            use_json_field=True,
         )
         self.assertIsNone(field.stream_block.meta.min_num)
         self.assertIsNone(field.stream_block.meta.max_num)
         self.assertIsNone(field.stream_block.meta.block_counts)
 
 
-class TestJSONStreamFieldCountValidation(TestStreamFieldCountValidation):
-    min_max_count_model = JSONMinMaxCountStreamModel
-    block_counts_model = JSONBlockCountsStreamModel
-    use_json_field = True
-
-
 class TestJSONStreamField(TestCase):
     @classmethod
     def setUpClass(cls):
@@ -669,14 +571,11 @@ class TestJSONStreamField(TestCase):
         )
 
     def test_internal_type(self):
-        text = StreamField([("paragraph", blocks.CharBlock())], use_json_field=False)
         json = StreamField([("paragraph", blocks.CharBlock())], use_json_field=True)
-
-        self.assertEqual(text.get_internal_type(), "TextField")
         self.assertEqual(json.get_internal_type(), "JSONField")
 
     def test_json_body_equals_to_text_body(self):
-        instance_text = StreamModel.objects.create(
+        instance_text = JSONStreamModel.objects.create(
             body=json.dumps([{"type": "text", "value": "foo"}]),
         )
         self.assertEqual(