Просмотр исходного кода

Fixes #23652: Resolve event type choices lazily in event rule forms

Arthur 19 часов назад
Родитель
Сommit
cd5acdedba

+ 1 - 1
netbox/extras/forms/bulk_edit.py

@@ -302,7 +302,7 @@ class EventRuleBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm):
         widget=BulkEditNullBooleanSelect()
     )
     event_types = forms.MultipleChoiceField(
-        choices=get_event_type_choices(),
+        choices=get_event_type_choices,
         required=False,
         label=_('Event types')
     )

+ 1 - 1
netbox/extras/forms/bulk_import.py

@@ -317,7 +317,7 @@ class EventRuleImportForm(OwnerCSVMixin, NetBoxModelImportForm):
         help_text=_("One or more assigned object types")
     )
     event_types = CSVMultipleChoiceField(
-        choices=get_event_type_choices(),
+        choices=get_event_type_choices,
         label=_('Event types'),
         help_text=_('The event type(s) which will trigger this rule')
     )

+ 1 - 1
netbox/extras/forms/model_forms.py

@@ -607,7 +607,7 @@ class EventRuleForm(OwnerMixin, NetBoxModelForm):
         queryset=ObjectType.objects.with_feature('event_rules'),
     )
     event_types = MultipleChoiceField(
-        choices=get_event_type_choices(),
+        choices=get_event_type_choices,
         label=_('Event types')
     )
     action_choice = ChoiceField(

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

@@ -12,12 +12,14 @@ from dcim.forms import SiteForm
 from dcim.models import Site
 from extras.choices import CustomFieldTypeChoices, EventRuleActionChoices
 from extras.forms import SavedFilterForm, TableConfigBulkEditForm, TableConfigForm
+from extras.forms.bulk_edit import EventRuleBulkEditForm
 from extras.forms.bulk_import import EventRuleImportForm
 from extras.forms.filtersets import EventRuleFilterForm
 from extras.forms.model_forms import CustomFieldChoiceSetForm, EventRuleForm
 from extras.forms.scripts import ScriptFileForm
 from extras.models import CustomField, CustomFieldChoiceSet, EventRule, NotificationGroup, Script, ScriptModule, Webhook
 from netbox.event_rules import EventRuleAction, register_event_rule_action
+from netbox.events import EventType
 from netbox.registry import registry
 from utilities.forms.widgets import HTMXSelect
 
@@ -522,6 +524,41 @@ class EventRuleFilterFormTestCase(TestCase):
         self.assertIn(None, choices)  # The blank choice is retained
 
 
+class EventRuleEventTypeChoicesTestCase(TestCase):
+    """
+    Event types registered after the forms module is imported (e.g. in a plugin's ready()) must be
+    offered by every event rule form.
+    """
+
+    def setUp(self):
+        EventType('test_late_event', 'Late event').register()
+        self.addCleanup(registry['event_types'].pop, 'test_late_event', None)
+
+    def test_model_form_offers_late_event_type(self):
+        choices = dict(EventRuleForm().fields['event_types'].choices)
+        self.assertEqual(choices.get('test_late_event'), 'Late event')
+
+    def test_bulk_edit_form_offers_late_event_type(self):
+        choices = dict(EventRuleBulkEditForm().fields['event_types'].choices)
+        self.assertEqual(choices.get('test_late_event'), 'Late event')
+
+    def test_filter_form_offers_late_event_type(self):
+        choices = dict(EventRuleFilterForm().fields['event_type'].choices)
+        self.assertEqual(choices.get('test_late_event'), 'Late event')
+
+    def test_import_form_accepts_late_event_type(self):
+        webhook = Webhook.objects.create(name='Late Event Webhook', payload_url='http://localhost:9000/')
+        form = EventRuleImportForm(data={
+            'name': 'Late Event Rule',
+            'object_types': 'dcim.site',
+            'event_types': 'test_late_event',
+            'action_type': EventRuleActionChoices.WEBHOOK,
+            'action_object': webhook.name,
+        })
+        self.assertTrue(form.is_valid(), form.errors)
+        self.assertEqual(form.cleaned_data['event_types'], ['test_late_event'])
+
+
 class EventRuleImportFormTestCase(TestCase):
     """
     EventRuleImportForm resolves action_object via each registered action's resolve_import_object()

+ 5 - 1
netbox/utilities/forms/fields/csv.py

@@ -37,7 +37,11 @@ class CSVChoicesMixin:
 
     def __init__(self, *, choices=(), **kwargs):
         super().__init__(choices=choices, **kwargs)
-        self.choices = unpack_grouped_choices(choices)
+        # Keep callable choices lazy so they're resolved on each access
+        if callable(choices):
+            self.choices = lambda: unpack_grouped_choices(choices())
+        else:
+            self.choices = unpack_grouped_choices(choices)
 
 
 class CSVChoiceField(CSVChoicesMixin, forms.ChoiceField):