Skip to content

Commit cec77ae

Browse files
committed
[models] Replace thirdparty JSONField with Django built-in JSONField #1061
Replaced jsonfield package's JSONField with Django's built-in JSONField across all models to use the modern, maintained Django implementation. Changes: - Updated imports from 'jsonfield import JSONField' to 'django.db.models import JSONField' - Removed jsonfield-specific parameters (load_kwargs, dump_kwargs) which are not needed - Created migrations to alter field types while preserving data - Removed unnecessary collections imports where no longer needed Django's JSONField preserves the same data format and is backward compatible, so no data migration is required. The built-in JSONField provides better performance and is actively maintained as part of Django core. Fixes #1061
1 parent 109f28e commit cec77ae

File tree

11 files changed

+306
-29
lines changed

11 files changed

+306
-29
lines changed

openwisp_controller/config/base/base.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import collections
21
import hashlib
32
import json
43
import logging
@@ -7,10 +6,10 @@
76
from cache_memoize import cache_memoize
87
from django.core.exceptions import ValidationError
98
from django.db import models
9+
from django.db.models import JSONField
1010
from django.utils.functional import cached_property
1111
from django.utils.module_loading import import_string
1212
from django.utils.translation import gettext_lazy as _
13-
from jsonfield import JSONField
1413
from netjsonconfig.exceptions import ValidationError as SchemaError
1514

1615
from openwisp_utils.base import TimeStampedEditableModel
@@ -126,8 +125,6 @@ class BaseConfig(BaseModel):
126125
_("configuration"),
127126
default=dict,
128127
help_text=_("configuration in NetJSON DeviceConfiguration format"),
129-
load_kwargs={"object_pairs_hook": collections.OrderedDict},
130-
dump_kwargs={"indent": 4},
131128
)
132129

133130
__template__ = False

