← Back to team overview

sts-sponsors team mailing list archive

[Merge] ~ack/maas:builtin-json-field into maas:master

 

Alberto Donato has proposed merging ~ack/maas:builtin-json-field into maas:master.

Commit message:
readd JSONOBjectField for legacy migrations

use builtin JSONFIeld, drop custom JSONOBjectField



Requested reviews:
  MAAS Maintainers (maas-maintainers)

For more details, see:
https://code.launchpad.net/~ack/maas/+git/maas/+merge/435983
-- 
Your team MAAS Committers is subscribed to branch maas:master.
diff --git a/src/maasserver/fields.py b/src/maasserver/fields.py
index 3c02e5a..1034765 100644
--- a/src/maasserver/fields.py
+++ b/src/maasserver/fields.py
@@ -21,8 +21,6 @@ __all__ = [
     "validate_mac",
 ]
 
-from copy import deepcopy
-from json import dumps, loads
 import re
 
 from django import forms
@@ -287,53 +285,6 @@ def register_mac_type(cursor):
     )
 
 
-class JSONObjectField(Field):
-    """A field that will store any jsonizable python object."""
-
-    def to_python(self, value):
-        """db -> python: json load."""
-        assert not isinstance(value, bytes)
-        if value is not None:
-            if isinstance(value, str):
-                try:
-                    return loads(value)
-                except ValueError:
-                    pass
-            return value
-        else:
-            return None
-
-    def from_db_value(self, value, expression, connection):
-        return self.to_python(value)
-
-    def get_db_prep_value(self, value, connection=None, prepared=False):
-        """python -> db: json dump.
-
-        Keys are sorted when dumped to guarantee stable output. DB field can
-        guarantee uniqueness and be queried (the same dict makes the same
-        JSON).
-        """
-        if value is not None:
-            return dumps(deepcopy(value), sort_keys=True)
-        else:
-            return None
-
-    def get_internal_type(self):
-        return "TextField"
-
-    def formfield(self, form_class=None, **kwargs):
-        """Return a plain `forms.Field` here to avoid "helpful" conversions.
-
-        Django's base model field defaults to returning a `CharField`, which
-        means that anything that's not character data gets smooshed to text by
-        `CharField.to_python` in forms (via the woefully named `smart_str`).
-        This is not helpful.
-        """
-        if form_class is None:
-            form_class = forms.Field
-        return super().formfield(form_class=form_class, **kwargs)
-
-
 class XMLField(Field):
     """A field for storing xml natively.
 
diff --git a/src/maasserver/migrations/legacy.py b/src/maasserver/migrations/legacy.py
new file mode 100644
index 0000000..2452f0e
--- /dev/null
+++ b/src/maasserver/migrations/legacy.py
@@ -0,0 +1,54 @@
+# This module holds legacy code that's been removed from models, but still
+# needs to exist not to break old migrations.
+
+from copy import deepcopy
+import json
+
+from django.db.models import Field
+
+
+class JSONObjectField(Field):
+    """A field that will store any jsonizable python object."""
+
+    def to_python(self, value):
+        """db -> python: json load."""
+        assert not isinstance(value, bytes)
+        if value is not None:
+            if isinstance(value, str):
+                try:
+                    return json.loads(value)
+                except ValueError:
+                    pass
+            return value
+        else:
+            return None
+
+    def from_db_value(self, value, expression, connection):
+        return self.to_python(value)
+
+    def get_db_prep_value(self, value, connection=None, prepared=False):
+        """python -> db: json dump.
+
+        Keys are sorted when dumped to guarantee stable output. DB field can
+        guarantee uniqueness and be queried (the same dict makes the same
+        JSON).
+        """
+        if value is not None:
+            return json.dumps(deepcopy(value), sort_keys=True)
+        else:
+            return None
+
+    def get_internal_type(self):
+        return "TextField"
+
+    def formfield(self, form_class=None, **kwargs):
+        """Return a plain `forms.Field` here to avoid "helpful" conversions.
+
+        Django's base model field defaults to returning a `CharField`, which
+        means that anything that's not character data gets smooshed to text by
+        `CharField.to_python` in forms (via the woefully named `smart_str`).
+        This is not helpful.
+        """
+        if form_class is None:
+            form_class = forms.Field
+        return super().formfield(form_class=form_class, **kwargs)
diff --git a/src/maasserver/migrations/maasserver/0001_initial.py b/src/maasserver/migrations/maasserver/0001_initial.py
index a342394..256ecc1 100644
--- a/src/maasserver/migrations/maasserver/0001_initial.py
+++ b/src/maasserver/migrations/maasserver/0001_initial.py
@@ -4,6 +4,7 @@ from django.db import migrations, models
 import django.db.models.deletion
 
 import maasserver.fields
+import maasserver.migrations.legacy
 import maasserver.models.bootresource
 import maasserver.models.cleansave
 import maasserver.models.fabric
@@ -124,7 +125,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "extra",
-                    maasserver.fields.JSONObjectField(
+                    maasserver.migrations.legacy.JSONObjectField(
                         default="", editable=False, blank=True
                     ),
                 ),
@@ -167,7 +168,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "extra",
-                    maasserver.fields.JSONObjectField(
+                    maasserver.migrations.legacy.JSONObjectField(
                         default="", editable=False, blank=True
                     ),
                 ),
@@ -414,7 +415,10 @@ class Migration(migrations.Migration):
                     ),
                 ),
                 ("name", models.CharField(unique=True, max_length=255)),
-                ("value", maasserver.fields.JSONObjectField(null=True)),
+                (
+                    "value",
+                    maasserver.migrations.legacy.JSONObjectField(null=True),
+                ),
             ],
         ),
         migrations.CreateModel(
@@ -803,15 +807,21 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "ipv4_params",
-                    maasserver.fields.JSONObjectField(default="", blank=True),
+                    maasserver.migrations.legacy.JSONObjectField(
+                        default="", blank=True
+                    ),
                 ),
                 (
                     "ipv6_params",
-                    maasserver.fields.JSONObjectField(default="", blank=True),
+                    maasserver.migrations.legacy.JSONObjectField(
+                        default="", blank=True
+                    ),
                 ),
                 (
                     "params",
-                    maasserver.fields.JSONObjectField(default="", blank=True),
+                    maasserver.migrations.legacy.JSONObjectField(
+                        default="", blank=True
+                    ),
                 ),
                 (
                     "tags",
@@ -1050,7 +1060,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "power_parameters",
-                    maasserver.fields.JSONObjectField(
+                    maasserver.migrations.legacy.JSONObjectField(
                         default="", max_length=32768, blank=True
                     ),
                 ),
diff --git a/src/maasserver/migrations/maasserver/0015_add_bmc_model.py b/src/maasserver/migrations/maasserver/0015_add_bmc_model.py
index 003ee7b..2a9be98 100644
--- a/src/maasserver/migrations/maasserver/0015_add_bmc_model.py
+++ b/src/maasserver/migrations/maasserver/0015_add_bmc_model.py
@@ -1,7 +1,7 @@
 from django.db import migrations, models
 import django.db.models.deletion
 
-import maasserver.fields
+import maasserver.migrations.legacy
 import maasserver.models.cleansave
 
 
@@ -30,7 +30,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "power_parameters",
-                    maasserver.fields.JSONObjectField(
+                    maasserver.migrations.legacy.JSONObjectField(
                         blank=True, default="", max_length=32768
                     ),
                 ),
diff --git a/src/maasserver/migrations/maasserver/0076_interface_discovery_rescue_mode.py b/src/maasserver/migrations/maasserver/0076_interface_discovery_rescue_mode.py
index e3314ad..dec4ccd 100644
--- a/src/maasserver/migrations/maasserver/0076_interface_discovery_rescue_mode.py
+++ b/src/maasserver/migrations/maasserver/0076_interface_discovery_rescue_mode.py
@@ -2,6 +2,7 @@
 from django.db import migrations, models
 
 import maasserver.fields
+import maasserver.migrations.legacy
 import maasserver.models.cleansave
 
 
@@ -99,7 +100,7 @@ class Migration(migrations.Migration):
         migrations.AddField(
             model_name="interface",
             name="active_discovery_params",
-            field=maasserver.fields.JSONObjectField(
+            field=maasserver.migrations.legacy.JSONObjectField(
                 editable=False, default="", blank=True
             ),
         ),
diff --git a/src/maasserver/migrations/maasserver/0093_add_rdns_model.py b/src/maasserver/migrations/maasserver/0093_add_rdns_model.py
index fedf057..bb4a3aa 100644
--- a/src/maasserver/migrations/maasserver/0093_add_rdns_model.py
+++ b/src/maasserver/migrations/maasserver/0093_add_rdns_model.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 from django.db import migrations, models
 
-import maasserver.fields
+import maasserver.migrations.legacy
 import maasserver.models.cleansave
 
 
@@ -31,7 +31,7 @@ class Migration(migrations.Migration):
                     ),
                 ),
                 ("hostname", models.CharField(max_length=256, null=True)),
-                ("hostnames", maasserver.fields.JSONObjectField()),
+                ("hostnames", maasserver.migrations.legacy.JSONObjectField()),
                 (
                     "observer",
                     models.ForeignKey(
diff --git a/src/maasserver/migrations/maasserver/0103_notifications.py b/src/maasserver/migrations/maasserver/0103_notifications.py
index 51613f3..d95931e 100644
--- a/src/maasserver/migrations/maasserver/0103_notifications.py
+++ b/src/maasserver/migrations/maasserver/0103_notifications.py
@@ -2,7 +2,7 @@
 from django.conf import settings
 from django.db import migrations, models
 
-import maasserver.fields
+import maasserver.migrations.legacy
 import maasserver.models.cleansave
 
 
@@ -37,7 +37,7 @@ class Migration(migrations.Migration):
                 ("message", models.TextField(blank=True)),
                 (
                     "context",
-                    maasserver.fields.JSONObjectField(
+                    maasserver.migrations.legacy.JSONObjectField(
                         default=dict, blank=True
                     ),
                 ),
diff --git a/src/maasserver/migrations/maasserver/0120_bootsourcecache_extra.py b/src/maasserver/migrations/maasserver/0120_bootsourcecache_extra.py
index 0e9b6c4..824f46e 100644
--- a/src/maasserver/migrations/maasserver/0120_bootsourcecache_extra.py
+++ b/src/maasserver/migrations/maasserver/0120_bootsourcecache_extra.py
@@ -1,6 +1,6 @@
 from django.db import migrations, models
 
-import maasserver.fields
+import maasserver.migrations.legacy
 
 
 class Migration(migrations.Migration):
@@ -11,7 +11,7 @@ class Migration(migrations.Migration):
         migrations.AddField(
             model_name="bootsourcecache",
             name="extra",
-            field=maasserver.fields.JSONObjectField(
+            field=maasserver.migrations.legacy.JSONObjectField(
                 blank=True, default="", editable=False
             ),
         )
diff --git a/src/maasserver/migrations/maasserver/0125_add_switch_model.py b/src/maasserver/migrations/maasserver/0125_add_switch_model.py
index 1438fde..cc9f605 100644
--- a/src/maasserver/migrations/maasserver/0125_add_switch_model.py
+++ b/src/maasserver/migrations/maasserver/0125_add_switch_model.py
@@ -1,6 +1,6 @@
 from django.db import migrations, models
 
-import maasserver.fields
+import maasserver.migrations.legacy
 import maasserver.models.cleansave
 
 
@@ -31,7 +31,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "nos_parameters",
-                    maasserver.fields.JSONObjectField(
+                    maasserver.migrations.legacy.JSONObjectField(
                         max_length=32768, blank=True, default=""
                     ),
                 ),
diff --git a/src/maasserver/migrations/maasserver/0126_add_controllerinfo_model.py b/src/maasserver/migrations/maasserver/0126_add_controllerinfo_model.py
index c558478..76daa9f 100644
--- a/src/maasserver/migrations/maasserver/0126_add_controllerinfo_model.py
+++ b/src/maasserver/migrations/maasserver/0126_add_controllerinfo_model.py
@@ -1,6 +1,6 @@
 from django.db import migrations, models
 
-import maasserver.fields
+import maasserver.migrations.legacy
 import maasserver.models.cleansave
 
 
@@ -29,13 +29,13 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "interfaces",
-                    maasserver.fields.JSONObjectField(
+                    maasserver.migrations.legacy.JSONObjectField(
                         default="", blank=True, max_length=32768
                     ),
                 ),
                 (
                     "interface_update_hints",
-                    maasserver.fields.JSONObjectField(
+                    maasserver.migrations.legacy.JSONObjectField(
                         default="", blank=True, max_length=32768
                     ),
                 ),
diff --git a/src/maasserver/migrations/maasserver/0292_use_builtin_json_field.py b/src/maasserver/migrations/maasserver/0292_use_builtin_json_field.py
new file mode 100644
index 0000000..afe7d16
--- /dev/null
+++ b/src/maasserver/migrations/maasserver/0292_use_builtin_json_field.py
@@ -0,0 +1,53 @@
+# Generated by Django 3.2.12 on 2023-01-18 16:17
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("maasserver", "0291_rdns_hostnames_as_array"),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="bootresource",
+            name="extra",
+            field=models.JSONField(blank=True, default=dict),
+        ),
+        migrations.AlterField(
+            model_name="bootresourcefile",
+            name="extra",
+            field=models.JSONField(blank=True, default=dict),
+        ),
+        migrations.AlterField(
+            model_name="bootsourcecache",
+            name="extra",
+            field=models.JSONField(blank=True, default=dict),
+        ),
+        migrations.AlterField(
+            model_name="config",
+            name="value",
+            field=models.JSONField(null=True),
+        ),
+        migrations.AlterField(
+            model_name="interface",
+            name="ipv4_params",
+            field=models.JSONField(blank=True, default=dict),
+        ),
+        migrations.AlterField(
+            model_name="interface",
+            name="ipv6_params",
+            field=models.JSONField(blank=True, default=dict),
+        ),
+        migrations.AlterField(
+            model_name="interface",
+            name="params",
+            field=models.JSONField(blank=True, default=dict),
+        ),
+        migrations.AlterField(
+            model_name="notification",
+            name="context",
+            field=models.JSONField(blank=True, default=dict),
+        ),
+    ]
diff --git a/src/maasserver/models/bootresource.py b/src/maasserver/models/bootresource.py
index 7fbb860..ccc0b2f 100644
--- a/src/maasserver/models/bootresource.py
+++ b/src/maasserver/models/bootresource.py
@@ -12,6 +12,7 @@ from django.db.models import (
     CharField,
     Count,
     IntegerField,
+    JSONField,
     Manager,
     Prefetch,
     Sum,
@@ -23,7 +24,6 @@ from maasserver.enum import (
     BOOT_RESOURCE_TYPE_CHOICES,
     BOOT_RESOURCE_TYPE_CHOICES_DICT,
 )
-from maasserver.fields import JSONObjectField
 from maasserver.models.bootresourceset import BootResourceSet
 from maasserver.models.bootsourcecache import BootSourceCache
 from maasserver.models.cleansave import CleanSave
@@ -500,7 +500,7 @@ class BootResource(CleanSave, TimestampedModel):
     # BootResource kernel and instructs Curtin to install the meta-package.
     rolling = BooleanField(blank=False, null=False, default=False)
 
-    extra = JSONObjectField(blank=True, default="", editable=False)
+    extra = JSONField(blank=True, default=dict)
 
     def __str__(self):
         return "<BootResource name={}, arch={}, kflavor={}, base={}>".format(
diff --git a/src/maasserver/models/bootresourcefile.py b/src/maasserver/models/bootresourcefile.py
index 30a899e..ea567ce 100644
--- a/src/maasserver/models/bootresourcefile.py
+++ b/src/maasserver/models/bootresourcefile.py
@@ -4,13 +4,12 @@
 """Boot Resource File."""
 
 
-from django.db.models import CASCADE, CharField, ForeignKey
+from django.db.models import CASCADE, CharField, ForeignKey, JSONField
 
 from maasserver.enum import (
     BOOT_RESOURCE_FILE_TYPE,
     BOOT_RESOURCE_FILE_TYPE_CHOICES,
 )
-from maasserver.fields import JSONObjectField
 from maasserver.models.bootresourceset import BootResourceSet
 from maasserver.models.cleansave import CleanSave
 from maasserver.models.largefile import LargeFile
@@ -56,7 +55,7 @@ class BootResourceFile(CleanSave, TimestampedModel):
         editable=False,
     )
 
-    extra = JSONObjectField(blank=True, default="", editable=False)
+    extra = JSONField(blank=True, default=dict)
 
     def __str__(self):
         return f"<BootResourceFile {self.filename}/{self.filetype}>"
diff --git a/src/maasserver/models/bootsourcecache.py b/src/maasserver/models/bootsourcecache.py
index 95e37f2..d7227e8 100644
--- a/src/maasserver/models/bootsourcecache.py
+++ b/src/maasserver/models/bootsourcecache.py
@@ -4,9 +4,15 @@
 """Model for a cache of images available in a boot source."""
 
 
-from django.db.models import CASCADE, CharField, DateField, ForeignKey, Manager
+from django.db.models import (
+    CASCADE,
+    CharField,
+    DateField,
+    ForeignKey,
+    JSONField,
+    Manager,
+)
 
-from maasserver.fields import JSONObjectField
 from maasserver.models.bootsource import BootSource
 from maasserver.models.cleansave import CleanSave
 from maasserver.models.timestampedmodel import TimestampedModel
@@ -65,7 +71,7 @@ class BootSourceCache(CleanSave, TimestampedModel):
 
     support_eol = DateField(null=True, blank=True)
 
-    extra = JSONObjectField(blank=True, default="", editable=False)
+    extra = JSONField(blank=True, default=dict)
 
     def __str__(self):
         return (
diff --git a/src/maasserver/models/config.py b/src/maasserver/models/config.py
index 6f840b1..d777e79 100644
--- a/src/maasserver/models/config.py
+++ b/src/maasserver/models/config.py
@@ -11,10 +11,9 @@ from datetime import timedelta
 from socket import gethostname
 import uuid
 
-from django.db.models import CharField, Manager, Model
+from django.db.models import CharField, JSONField, Manager, Model
 from django.db.models.signals import post_save
 
-from maasserver.fields import JSONObjectField
 from maasserver.listener import notify_action
 from provisioningserver.drivers.osystem.ubuntu import UbuntuOS
 from provisioningserver.events import EVENT_TYPES
@@ -325,7 +324,7 @@ class Config(Model):
     """
 
     name = CharField(max_length=255, unique=True)
