1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Further work on custom fields

This commit is contained in:
Jeremy Stretch
2020-08-24 14:11:13 -04:00
parent 2276603ac3
commit c85a45e520
4 changed files with 67 additions and 92 deletions

View File

@ -148,10 +148,10 @@ class CustomFieldModelSerializer(ValidatedModelSerializer):
fields = CustomField.objects.filter(obj_type=content_type) fields = CustomField.objects.filter(obj_type=content_type)
# Populate CustomFieldValues for each instance from database # Populate CustomFieldValues for each instance from database
try: if type(self.instance) in (list, tuple):
for obj in self.instance: for obj in self.instance:
self._populate_custom_fields(obj, fields) self._populate_custom_fields(obj, fields)
except TypeError: else:
self._populate_custom_fields(self.instance, fields) self._populate_custom_fields(self.instance, fields)
def _populate_custom_fields(self, instance, custom_fields): def _populate_custom_fields(self, instance, custom_fields):

View File

@ -57,26 +57,13 @@ class CustomFieldModelForm(forms.ModelForm):
# Annotate the field in the list of CustomField form fields # Annotate the field in the list of CustomField form fields
self.custom_fields.append(field_name) self.custom_fields.append(field_name)
def _save_custom_fields(self):
for field_name in self.custom_fields:
self.instance.custom_field_data[field_name[3:]] = self.cleaned_data[field_name]
def save(self, commit=True): def save(self, commit=True):
# Cache custom field values on object prior to save to ensure change logging # Save custom field data on instance
for cf_name in self.custom_fields: for cf_name in self.custom_fields:
self.instance._cf[cf_name[3:]] = self.cleaned_data.get(cf_name) self.instance.custom_field_data[cf_name[3:]] = self.cleaned_data.get(cf_name)
obj = super().save(commit) return super().save(commit)
# Handle custom fields the same way we do M2M fields
if commit:
self._save_custom_fields()
else:
obj.save_custom_fields = self._save_custom_fields
return obj
class CustomFieldModelCSVForm(CSVModelForm, CustomFieldModelForm): class CustomFieldModelCSVForm(CSVModelForm, CustomFieldModelForm):

View File

@ -1,3 +1,4 @@
from collections import OrderedDict
from datetime import date from datetime import date
from django import forms from django import forms
@ -34,6 +35,15 @@ class CustomFieldModel(models.Model):
""" """
return self.custom_field_data return self.custom_field_data
def get_custom_fields(self):
"""
Return a dictionary of custom fields for a single object in the form {<field>: value}.
"""
fields = CustomField.objects.get_for_model(self)
return OrderedDict([
(field, self.custom_field_data.get(field.name)) for field in fields
])
class CustomFieldManager(models.Manager): class CustomFieldManager(models.Manager):
use_in_migrations = True use_in_migrations = True

View File

