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

Add RESERVED_ACTIONS constant and fix dedup in registered actions

- Define RESERVED_ACTIONS in users/constants.py for the four built-in
  permission actions (view, add, change, delete)
- Replace hardcoded action lists in ObjectPermissionForm with the constant
- Fix duplicate action names in clean() when the same action is registered
  across multiple models (e.g. render_config for Device and VirtualMachine)
- Fix template substring matching bug in objectpermission.html detail view
  by passing RESERVED_ACTIONS through view context for proper list membership
Jason Novinger 22 часов назад
Родитель
Сommit
e2537305b2

+ 1 - 1
netbox/templates/users/objectpermission.html

@@ -47,7 +47,7 @@
             <td>{% checkmark object.can_delete %}</td>
           </tr>
           {% for action in object.actions %}
-            {% if action not in 'view,add,change,delete' %}
+            {% if action not in reserved_actions %}
               <tr>
                 <th scope="row">{{ action }}</th>
                 <td>{% checkmark True %}</td>

+ 4 - 0
netbox/users/constants.py

@@ -10,6 +10,10 @@ OBJECTPERMISSION_OBJECT_TYPES = (
 
 CONSTRAINT_TOKEN_USER = '$user'
 
+# Built-in actions that receive special handling (dedicated checkboxes, model properties)
+# and should not be registered as custom model actions.
+RESERVED_ACTIONS = ('view', 'add', 'change', 'delete')
+
 # API tokens
 TOKEN_PREFIX = 'nbt_'  # Used for v2 tokens only
 TOKEN_KEY_LENGTH = 12

+ 5 - 4
netbox/users/forms/model_forms.py

@@ -423,7 +423,7 @@ class ObjectPermissionForm(forms.ModelForm):
             remaining_actions = list(self.instance.actions)
 
             # Check the appropriate CRUD checkboxes
-            for action in ['view', 'add', 'change', 'delete']:
+            for action in RESERVED_ACTIONS:
                 if action in remaining_actions:
                     self.fields[f'can_{action}'].initial = True
                     remaining_actions.remove(action)
@@ -450,7 +450,7 @@ class ObjectPermissionForm(forms.ModelForm):
                 if isinstance(self.initial['actions'], str):
                     self.initial['actions'] = [self.initial['actions']]
                 if cloned_actions := self.initial['actions']:
-                    for action in ['view', 'add', 'change', 'delete']:
+                    for action in RESERVED_ACTIONS:
                         if action in cloned_actions:
                             self.fields[f'can_{action}'].initial = True
                             self.initial['actions'].remove(action)
@@ -479,10 +479,11 @@ class ObjectPermissionForm(forms.ModelForm):
                         'Action "{action}" is for {model} which is not selected.'
                     ).format(action=action_name, model=model_key)
                 })
-            final_actions.append(action_name)
+            if action_name not in final_actions:
+                final_actions.append(action_name)
 
         # Append any of the selected CRUD checkboxes to the actions list
-        for action in ['view', 'add', 'change', 'delete']:
+        for action in RESERVED_ACTIONS:
             if self.cleaned_data.get(f'can_{action}') and action not in final_actions:
                 final_actions.append(action)
 

+ 6 - 0
netbox/users/views.py

@@ -10,6 +10,7 @@ from utilities.query import count_related
 from utilities.views import GetRelatedModelsMixin, register_model_view
 
 from . import filtersets, forms, tables
+from .constants import RESERVED_ACTIONS
 from .models import Group, ObjectPermission, Owner, OwnerGroup, Token, User
 
 #
@@ -214,6 +215,11 @@ class ObjectPermissionView(generic.ObjectView):
     queryset = ObjectPermission.objects.all()
     template_name = 'users/objectpermission.html'
 
+    def get_extra_context(self, request, instance):
+        return {
+            'reserved_actions': RESERVED_ACTIONS,
+        }
+
 
 @register_model_view(ObjectPermission, 'add', detail=False)
 @register_model_view(ObjectPermission, 'edit')