From 983032189abb1f0e26835f761ae71ea6da0c509a Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Tue, 24 Sep 2024 09:56:39 -0700 Subject: [PATCH] 17558 raise validation error if removing choice from choiceset that is currently used --- netbox/extras/models/customfields.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py index 889594902..017e65c24 100644 --- a/netbox/extras/models/customfields.py +++ b/netbox/extras/models/customfields.py @@ -785,6 +785,10 @@ class CustomFieldChoiceSet(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel def __str__(self): return self.name + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._original_extra_choices = self.__dict__.get('extra_choices') + def get_absolute_url(self): return reverse('extras:customfieldchoiceset', args=[self.pk]) @@ -818,6 +822,29 @@ class CustomFieldChoiceSet(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel if not self.base_choices and not self.extra_choices: raise ValidationError(_("Must define base or extra choices.")) + # check if removing any used choices + original_choices = [obj[1] for obj in self._original_extra_choices] + new_choices = [obj[1] for obj in self.extra_choices] + diff_choices = list(set(original_choices) - set(new_choices)) + if diff_choices: + # CustomFields using this ChoiceSet + for custom_field in self.choices_for.all(): + for object_type in custom_field.object_types.all(): + # unfortunately querying the whole array of diff_choices doesn't work + for choice in diff_choices: + # Need to OR query as can be multiple choice or single choice so + # have to do both contains and equals to catch both + query_args = { + f"custom_field_data__{custom_field.name}__contains": choice, + f"custom_field_data__{custom_field.name}": choice + } + if object_type.model_class().objects.filter(models.Q(**query_args, _connector=models.Q.OR)).exists(): + raise ValidationError( + _("Cannot remove choice {choice} as it is used in model {model}").format( + choice=choice, model=object_type + ) + ) + def save(self, *args, **kwargs): # Sort choices if alphabetical ordering is enforced