mirror of
https://github.com/netbox-community/netbox.git
synced 2025-09-06 06:13:36 -06:00
Fixes #20221: JSON CustomField does not coerce {}
to null
Some checks are pending
CI / build (20.x, 3.10) (push) Waiting to run
CI / build (20.x, 3.11) (push) Waiting to run
CI / build (20.x, 3.12) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, actions) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, python) (push) Waiting to run
Some checks are pending
CI / build (20.x, 3.10) (push) Waiting to run
CI / build (20.x, 3.11) (push) Waiting to run
CI / build (20.x, 3.12) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, actions) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, python) (push) Waiting to run
This fix actually fixes this for all valid JSON values that evaluate to `False` in Python when loaded and cast to bool: `bool(json.loads(<val>))`. - `{}` - `[]` - `0` - `False` This does not change the behavior of `()` or `""` which are both explicitly cited as "empty" values on `JSONField`.
This commit is contained in:
parent
8311f457b5
commit
fcb380b5c5
@ -538,7 +538,7 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
|
||||
|
||||
# JSON
|
||||
elif self.type == CustomFieldTypeChoices.TYPE_JSON:
|
||||
field = JSONField(required=required, initial=json.dumps(initial) if initial else None)
|
||||
field = JSONField(required=required, initial=json.dumps(initial) if initial is not None else None)
|
||||
|
||||
# Object
|
||||
elif self.type == CustomFieldTypeChoices.TYPE_OBJECT:
|
||||
|
@ -1,7 +1,9 @@
|
||||
import datetime
|
||||
import json
|
||||
from decimal import Decimal
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.test import tag
|
||||
from django.urls import reverse
|
||||
from rest_framework import status
|
||||
|
||||
@ -269,6 +271,60 @@ class CustomFieldTest(TestCase):
|
||||
instance.refresh_from_db()
|
||||
self.assertIsNone(instance.custom_field_data.get(cf.name))
|
||||
|
||||
@tag('regression')
|
||||
def test_json_field_falsy_defaults(self):
|
||||
"""Test that falsy JSON default values are properly handled"""
|
||||
falsy_test_cases = [
|
||||
({}, 'empty_dict'),
|
||||
([], 'empty_array'),
|
||||
(0, 'zero'),
|
||||
(False, 'false_bool'),
|
||||
("", 'empty_string'),
|
||||
]
|
||||
|
||||
for default, suffix in falsy_test_cases:
|
||||
with self.subTest(default=default, suffix=suffix):
|
||||
cf = CustomField.objects.create(
|
||||
name=f'json_falsy_{suffix}',
|
||||
type=CustomFieldTypeChoices.TYPE_JSON,
|
||||
default=default,
|
||||
required=False
|
||||
)
|
||||
cf.object_types.set([self.object_type])
|
||||
|
||||
instance = Site.objects.create(name=f'Test Site {suffix}', slug=f'test-site-{suffix}')
|
||||
|
||||
self.assertIsNotNone(instance.custom_field_data)
|
||||
self.assertIn(cf.name, instance.custom_field_data)
|
||||
|
||||
instance.refresh_from_db()
|
||||
stored = instance.custom_field_data[cf.name]
|
||||
self.assertEqual(stored, default)
|
||||
|
||||
@tag('regression')
|
||||
def test_json_field_falsy_to_form_field(self):
|
||||
"""Test form field generation preserves falsy defaults"""
|
||||
falsy_test_cases = (
|
||||
({}, json.dumps({}), 'empty_dict'),
|
||||
([], json.dumps([]), 'empty_array'),
|
||||
(0, json.dumps(0), 'zero'),
|
||||
(False, json.dumps(False), 'false_bool'),
|
||||
("", '""', 'empty_string'),
|
||||
)
|
||||
|
||||
for default, expected, suffix in falsy_test_cases:
|
||||
with self.subTest(default=default, expected=expected, suffix=suffix):
|
||||
cf = CustomField.objects.create(
|
||||
name=f'json_falsy_{suffix}',
|
||||
type=CustomFieldTypeChoices.TYPE_JSON,
|
||||
default=default,
|
||||
required=False
|
||||
)
|
||||
cf.object_types.set([self.object_type])
|
||||
|
||||
form_field = cf.to_form_field(set_initial=True)
|
||||
self.assertEqual(form_field.initial, expected)
|
||||
|
||||
def test_select_field(self):
|
||||
CHOICES = (
|
||||
('a', 'Option A'),
|
||||
|
@ -14,7 +14,7 @@
|
||||
{{ value|isodatetime }}
|
||||
{% elif customfield.type == 'url' and value %}
|
||||
<a href="{{ value }}">{{ value|truncatechars:70 }}</a>
|
||||
{% elif customfield.type == 'json' and value %}
|
||||
{% elif customfield.type == 'json' and value is not None %}
|
||||
<pre>{{ value|json }}</pre>
|
||||
{% elif customfield.type == 'multiselect' and value %}
|
||||
{{ value|join:", " }}
|
||||
|
Loading…
Reference in New Issue
Block a user