@ -174,7 +174,7 @@ class CustomFieldAPITest(APITestCase):
} }
cls.sites[1].save() cls.sites[1].save()
def test_get_single_object_without_custom_field_values(self): def test_get_single_object_without_custom_field_data(self):
""" """
Validate that custom fields are present on an object even if it has no values defined. Validate that custom fields are present on an object even if it has no values defined.
""" """
@ -192,13 +192,11 @@ class CustomFieldAPITest(APITestCase):
'choice_field': None, 'choice_field': None,
}) })
def test_get_single_object_with_custom_field_values(self): def test_get_single_object_with_custom_field_data(self):
""" """
Validate that custom fields are present and correctly set for an object with values defined. Validate that custom fields are present and correctly set for an object with values defined.
""" """
site2_cfvs = { site2_cfvs = self.sites[1].custom_field_data
cfv.field.name: cfv.value for cfv in self.sites[1].custom_field_values.all()
}
url = reverse('dcim-api:site-detail', kwargs={'pk': self.sites[1].pk}) url = reverse('dcim-api:site-detail', kwargs={'pk': self.sites[1].pk})
self.add_permissions('dcim.view_site') self.add_permissions('dcim.view_site')
@ -236,15 +234,12 @@ class CustomFieldAPITest(APITestCase):
# Validate database data # Validate database data
site = Site.objects.get(pk=response.data['id']) site = Site.objects.get(pk=response.data['id'])
cfvs = { self.assertEqual(site.custom_field_data['text_field'], self.cf_text.default)
cfv.field.name: cfv.value for cfv in site.custom_field_values.all() self.assertEqual(site.custom_field_data['number_field'], self.cf_integer.default)
} self.assertEqual(site.custom_field_data['boolean_field'], self.cf_boolean.default)
self.assertEqual(cfvs['text_field'], self.cf_text.default) self.assertEqual(str(site.custom_field_data['date_field']), self.cf_date.default)
self.assertEqual(cfvs['number_field'], self.cf_integer.default) self.assertEqual(site.custom_field_data['url_field'], self.cf_url.default)
self.assertEqual(cfvs['boolean_field'], self.cf_boolean.default) self.assertEqual(site.custom_field_data['choice_field'].pk, self.cf_select_choice1.pk)
self.assertEqual(str(cfvs['date_field']), self.cf_date.default)
self.assertEqual(cfvs['url_field'], self.cf_url.default)
self.assertEqual(cfvs['choice_field'].pk, self.cf_select_choice1.pk)
def test_create_single_object_with_values(self): def test_create_single_object_with_values(self):
""" """
@ -280,15 +275,12 @@ class CustomFieldAPITest(APITestCase):
# Validate database data # Validate database data
site = Site.objects.get(pk=response.data['id']) site = Site.objects.get(pk=response.data['id'])
cfvs = { self.assertEqual(site.custom_field_data['text_field'], data_cf['text_field'])
cfv.field.name: cfv.value for cfv in site.custom_field_values.all() self.assertEqual(site.custom_field_data['number_field'], data_cf['number_field'])
} self.assertEqual(site.custom_field_data['boolean_field'], data_cf['boolean_field'])
self.assertEqual(cfvs['text_field'], data_cf['text_field']) self.assertEqual(str(site.custom_field_data['date_field']), data_cf['date_field'])
self.assertEqual(cfvs['number_field'], data_cf['number_field']) self.assertEqual(site.custom_field_data['url_field'], data_cf['url_field'])
self.assertEqual(cfvs['boolean_field'], data_cf['boolean_field']) self.assertEqual(site.custom_field_data['choice_field'].pk, data_cf['choice_field'])
self.assertEqual(str(cfvs['date_field']), data_cf['date_field'])
self.assertEqual(cfvs['url_field'], data_cf['url_field'])
self.assertEqual(cfvs['choice_field'].pk, data_cf['choice_field'])
def test_create_multiple_objects_with_defaults(self): def test_create_multiple_objects_with_defaults(self):
""" """
@ -329,15 +321,12 @@ class CustomFieldAPITest(APITestCase):
# Validate database data # Validate database data
site = Site.objects.get(pk=response.data[i]['id']) site = Site.objects.get(pk=response.data[i]['id'])
cfvs = { self.assertEqual(site.custom_field_data['text_field'], self.cf_text.default)
cfv.field.name: cfv.value for cfv in site.custom_field_values.all() self.assertEqual(site.custom_field_data['number_field'], self.cf_integer.default)
} self.assertEqual(site.custom_field_data['boolean_field'], self.cf_boolean.default)
self.assertEqual(cfvs['text_field'], self.cf_text.default) self.assertEqual(str(site.custom_field_data['date_field']), self.cf_date.default)
self.assertEqual(cfvs['number_field'], self.cf_integer.default) self.assertEqual(site.custom_field_data['url_field'], self.cf_url.default)
self.assertEqual(cfvs['boolean_field'], self.cf_boolean.default) self.assertEqual(site.custom_field_data['choice_field'].pk, self.cf_select_choice1.pk)
self.assertEqual(str(cfvs['date_field']), self.cf_date.default)
self.assertEqual(cfvs['url_field'], self.cf_url.default)
self.assertEqual(cfvs['choice_field'].pk, self.cf_select_choice1.pk)
def test_create_multiple_objects_with_values(self): def test_create_multiple_objects_with_values(self):
""" """
@ -388,24 +377,20 @@ class CustomFieldAPITest(APITestCase):
# Validate database data # Validate database data
site = Site.objects.get(pk=response.data[i]['id']) site = Site.objects.get(pk=response.data[i]['id'])
cfvs = { self.assertEqual(site.custom_field_data['text_field'], custom_field_data['text_field'])
cfv.field.name: cfv.value for cfv in site.custom_field_values.all() self.assertEqual(site.custom_field_data['number_field'], custom_field_data['number_field'])
} self.assertEqual(site.custom_field_data['boolean_field'], custom_field_data['boolean_field'])
self.assertEqual(cfvs['text_field'], custom_field_data['text_field']) self.assertEqual(str(site.custom_field_data['date_field']), custom_field_data['date_field'])
self.assertEqual(cfvs['number_field'], custom_field_data['number_field']) self.assertEqual(site.custom_field_data['url_field'], custom_field_data['url_field'])
self.assertEqual(cfvs['boolean_field'], custom_field_data['boolean_field']) self.assertEqual(site.custom_field_data['choice_field'].pk, custom_field_data['choice_field'])
self.assertEqual(str(cfvs['date_field']), custom_field_data['date_field'])
self.assertEqual(cfvs['url_field'], custom_field_data['url_field'])
self.assertEqual(cfvs['choice_field'].pk, custom_field_data['choice_field'])
def test_update_single_object_with_values(self): def test_update_single_object_with_values(self):
""" """
Update an object with existing custom field values. Ensure that only the updated custom field values are Update an object with existing custom field values. Ensure that only the updated custom field values are
modified. modified.
""" """
site2_original_cfvs = { site = self.sites[1]
cfv.field.name: cfv.value for cfv in self.sites[1].custom_field_values.all() original_cfvs = {**site.custom_field_data}
}
data = { data = {
'custom_fields': { 'custom_fields': {
'text_field': 'ABCD', 'text_field': 'ABCD',
@ -430,15 +415,13 @@ class CustomFieldAPITest(APITestCase):
# self.assertEqual(response_cf['choice_field']['label'], site2_original_cfvs['choice_field'].value) # self.assertEqual(response_cf['choice_field']['label'], site2_original_cfvs['choice_field'].value)
# Validate database data # Validate database data
site2_updated_cfvs = { site.refresh_from_db()
cfv.field.name: cfv.value for cfv in self.sites[1].custom_field_values.all() self.assertEqual(site.custom_field_data['text_field'], data_cf['text_field'])
} self.assertEqual(site.custom_field_data['number_field'], data_cf['number_field'])
self.assertEqual(site2_updated_cfvs['text_field'], data_cf['text_field']) self.assertEqual(site.custom_field_data['boolean_field'], original_cfvs['boolean_field'])
self.assertEqual(site2_updated_cfvs['number_field'], data_cf['number_field']) self.assertEqual(site.custom_field_data['date_field'], original_cfvs['date_field'])
self.assertEqual(site2_updated_cfvs['boolean_field'], site2_original_cfvs['boolean_field']) self.assertEqual(site.custom_field_data['url_field'], original_cfvs['url_field'])
self.assertEqual(site2_updated_cfvs['date_field'], site2_original_cfvs['date_field']) self.assertEqual(site.custom_field_data['choice_field'], original_cfvs['choice_field'])
self.assertEqual(site2_updated_cfvs['url_field'], site2_original_cfvs['url_field'])
self.assertEqual(site2_updated_cfvs['choice_field'], site2_original_cfvs['choice_field'])
class CustomFieldChoiceAPITest(APITestCase): class CustomFieldChoiceAPITest(APITestCase):
@ -514,31 +497,26 @@ class CustomFieldImportTest(TestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
# Validate data for site 1 # Validate data for site 1
custom_field_values = { site1 = Site.objects.get(name='Site 1')
cf.name: value for cf, value in Site.objects.get(name='Site 1').custom_field_data self.assertEqual(len(site1.custom_field_data), 6)
} self.assertEqual(site1.custom_field_data['text'], 'ABC')
self.assertEqual(len(custom_field_values), 6) self.assertEqual(site1.custom_field_data['integer'], 123)
self.assertEqual(custom_field_values['text'], 'ABC') self.assertEqual(site1.custom_field_data['boolean'], True)
self.assertEqual(custom_field_values['integer'], 123) self.assertEqual(site1.custom_field_data['date'], date(2020, 1, 1))
self.assertEqual(custom_field_values['boolean'], True) self.assertEqual(site1.custom_field_data['url'], 'http://example.com/1')
self.assertEqual(custom_field_values['date'], date(2020, 1, 1)) self.assertEqual(site1.custom_field_data['select'].value, 'Choice A')
self.assertEqual(custom_field_values['url'], 'http://example.com/1')
self.assertEqual(custom_field_values['select'].value, 'Choice A')
# Validate data for site 2 # Validate data for site 2
custom_field_values = { site2 = Site.objects.get(name='Site 2')
cf.name: value for cf, value in Site.objects.get(name='Site 2').custom_field_data self.assertEqual(len(site2.custom_field_data), 6)
} self.assertEqual(site2.custom_field_data['text'], 'DEF')
self.assertEqual(len(custom_field_values), 6) self.assertEqual(site2.custom_field_data['integer'], 456)
self.assertEqual(custom_field_values['text'], 'DEF') self.assertEqual(site2.custom_field_data['boolean'], False)
self.assertEqual(custom_field_values['integer'], 456) self.assertEqual(site2.custom_field_data['date'], date(2020, 1, 2))
self.assertEqual(custom_field_values['boolean'], False) self.assertEqual(site2.custom_field_data['url'], 'http://example.com/2')
self.assertEqual(custom_field_values['date'], date(2020, 1, 2)) self.assertEqual(site2.custom_field_data['select'].value, 'Choice B')
self.assertEqual(custom_field_values['url'], 'http://example.com/2')
self.assertEqual(custom_field_values['select'].value, 'Choice B')
# No CustomFieldValues should be created for site 3 # No CustomFieldValues should be created for site 3
obj_type = ContentType.objects.get_for_model(Site)
site3 = Site.objects.get(name='Site 3') site3 = Site.objects.get(name='Site 3')
self.assertEqual(site3.custom_field_data, {}) self.assertEqual(site3.custom_field_data, {})