openwisp_controller/config/base/config.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
from django.core.cache import cache
88
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied, ValidationError
99
from django.db import models, transaction
10+
from django.db.models import JSONField
1011
from django.utils.translation import gettext_lazy as _
11-
from jsonfield import JSONField
1212
from model_utils import Choices
1313
from model_utils.fields import StatusField
1414
from netjsonconfig import OpenWrt
@@ -90,8 +90,6 @@ class AbstractConfig(ChecksumCacheMixin, BaseConfig):
9090
'en/stable/general/basics.html#context" target="_blank">'
9191
"context (configuration variables)</a> in JSON format"
9292
),
93-
load_kwargs={"object_pairs_hook": collections.OrderedDict},
94-
dump_kwargs={"indent": 4},
9593
)
9694
checksum_db = models.CharField(
9795
_("configuration checksum"),

openwisp_controller/config/base/device_group.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import collections
21
from copy import deepcopy
32

43
import jsonschema
54
from django.core.exceptions import ValidationError
65
from django.db import models
6+
from django.db.models import JSONField
77
from django.utils.translation import gettext_lazy as _
8-
from jsonfield import JSONField
98
from jsonschema.exceptions import ValidationError as SchemaError
109
from swapper import get_model_name, load_model
1110

@@ -39,8 +38,6 @@ class AbstractDeviceGroup(OrgMixin, TimeStampedEditableModel):
3938
meta_data = JSONField(
4039
blank=True,
4140
default=dict,
42-
load_kwargs={"object_pairs_hook": collections.OrderedDict},
43-
dump_kwargs={"indent": 4},
4441
help_text=_(
4542
"Group meta data, use this field to store data which is related"
4643
" to this group and can be retrieved via the REST API."
@@ -50,8 +47,6 @@ class AbstractDeviceGroup(OrgMixin, TimeStampedEditableModel):
5047
context = JSONField(
5148
blank=True,
5249
default=dict,
53-
load_kwargs={"object_pairs_hook": collections.OrderedDict},
54-
dump_kwargs={"indent": 4},
5550
help_text=_(
5651
"This field can be used to add meta data for the group"
5752
' or to add "Configuration Variables" to the devices.'

openwisp_controller/config/base/multitenancy.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import collections
21
from copy import deepcopy
32

43
import swapper
54
from django.db import models
5+
from django.db.models import JSONField
66
from django.utils.translation import gettext_lazy as _
7-
from jsonfield import JSONField
87

98
from openwisp_utils.base import KeyField, UUIDModel
109

@@ -34,8 +33,6 @@ class AbstractOrganizationConfigSettings(UUIDModel):
3433
context = JSONField(
3534
blank=True,
3635
default=dict,
37-
load_kwargs={"object_pairs_hook": collections.OrderedDict},
38-
dump_kwargs={"indent": 4},
3936
help_text=_(
4037
'This field can be used to add "Configuration Variables"' " to the devices."
4138
),

openwisp_controller/config/base/template.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55

66
from django.core.exceptions import ObjectDoesNotExist, ValidationError
77
from django.db import models, transaction
8+
from django.db.models import JSONField
89
from django.utils.translation import gettext_lazy as _
9-
from jsonfield import JSONField
1010
from netjsonconfig.exceptions import ValidationError as NetjsonconfigValidationError
1111
from swapper import get_model_name, load_model
1212
from taggit.managers import TaggableManager
@@ -102,8 +102,6 @@ class AbstractTemplate(ShareableOrgMixinUniqueName, BaseConfig):
102102
"template; these default variables will "
103103
"be used during schema validation."
104104
),
105-
load_kwargs={"object_pairs_hook": OrderedDict},
106-
dump_kwargs={"indent": 4},
107105
)
108106
__template__ = True
109107

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Generated by Django 5.2.11 on 2026-02-04 02:53
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("config", "0061_config_checksum_db"),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name="config",
15+
name="config",
16+
field=models.JSONField(
17+
blank=True,
18+
default=dict,
19+
help_text="configuration in NetJSON DeviceConfiguration format",
20+
verbose_name="configuration",
21+
),
22+
),
23+
migrations.AlterField(
24+
model_name="config",
25+
name="context",
26+
field=models.JSONField(
27+
blank=True,
28+
default=dict,
29+
help_text=(
30+
'Additional <a href="http://netjsonconfig.openwisp.org/'
31+
'en/stable/general/basics.html#context" target="_blank">'
32+
"context (configuration variables)</a> in JSON format"
33+
),
34+
),
35+
),
36+
migrations.AlterField(
37+
model_name="devicegroup",
38+
name="context",
39+
field=models.JSONField(
40+
blank=True,
41+
default=dict,
42+
help_text=(
43+
"This field can be used to add meta data for the group"
44+
' or to add "Configuration Variables" to the devices.'
45+
),
46+
verbose_name="Configuration Variables",
47+
),
48+
),
49+
migrations.AlterField(
50+
model_name="devicegroup",
51+
name="meta_data",
52+
field=models.JSONField(
53+
blank=True,
54+
default=dict,
55+
help_text=(
56+
"Group meta data, use this field to store data "
57+
"which is related to this group and can be "
58+
"retrieved via the REST API."
59+
),
60+
verbose_name="Metadata",
61+
),
62+
),
63+
migrations.AlterField(
64+
model_name="organizationconfigsettings",
65+
name="context",
66+
field=models.JSONField(
67+
blank=True,
68+
default=dict,
69+
help_text=(
70+
'This field can be used to add "Configuration Variables"'
71+
" to the devices."
72+
),
73+
verbose_name="Configuration Variables",
74+
),
75+
),
76+
migrations.AlterField(
77+
model_name="template",
78+
name="config",
79+
field=models.JSONField(
80+
blank=True,
81+
default=dict,
82+
help_text="configuration in NetJSON DeviceConfiguration format",
83+
verbose_name="configuration",
84+
),
85+
),
86+
migrations.AlterField(
87+
model_name="template",
88+
name="default_values",
89+
field=models.JSONField(
90+
blank=True,
91+
default=dict,
92+
help_text=(
93+
"A dictionary containing the default "
94+
"values for the variables used by this "
95+
"template; these default variables will "
96+
"be used during schema validation."
97+
),
98+
verbose_name="Default Values",
99+
),
100+
),
101+
migrations.AlterField(
102+
model_name="vpn",
103+
name="config",
104+
field=models.JSONField(
105+
default=dict,
106+
help_text="configuration in NetJSON DeviceConfiguration format",
107+
verbose_name="configuration",
108+
),
109+
),
110+
]

openwisp_controller/connection/base/models.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
import collections
21
import logging
32

43
import django
54
import jsonschema
65
from django.core.exceptions import ValidationError
76
from django.db import models, transaction
7+
from django.db.models import JSONField
88
from django.utils import timezone
99
from django.utils.functional import cached_property
1010
from django.utils.module_loading import import_string
1111
from django.utils.translation import gettext
1212
from django.utils.translation import gettext_lazy as _
13-
from jsonfield import JSONField
1413
from jsonschema.exceptions import ValidationError as SchemaError
1514
from swapper import get_model_name, load_model
1615

@@ -101,8 +100,6 @@ class AbstractCredentials(ConnectorMixin, ShareableOrgMixinUniqueName, BaseModel
101100
_("parameters"),
102101
default=dict,
103102
help_text=_("global connection parameters"),
104-
load_kwargs={"object_pairs_hook": collections.OrderedDict},
105-
dump_kwargs={"indent": 4},
106103
)
107104
auto_add = models.BooleanField(
108105
_("auto add"),
@@ -245,8 +242,6 @@ class AbstractDeviceConnection(ConnectorMixin, TimeStampedEditableModel):
245242
"local connection parameters (will override "
246243
"the global parameters if specified)"
247244
),
248-
load_kwargs={"object_pairs_hook": collections.OrderedDict},
249-
dump_kwargs={"indent": 4},
250245
)
251246
# usability improvements
252247
is_working = models.BooleanField(null=True, blank=True, default=None)
@@ -425,8 +420,6 @@ class AbstractCommand(TimeStampedEditableModel):
425420
input = JSONField(
426421
blank=True,
427422
null=True,
428-
load_kwargs={"object_pairs_hook": collections.OrderedDict},
429-
dump_kwargs={"indent": 4},
430423
)
431424
output = models.TextField(blank=True)
432425

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Generated by Django 5.2.11 on 2026-02-04 02:53
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("connection", "0009_alter_deviceconnection_unique_together"),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name="command",
15+
name="input",
16+
field=models.JSONField(blank=True, null=True),
17+
),
18+
migrations.AlterField(
19+
model_name="credentials",
20+
name="params",
21+
field=models.JSONField(
22+
default=dict,
23+
help_text="global connection parameters",
24+
verbose_name="parameters",
25+
),
26+
),
27+
migrations.AlterField(
28+
model_name="deviceconnection",
29+
name="params",
30+
field=models.JSONField(
31+
blank=True,
32+
default=dict,
33+
help_text=(
34+
"local connection parameters (will override "
35+
"the global parameters if specified)"
36+
),
37+
verbose_name="parameters",
38+
),
39+
),
40+
]

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,3 @@ django-cache-memoize~=0.2.1
1616
shortuuid~=1.0.13
1717
netaddr~=1.3.0
1818
django-import-export~=4.3.14
19-
jsonfield>=3.1.0,<4.0.0

0 commit comments

Comments
 (0)