diff --git a/netbox/extras/forms/model_forms.py b/netbox/extras/forms/model_forms.py
index 2f617b682..aaa426ac3 100644
--- a/netbox/extras/forms/model_forms.py
+++ b/netbox/extras/forms/model_forms.py
@@ -10,6 +10,7 @@ from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site
from extras.choices import *
from extras.models import *
from extras.utils import FeatureQuery
+from netbox.config import get_config, PARAMS
from netbox.forms import NetBoxModelForm
from tenancy.models import Tenant, TenantGroup
from utilities.forms import BootstrapMixin, add_blank_choice
@@ -21,6 +22,7 @@ from virtualization.models import Cluster, ClusterGroup, ClusterType
__all__ = (
'ConfigContextForm',
+ 'ConfigRevisionForm',
'ConfigTemplateForm',
'CustomFieldForm',
'CustomLinkForm',
@@ -374,3 +376,77 @@ class JournalEntryForm(NetBoxModelForm):
'assigned_object_type': forms.HiddenInput,
'assigned_object_id': forms.HiddenInput,
}
+
+
+EMPTY_VALUES = ('', None, [], ())
+
+
+class FormMetaclass(forms.models.ModelFormMetaclass):
+
+ def __new__(mcs, name, bases, attrs):
+
+ # Emulate a declared field for each supported configuration parameter
+ param_fields = {}
+ for param in PARAMS:
+ field_kwargs = {
+ 'required': False,
+ 'label': param.label,
+ 'help_text': param.description,
+ }
+ field_kwargs.update(**param.field_kwargs)
+ param_fields[param.name] = param.field(**field_kwargs)
+ attrs.update(param_fields)
+
+ return super().__new__(mcs, name, bases, attrs)
+
+
+class ConfigRevisionForm(forms.BaseModelForm, metaclass=FormMetaclass):
+ """
+ Form for creating a new ConfigRevision.
+ """
+ class Meta:
+ widgets = {
+ 'comment': forms.Textarea(),
+ }
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ # Append current parameter values to form field help texts and check for static configurations
+ config = get_config()
+ for param in PARAMS:
+ value = getattr(config, param.name)
+ is_static = hasattr(settings, param.name)
+ if value:
+ help_text = self.fields[param.name].help_text
+ if help_text:
+ help_text += '
' # Line break
+ help_text += f'Current value: {value}'
+ if is_static:
+ help_text += ' (defined statically)'
+ elif value == param.default:
+ help_text += ' (default)'
+ self.fields[param.name].help_text = help_text
+ if is_static:
+ self.fields[param.name].disabled = True
+
+ def save(self, commit=True):
+ instance = super().save(commit=False)
+
+ # Populate JSON data on the instance
+ instance.data = self.render_json()
+
+ if commit:
+ instance.save()
+
+ return instance
+
+ def render_json(self):
+ json = {}
+
+ # Iterate through each field and populate non-empty values
+ for field_name in self.declared_fields:
+ if field_name in self.cleaned_data and self.cleaned_data[field_name] not in EMPTY_VALUES:
+ json[field_name] = self.cleaned_data[field_name]
+
+ return json
diff --git a/netbox/extras/urls.py b/netbox/extras/urls.py
index 811576b3c..13f8fdf28 100644
--- a/netbox/extras/urls.py
+++ b/netbox/extras/urls.py
@@ -118,5 +118,5 @@ urlpatterns = [
# Config Revision
path('config-revision/', views.ConfigRevisionView.as_view(), name='config_revision'),
-
+ path('config-revision/add/', views.ConfigRevisionEditView.as_view(), name='config_revision_add'),
]
diff --git a/netbox/extras/views.py b/netbox/extras/views.py
index 81bea84f1..842936766 100644
--- a/netbox/extras/views.py
+++ b/netbox/extras/views.py
@@ -1195,6 +1195,7 @@ class RenderMarkdownView(View):
# Config Revision
#
+@register_model_view(ConfigRevision)
class ConfigRevisionView(generic.ObjectView):
queryset = ConfigRevision.objects.all()
@@ -1206,3 +1207,9 @@ class ConfigRevisionView(generic.ObjectView):
'tab': self.tab,
**self.get_extra_context(request, instance),
})
+
+
+@register_model_view(ConfigRevision, 'edit')
+class ConfigRevisionEditView(generic.ObjectEditView):
+ queryset = ConfigRevision.objects.all()
+ form = forms.ConfigRevisionForm