Remove CustomField.choices

This commit is contained in:
Jeremy Stretch 2023-07-17 11:29:06 -04:00
parent f3bfe29751
commit 42a94078ce
3 changed files with 67 additions and 38 deletions

View File

@ -0,0 +1,17 @@
# Generated by Django 4.1.10 on 2023-07-17 15:22
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('extras', '0096_customfieldchoiceset'),
]
operations = [
migrations.RemoveField(
model_name='customfield',
name='choices',
),
]

View File

@ -166,12 +166,6 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
blank=True, blank=True,
null=True null=True
) )
choices = ArrayField(
base_field=models.CharField(max_length=100),
blank=True,
null=True,
help_text=_('Comma-separated list of available choices (for selection fields)')
)
ui_visibility = models.CharField( ui_visibility = models.CharField(
max_length=50, max_length=50,
choices=CustomFieldVisibilityChoices, choices=CustomFieldVisibilityChoices,
@ -189,8 +183,8 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
clone_fields = ( clone_fields = (
'content_types', 'type', 'object_type', 'group_name', 'description', 'required', 'search_weight', 'content_types', 'type', 'object_type', 'group_name', 'description', 'required', 'search_weight',
'filter_logic', 'default', 'weight', 'validation_minimum', 'validation_maximum', 'validation_regex', 'choices', 'filter_logic', 'default', 'weight', 'validation_minimum', 'validation_maximum', 'validation_regex',
'ui_visibility', 'is_cloneable', 'choice_set', 'ui_visibility', 'is_cloneable',
) )
class Meta: class Meta:
@ -216,6 +210,12 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
def search_type(self): def search_type(self):
return SEARCH_TYPES.get(self.type) return SEARCH_TYPES.get(self.type)
@property
def choices(self):
if self.choice_set:
return self.choice_set.choices
return []
def populate_initial_data(self, content_types): def populate_initial_data(self, content_types):
""" """
Populate initial custom field data upon either a) the creation of a new CustomField, or Populate initial custom field data upon either a) the creation of a new CustomField, or
@ -299,15 +299,6 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
'choice_set': "Choices may be set only for selection fields." 'choice_set': "Choices may be set only for selection fields."
}) })
# Selection fields must have at least one choice defined
if self.type in (
CustomFieldTypeChoices.TYPE_SELECT,
CustomFieldTypeChoices.TYPE_MULTISELECT
) and not self.choices:
raise ValidationError({
'choices': "Selection fields must specify at least one choice."
})
# A selection field's default (if any) must be present in its available choices # A selection field's default (if any) must be present in its available choices
if self.type == CustomFieldTypeChoices.TYPE_SELECT and self.default and self.default not in self.choices: if self.type == CustomFieldTypeChoices.TYPE_SELECT and self.default and self.default not in self.choices:
raise ValidationError({ raise ValidationError({

View File

@ -10,7 +10,7 @@ from dcim.filtersets import SiteFilterSet
from dcim.forms import SiteImportForm from dcim.forms import SiteImportForm
from dcim.models import Manufacturer, Rack, Site from dcim.models import Manufacturer, Rack, Site
from extras.choices import * from extras.choices import *
from extras.models import CustomField from extras.models import CustomField, CustomFieldChoiceSet
from ipam.models import VLAN from ipam.models import VLAN
from utilities.testing import APITestCase, TestCase from utilities.testing import APITestCase, TestCase
from virtualization.models import VirtualMachine from virtualization.models import VirtualMachine
@ -272,12 +272,18 @@ class CustomFieldTest(TestCase):
CHOICES = ('Option A', 'Option B', 'Option C') CHOICES = ('Option A', 'Option B', 'Option C')
value = CHOICES[1] value = CHOICES[1]
# Create a set of custom field choices
choice_set = CustomFieldChoiceSet.objects.create(
name='Custom Field Choice Set 1',
extra_choices=CHOICES
)
# Create a custom field & check that initial value is null # Create a custom field & check that initial value is null
cf = CustomField.objects.create( cf = CustomField.objects.create(
name='select_field', name='select_field',
type=CustomFieldTypeChoices.TYPE_SELECT, type=CustomFieldTypeChoices.TYPE_SELECT,
required=False, required=False,
choices=CHOICES choice_set=choice_set
) )
cf.content_types.set([self.object_type]) cf.content_types.set([self.object_type])
instance = Site.objects.first() instance = Site.objects.first()
@ -299,12 +305,18 @@ class CustomFieldTest(TestCase):
CHOICES = ['Option A', 'Option B', 'Option C'] CHOICES = ['Option A', 'Option B', 'Option C']
value = [CHOICES[1], CHOICES[2]] value = [CHOICES[1], CHOICES[2]]
# Create a set of custom field choices
choice_set = CustomFieldChoiceSet.objects.create(
name='Custom Field Choice Set 1',
extra_choices=CHOICES
)
# Create a custom field & check that initial value is null # Create a custom field & check that initial value is null
cf = CustomField.objects.create( cf = CustomField.objects.create(
name='multiselect_field', name='multiselect_field',
type=CustomFieldTypeChoices.TYPE_MULTISELECT, type=CustomFieldTypeChoices.TYPE_MULTISELECT,
required=False, required=False,
choices=CHOICES choice_set=choice_set
) )
cf.content_types.set([self.object_type]) cf.content_types.set([self.object_type])
instance = Site.objects.first() instance = Site.objects.first()
@ -438,6 +450,12 @@ class CustomFieldAPITest(APITestCase):
) )
VLAN.objects.bulk_create(vlans) VLAN.objects.bulk_create(vlans)
# Create a set of custom field choices
choice_set = CustomFieldChoiceSet.objects.create(
name='Custom Field Choice Set 1',
extra_choices=('Foo', 'Bar', 'Baz')
)
custom_fields = ( custom_fields = (
CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, name='text_field', default='foo'), CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, name='text_field', default='foo'),
CustomField(type=CustomFieldTypeChoices.TYPE_LONGTEXT, name='longtext_field', default='ABC'), CustomField(type=CustomFieldTypeChoices.TYPE_LONGTEXT, name='longtext_field', default='ABC'),
@ -452,17 +470,13 @@ class CustomFieldAPITest(APITestCase):
type=CustomFieldTypeChoices.TYPE_SELECT, type=CustomFieldTypeChoices.TYPE_SELECT,
name='select_field', name='select_field',
default='Foo', default='Foo',
choices=( choice_set=choice_set
'Foo', 'Bar', 'Baz'
)
), ),
CustomField( CustomField(
type=CustomFieldTypeChoices.TYPE_MULTISELECT, type=CustomFieldTypeChoices.TYPE_MULTISELECT,
name='multiselect_field', name='multiselect_field',
default=['Foo'], default=['Foo'],
choices=( choice_set=choice_set
'Foo', 'Bar', 'Baz'
)
), ),
CustomField( CustomField(
type=CustomFieldTypeChoices.TYPE_OBJECT, type=CustomFieldTypeChoices.TYPE_OBJECT,
@ -1024,6 +1038,12 @@ class CustomFieldImportTest(TestCase):
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
# Create a set of custom field choices
choice_set = CustomFieldChoiceSet.objects.create(
name='Custom Field Choice Set 1',
extra_choices=('Choice A', 'Choice B', 'Choice C')
)
custom_fields = ( custom_fields = (
CustomField(name='text', type=CustomFieldTypeChoices.TYPE_TEXT), CustomField(name='text', type=CustomFieldTypeChoices.TYPE_TEXT),
CustomField(name='longtext', type=CustomFieldTypeChoices.TYPE_LONGTEXT), CustomField(name='longtext', type=CustomFieldTypeChoices.TYPE_LONGTEXT),
@ -1034,12 +1054,8 @@ class CustomFieldImportTest(TestCase):
CustomField(name='datetime', type=CustomFieldTypeChoices.TYPE_DATETIME), CustomField(name='datetime', type=CustomFieldTypeChoices.TYPE_DATETIME),
CustomField(name='url', type=CustomFieldTypeChoices.TYPE_URL), CustomField(name='url', type=CustomFieldTypeChoices.TYPE_URL),
CustomField(name='json', type=CustomFieldTypeChoices.TYPE_JSON), CustomField(name='json', type=CustomFieldTypeChoices.TYPE_JSON),
CustomField(name='select', type=CustomFieldTypeChoices.TYPE_SELECT, choices=[ CustomField(name='select', type=CustomFieldTypeChoices.TYPE_SELECT, choice_set=choice_set),
'Choice A', 'Choice B', 'Choice C', CustomField(name='multiselect', type=CustomFieldTypeChoices.TYPE_MULTISELECT, choice_set=choice_set),
]),
CustomField(name='multiselect', type=CustomFieldTypeChoices.TYPE_MULTISELECT, choices=[
'Choice A', 'Choice B', 'Choice C',
]),
) )
for cf in custom_fields: for cf in custom_fields:
cf.save() cf.save()
@ -1203,6 +1219,11 @@ class CustomFieldModelFilterTest(TestCase):
Manufacturer(name='Manufacturer 4', slug='manufacturer-4'), Manufacturer(name='Manufacturer 4', slug='manufacturer-4'),
)) ))
choice_set = CustomFieldChoiceSet.objects.create(
name='Custom Field Choice Set 1',
extra_choices=['A', 'B', 'C', 'X']
)
# Integer filtering # Integer filtering
cf = CustomField(name='cf1', type=CustomFieldTypeChoices.TYPE_INTEGER) cf = CustomField(name='cf1', type=CustomFieldTypeChoices.TYPE_INTEGER)
cf.save() cf.save()
@ -1263,7 +1284,7 @@ class CustomFieldModelFilterTest(TestCase):
cf = CustomField( cf = CustomField(
name='cf9', name='cf9',
type=CustomFieldTypeChoices.TYPE_SELECT, type=CustomFieldTypeChoices.TYPE_SELECT,
choices=['Foo', 'Bar', 'Baz'] choice_set=choice_set
) )
cf.save() cf.save()
cf.content_types.set([obj_type]) cf.content_types.set([obj_type])
@ -1272,7 +1293,7 @@ class CustomFieldModelFilterTest(TestCase):
cf = CustomField( cf = CustomField(
name='cf10', name='cf10',
type=CustomFieldTypeChoices.TYPE_MULTISELECT, type=CustomFieldTypeChoices.TYPE_MULTISELECT,
choices=['A', 'B', 'C', 'X'] choice_set=choice_set
) )
cf.save() cf.save()
cf.content_types.set([obj_type]) cf.content_types.set([obj_type])
@ -1305,7 +1326,7 @@ class CustomFieldModelFilterTest(TestCase):
'cf6': '2016-06-26', 'cf6': '2016-06-26',
'cf7': 'http://a.example.com', 'cf7': 'http://a.example.com',
'cf8': 'http://a.example.com', 'cf8': 'http://a.example.com',
'cf9': 'Foo', 'cf9': 'A',
'cf10': ['A', 'X'], 'cf10': ['A', 'X'],
'cf11': manufacturers[0].pk, 'cf11': manufacturers[0].pk,
'cf12': [manufacturers[0].pk, manufacturers[3].pk], 'cf12': [manufacturers[0].pk, manufacturers[3].pk],
@ -1319,7 +1340,7 @@ class CustomFieldModelFilterTest(TestCase):
'cf6': '2016-06-27', 'cf6': '2016-06-27',
'cf7': 'http://b.example.com', 'cf7': 'http://b.example.com',
'cf8': 'http://b.example.com', 'cf8': 'http://b.example.com',
'cf9': 'Bar', 'cf9': 'B',
'cf10': ['B', 'X'], 'cf10': ['B', 'X'],
'cf11': manufacturers[1].pk, 'cf11': manufacturers[1].pk,
'cf12': [manufacturers[1].pk, manufacturers[3].pk], 'cf12': [manufacturers[1].pk, manufacturers[3].pk],
@ -1333,7 +1354,7 @@ class CustomFieldModelFilterTest(TestCase):
'cf6': '2016-06-28', 'cf6': '2016-06-28',
'cf7': 'http://c.example.com', 'cf7': 'http://c.example.com',
'cf8': 'http://c.example.com', 'cf8': 'http://c.example.com',
'cf9': 'Baz', 'cf9': 'C',
'cf10': ['C', 'X'], 'cf10': ['C', 'X'],
'cf11': manufacturers[2].pk, 'cf11': manufacturers[2].pk,
'cf12': [manufacturers[2].pk, manufacturers[3].pk], 'cf12': [manufacturers[2].pk, manufacturers[3].pk],
@ -1399,7 +1420,7 @@ class CustomFieldModelFilterTest(TestCase):
self.assertEqual(self.filterset({'cf_cf8': ['example.com']}, self.queryset).qs.count(), 3) self.assertEqual(self.filterset({'cf_cf8': ['example.com']}, self.queryset).qs.count(), 3)
def test_filter_select(self): def test_filter_select(self):
self.assertEqual(self.filterset({'cf_cf9': ['Foo', 'Bar']}, self.queryset).qs.count(), 2) self.assertEqual(self.filterset({'cf_cf9': ['A', 'B']}, self.queryset).qs.count(), 2)
def test_filter_multiselect(self): def test_filter_multiselect(self):
self.assertEqual(self.filterset({'cf_cf10': ['A', 'B']}, self.queryset).qs.count(), 2) self.assertEqual(self.filterset({'cf_cf10': ['A', 'B']}, self.queryset).qs.count(), 2)