Quellcode durchsuchen

Fixes #5968: Model forms should save empty custom field values as null

jeremystretch vor 4 Jahren
Ursprung
Commit
5b89cdc868
3 geänderte Dateien mit 62 neuen und 1 gelöschten Zeilen
  1. 4 0
      docs/release-notes/version-2.11.md
  2. 5 1
      netbox/extras/forms.py
  3. 53 0
      netbox/extras/tests/test_forms.py

+ 4 - 0
docs/release-notes/version-2.11.md

@@ -7,6 +7,10 @@
 * [#6748](https://github.com/netbox-community/netbox/issues/6748) - Add site group filter to devices list
 * [#6872](https://github.com/netbox-community/netbox/issues/6872) - Add table configuration button to child prefixes view
 
+### Bug Fixes
+
+* [#5968](https://github.com/netbox-community/netbox/issues/5968) - Model forms should save empty custom field values as null
+
 ---
 
 ## v2.11.11 (2021-08-12)

+ 5 - 1
netbox/extras/forms.py

@@ -77,7 +77,11 @@ class CustomFieldModelForm(forms.ModelForm):
 
         # Save custom field data on instance
         for cf_name in self.custom_fields:
-            self.instance.custom_field_data[cf_name[3:]] = self.cleaned_data.get(cf_name)
+            key = cf_name[3:]  # Strip "cf_" from field name
+            value = self.cleaned_data.get(cf_name)
+            empty_values = self.fields[cf_name].empty_values
+            # Convert "empty" values to null
+            self.instance.custom_field_data[key] = value if value not in empty_values else None
 
         return super().clean()
 

+ 53 - 0
netbox/extras/tests/test_forms.py

@@ -0,0 +1,53 @@
+from django.contrib.contenttypes.models import ContentType
+from django.test import TestCase
+
+from dcim.forms import SiteForm
+from dcim.models import Site
+from extras.choices import CustomFieldTypeChoices
+from extras.models import CustomField
+
+
+class CustomFieldModelFormTest(TestCase):
+
+    @classmethod
+    def setUpTestData(cls):
+        obj_type = ContentType.objects.get_for_model(Site)
+        CHOICES = ('A', 'B', 'C')
+
+        cf_text = CustomField.objects.create(name='text', type=CustomFieldTypeChoices.TYPE_TEXT)
+        cf_text.content_types.set([obj_type])
+
+        cf_integer = CustomField.objects.create(name='integer', type=CustomFieldTypeChoices.TYPE_INTEGER)
+        cf_integer.content_types.set([obj_type])
+
+        cf_boolean = CustomField.objects.create(name='boolean', type=CustomFieldTypeChoices.TYPE_BOOLEAN)
+        cf_boolean.content_types.set([obj_type])
+
+        cf_date = CustomField.objects.create(name='date', type=CustomFieldTypeChoices.TYPE_DATE)
+        cf_date.content_types.set([obj_type])
+
+        cf_url = CustomField.objects.create(name='url', type=CustomFieldTypeChoices.TYPE_URL)
+        cf_url.content_types.set([obj_type])
+
+        cf_select = CustomField.objects.create(name='select', type=CustomFieldTypeChoices.TYPE_SELECT, choices=CHOICES)
+        cf_select.content_types.set([obj_type])
+
+        cf_multiselect = CustomField.objects.create(name='multiselect', type=CustomFieldTypeChoices.TYPE_MULTISELECT,
+                                                    choices=CHOICES)
+        cf_multiselect.content_types.set([obj_type])
+
+    def test_empty_values(self):
+        """
+        Test that empty custom field values are stored as null
+        """
+        form = SiteForm({
+            'name': 'Site 1',
+            'slug': 'site-1',
+            'status': 'active',
+        })
+        self.assertTrue(form.is_valid())
+        instance = form.save()
+
+        for field_type, _ in CustomFieldTypeChoices.CHOICES:
+            self.assertIn(field_type, instance.custom_field_data)
+            self.assertIsNone(instance.custom_field_data[field_type])