-    value = JSONObjectField(null=True)
+    value = JSONField(null=True)
 
     objects = ConfigManager()
 
diff --git a/src/maasserver/models/interface.py b/src/maasserver/models/interface.py
index 225bf7c..6c8368b 100644
--- a/src/maasserver/models/interface.py
+++ b/src/maasserver/models/interface.py
@@ -23,6 +23,7 @@ from django.db.models import (
     CharField,
     Count,
     ForeignKey,
+    JSONField,
     Manager,
     ManyToManyField,
     PositiveIntegerField,
@@ -48,7 +49,6 @@ from maasserver.exceptions import (
     StaticIPAddressUnavailable,
 )
 from maasserver.fields import (
-    JSONObjectField,
     MACAddressField,
     validate_mac,
     VerboseRegexValidator,
@@ -580,11 +580,11 @@ class Interface(CleanSave, TimestampedModel):
 
     mac_address = MACAddressField(unique=False, null=True, blank=True)
 
-    ipv4_params = JSONObjectField(blank=True, default="")
+    ipv4_params = JSONField(blank=True, default=dict)
 
-    ipv6_params = JSONObjectField(blank=True, default="")
+    ipv6_params = JSONField(blank=True, default=dict)
 
-    params = JSONObjectField(blank=True, default="")
+    params = JSONField(blank=True, default=dict)
 
     tags = ArrayField(TextField(), blank=True, null=True, default=list)
 
diff --git a/src/maasserver/models/notification.py b/src/maasserver/models/notification.py
index 2e73dae..889994f 100644
--- a/src/maasserver/models/notification.py
+++ b/src/maasserver/models/notification.py
@@ -14,12 +14,12 @@ from django.db.models import (
     CASCADE,
     CharField,
     ForeignKey,
+    JSONField,
     Manager,
     TextField,
 )
 from markupsafe import Markup
 
-from maasserver.fields import JSONObjectField
 from maasserver.models.cleansave import CleanSave
 from maasserver.models.timestampedmodel import TimestampedModel
 
@@ -184,7 +184,7 @@ class Notification(CleanSave, TimestampedModel):
     admins = BooleanField(null=False, blank=True, default=False)
 
     message = TextField(null=False, blank=False)
-    context = JSONObjectField(null=False, blank=True, default=dict)
+    context = JSONField(blank=True, default=dict)
     category = CharField(
         null=False,
         blank=True,
diff --git a/src/maasserver/tests/models.py b/src/maasserver/tests/models.py
index c66dc74..a6993b1 100644
--- a/src/maasserver/tests/models.py
+++ b/src/maasserver/tests/models.py
@@ -10,7 +10,6 @@ __all__ = [
     "FieldChangeTestModel",
     "GenericTestModel",
     "IPv4CIDRTestModel",
-    "JSONFieldModel",
     "LargeObjectFieldModel",
     "MessagesTestModel",
     "TimestampedModelTestModel",
@@ -28,7 +27,6 @@ from django.db.models import (
 from maasserver.fields import (
     CIDRField,
     IPv4CIDRField,
-    JSONObjectField,
     LargeObjectField,
     XMLField,
 )
@@ -43,11 +41,6 @@ class GenericTestModel(Model):
     field = CharField(max_length=20, blank=True)
 
 
-class JSONFieldModel(Model):
-    name = CharField(max_length=255, unique=False)
-    value = JSONObjectField(null=True)
-
-
 class XMLFieldModel(Model):
     class Meta:
         db_table = "docs"
diff --git a/src/maasserver/tests/test_fields.py b/src/maasserver/tests/test_fields.py
index 8a4d610..0769d8d 100644
--- a/src/maasserver/tests/test_fields.py
+++ b/src/maasserver/tests/test_fields.py
@@ -5,7 +5,6 @@ import json
 from random import choice, randint
 import re
 
-from django import forms
 from django.core import serializers
 from django.core.exceptions import ValidationError
 from django.db import connection, DatabaseError
@@ -13,14 +12,12 @@ from django.db.models import BinaryField
 from psycopg2 import OperationalError
 from psycopg2.extensions import ISQLQuote
 from testtools import ExpectedException
-from testtools.matchers import AfterPreprocessing, Equals, Is
 
 from maasserver.enum import INTERFACE_TYPE
 from maasserver.fields import (
     EditableBinaryField,
     HostListFormField,
     IPListFormField,
-    JSONObjectField,
     LargeObjectField,
     LargeObjectFile,
     MAC,
@@ -45,7 +42,6 @@ from maasserver.testing.testcase import (
 from maasserver.tests.models import (
     CIDRTestModel,
     IPv4CIDRTestModel,
-    JSONFieldModel,
     LargeObjectFieldModel,
     XMLFieldModel,
 )
@@ -283,48 +279,6 @@ class TestMACAddressField(MAASServerTestCase):
         self.assertRaises(ValidationError, validate_mac, "00:11:222:33:44:55")
 
 
-class TestJSONObjectField(MAASLegacyServerTestCase):
-
-    apps = ["maasserver.tests"]
-
-    def test_stores_types(self):
-        values = [
-            None,
-            True,
-            False,
-            3.33,
-            "A simple string",
-            [1, 2.43, "3"],
-            {"not": 5, "another": "test"},
-        ]
-        for value in values:
-            name = factory.make_string()
-            test_instance = JSONFieldModel(name=name, value=value)
-            test_instance.save()
-
-            test_instance = JSONFieldModel.objects.get(name=name)
-            self.assertEqual(value, test_instance.value)
-
-    def test_field_exact_lookup(self):
-        # Value can be query via an 'exact' lookup.
-        obj = [4, 6, {}]
-        JSONFieldModel.objects.create(value=obj)
-        test_instance = JSONFieldModel.objects.get(value=obj)
-        self.assertEqual(obj, test_instance.value)
-
-    def test_field_none_lookup(self):
-        # Value can be queried via a 'isnull' lookup.
-        JSONFieldModel.objects.create(value=None)
-        test_instance = JSONFieldModel.objects.get(value__isnull=True)
-        self.assertIsNone(test_instance.value)
-
-    def test_form_field_is_a_plain_field(self):
-        self.assertThat(
-            JSONObjectField().formfield(),
-            AfterPreprocessing(type, Is(forms.Field)),
-        )
-
-
 class TestXMLField(MAASLegacyServerTestCase):
 
     apps = ["maasserver.tests"]
@@ -665,12 +619,10 @@ class TestHostListFormField(MAASTestCase):
         error = self.assertRaises(
             ValidationError, HostListFormField().clean, input
         )
-        self.assertThat(
+        self.assertEqual(
             error.message,
-            Equals(
-                "Invalid hostname: Label cannot start or end with "
-                "hyphen: 'abc-'."
-            ),
+            "Invalid hostname: Label cannot start or end with "
+            "hyphen: 'abc-'.",
         )
 
 
@@ -817,12 +769,10 @@ class TestSubnetListFormField(MAASTestCase):
         error = self.assertRaises(
             ValidationError, SubnetListFormField().clean, input
         )
-        self.assertThat(
+        self.assertEqual(
             error.message,
-            Equals(
-                "Invalid hostname: Label cannot start or end with "
-                "hyphen: 'abc-'."
-            ),
+            "Invalid hostname: Label cannot start or end with "
+            "hyphen: 'abc-'.",
         )
 
 
diff --git a/src/maasserver/websockets/handlers/node_result.py b/src/maasserver/websockets/handlers/node_result.py
index b441898..86b2dcc 100644
--- a/src/maasserver/websockets/handlers/node_result.py
+++ b/src/maasserver/websockets/handlers/node_result.py
@@ -64,8 +64,7 @@ class NodeResultHandler(TimestampedModelHandler):
             self.cache["system_ids"] = {}
 
     def dehydrate_parameters(self, parameters):
-        # Parameters is a JSONObjectField to convert it to a dictionary it must
-        # be accessed. Don't show password parameter values over the websocket.
+        # Don't show password parameter values over the websocket.
         for parameter in parameters.values():
             if parameter.get("type") == "password" and "value" in parameter:
                 parameter["value"] = "REDACTED"
diff --git a/src/metadataserver/migrations/0002_script_models.py b/src/metadataserver/migrations/0002_script_models.py
index 4e56f5c..8cdf9de 100644
--- a/src/metadataserver/migrations/0002_script_models.py
+++ b/src/metadataserver/migrations/0002_script_models.py
@@ -4,7 +4,7 @@ import django.contrib.postgres.fields
 from django.db import migrations, models
 import django.db.models.deletion
 
-import maasserver.fields
+import maasserver.migrations.legacy
 import maasserver.models.cleansave
 import metadataserver.fields
 
@@ -121,7 +121,9 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "result",
-                    maasserver.fields.JSONObjectField(default="", blank=True),
+                    maasserver.migrations.legacy.JSONObjectField(
+                        default="", blank=True
+                    ),
                 ),
                 (
                     "script",
diff --git a/src/metadataserver/migrations/0011_script_metadata.py b/src/metadataserver/migrations/0011_script_metadata.py
index 6cc9995..9b51b38 100644
--- a/src/metadataserver/migrations/0011_script_metadata.py
+++ b/src/metadataserver/migrations/0011_script_metadata.py
@@ -1,6 +1,6 @@
 from django.db import migrations, models
 
-import maasserver.fields
+import maasserver.migrations.legacy
 
 
 class Migration(migrations.Migration):
@@ -26,7 +26,9 @@ class Migration(migrations.Migration):
         migrations.AddField(
             model_name="script",
             name="packages",
-            field=maasserver.fields.JSONObjectField(blank=True, default={}),
+            field=maasserver.migrations.legacy.JSONObjectField(
+                blank=True, default={}
+            ),
         ),
         migrations.AddField(
             model_name="script",
@@ -43,16 +45,22 @@ class Migration(migrations.Migration):
         migrations.AddField(
             model_name="script",
             name="parameters",
-            field=maasserver.fields.JSONObjectField(blank=True, default={}),
+            field=maasserver.migrations.legacy.JSONObjectField(
+                blank=True, default={}
+            ),
         ),
         migrations.AddField(
             model_name="script",
             name="results",
-            field=maasserver.fields.JSONObjectField(blank=True, default={}),
+            field=maasserver.migrations.legacy.JSONObjectField(
+                blank=True, default={}
+            ),
         ),
         migrations.AddField(
             model_name="scriptresult",
             name="parameters",
-            field=maasserver.fields.JSONObjectField(blank=True, default={}),
+            field=maasserver.migrations.legacy.JSONObjectField(
+                blank=True, default={}
+            ),
         ),
     ]
diff --git a/src/metadataserver/migrations/0034_use_builtin_json_field.py b/src/metadataserver/migrations/0034_use_builtin_json_field.py
new file mode 100644
index 0000000..a8c51d6
--- /dev/null
+++ b/src/metadataserver/migrations/0034_use_builtin_json_field.py
@@ -0,0 +1,33 @@
+# Generated by Django 3.2.12 on 2023-01-18 16:17
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("metadataserver", "0033_remove_nodekey_key"),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="script",
+            name="packages",
+            field=models.JSONField(blank=True, default=dict),
+        ),
+        migrations.AlterField(
+            model_name="script",
+            name="parameters",
+            field=models.JSONField(blank=True, default=dict),
+        ),
+        migrations.AlterField(
+            model_name="script",
+            name="results",
+            field=models.JSONField(blank=True, default=dict),
+        ),
+        migrations.AlterField(
+            model_name="scriptresult",
+            name="parameters",
+            field=models.JSONField(blank=True, default=dict),
+        ),
+    ]
diff --git a/src/metadataserver/models/script.py b/src/metadataserver/models/script.py
index 0402082..97efa51 100644
--- a/src/metadataserver/models/script.py
+++ b/src/metadataserver/models/script.py
@@ -19,12 +19,12 @@ from django.db.models import (
     CharField,
     DurationField,
     IntegerField,
+    JSONField,
     Manager,
     OneToOneField,
     TextField,
 )
 
-from maasserver.fields import JSONObjectField
 from maasserver.models.cleansave import CleanSave
 from maasserver.models.timestampedmodel import TimestampedModel
 from maasserver.models.versionedtextfile import VersionedTextFile
@@ -158,13 +158,13 @@ class Script(CleanSave, TimestampedModel):
     )
 
     # Any results which will be made availble after the script is run.
-    results = JSONObjectField(blank=True, default={})
+    results = JSONField(blank=True, default=dict)
 
     # Parameters which may be passed to the script and their constraints.
-    parameters = JSONObjectField(blank=True, default={})
+    parameters = JSONField(blank=True, default=dict)
 
     # apt, snap, dpkg, to install or archives to extract.
-    packages = JSONObjectField(blank=True, default={})
+    packages = JSONField(blank=True, default=dict)
 
     # 0 is no timeout
     timeout = DurationField(default=datetime.timedelta())
diff --git a/src/metadataserver/models/scriptresult.py b/src/metadataserver/models/scriptresult.py
index 9c23c63..832c355 100644
--- a/src/metadataserver/models/scriptresult.py
+++ b/src/metadataserver/models/scriptresult.py
@@ -12,12 +12,12 @@ from django.db.models import (
     DateTimeField,
     ForeignKey,
     IntegerField,
+    JSONField,
     Q,
     SET_NULL,
 )
 import yaml
 
-from maasserver.fields import JSONObjectField
 from maasserver.models.cleansave import CleanSave
 from maasserver.models.event import Event
 from maasserver.models.interface import Interface
@@ -51,7 +51,7 @@ class ScriptResult(CleanSave, TimestampedModel):
 
     # Any parameters set by MAAS or the user which should be passed to the
     # running script.
-    parameters = JSONObjectField(blank=True, default={})
+    parameters = JSONField(blank=True, default=dict)
 
     # If the result is in reference to a particular block device link it.
     physical_blockdevice = ForeignKey(

Follow ups