mirror of
https://github.com/netbox-community/netbox.git
synced 2026-03-31 22:53:21 +02:00
feat(dcim): Add changelog message support to bulk component creation (#21769)
Add ChangelogMessageMixin to DeviceBulkAddComponentForm and capture changelog_message during bulk component creation. Ensure message is applied to each created component instance. Add test coverage for changelog message propagation.
This commit is contained in:
@@ -3,7 +3,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from dcim.models import *
|
||||
from extras.models import Tag
|
||||
from netbox.forms.mixins import CustomFieldsMixin
|
||||
from netbox.forms.mixins import ChangelogMessageMixin, CustomFieldsMixin
|
||||
from utilities.forms import form_from_model
|
||||
from utilities.forms.fields import DynamicModelMultipleChoiceField, ExpandableNameField
|
||||
from utilities.forms.mixins import BackgroundJobMixin
|
||||
@@ -28,7 +28,7 @@ __all__ = (
|
||||
# Device components
|
||||
#
|
||||
|
||||
class DeviceBulkAddComponentForm(BackgroundJobMixin, CustomFieldsMixin, ComponentCreateForm):
|
||||
class DeviceBulkAddComponentForm(BackgroundJobMixin, ChangelogMessageMixin, CustomFieldsMixin, ComponentCreateForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=Device.objects.all(),
|
||||
widget=forms.MultipleHiddenInput()
|
||||
|
||||
@@ -3,11 +3,13 @@ from decimal import Decimal
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
import yaml
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import override_settings, tag
|
||||
from django.urls import reverse
|
||||
from netaddr import EUI
|
||||
|
||||
from core.models import ObjectType
|
||||
from core.choices import ObjectChangeActionChoices
|
||||
from core.models import ObjectChange, ObjectType
|
||||
from dcim.choices import *
|
||||
from dcim.constants import *
|
||||
from dcim.models import *
|
||||
@@ -2741,6 +2743,50 @@ class ConsolePortTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
f"{console_ports[2].pk},Console Port 9,New description9",
|
||||
)
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], EXEMPT_EXCLUDE_MODELS=[])
|
||||
def test_bulk_add_components_with_changelog_message(self):
|
||||
device1 = Device.objects.get(name='Device 1')
|
||||
device2 = create_test_device('Device 2')
|
||||
changelog_message = 'Bulk-created console ports'
|
||||
|
||||
obj_perm = ObjectPermission(
|
||||
name='Test permission',
|
||||
actions=['add'],
|
||||
)
|
||||
obj_perm.save()
|
||||
obj_perm.users.add(self.user)
|
||||
obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model))
|
||||
|
||||
request = {
|
||||
'path': reverse('dcim:device_bulk_add_consoleport'),
|
||||
'data': post_data({
|
||||
'pk': [device1.pk, device2.pk],
|
||||
'name': 'Console Port Bulk',
|
||||
'type': ConsolePortTypeChoices.TYPE_RJ45,
|
||||
'description': 'Bulk-created console port',
|
||||
'changelog_message': changelog_message,
|
||||
'_create': True,
|
||||
}),
|
||||
}
|
||||
|
||||
initial_count = self._get_queryset().count()
|
||||
response = self.client.post(**request)
|
||||
self.assertHttpStatus(response, 302)
|
||||
self.assertEqual(initial_count + 2, self._get_queryset().count())
|
||||
|
||||
created_ports = list(ConsolePort.objects.filter(name='Console Port Bulk').order_by('device_id'))
|
||||
self.assertEqual(len(created_ports), 2)
|
||||
self.assertEqual([port.device_id for port in created_ports], [device1.pk, device2.pk])
|
||||
|
||||
objectchanges = ObjectChange.objects.filter(
|
||||
action=ObjectChangeActionChoices.ACTION_CREATE,
|
||||
changed_object_type=ContentType.objects.get_for_model(ConsolePort),
|
||||
changed_object_id__in=[port.pk for port in created_ports],
|
||||
)
|
||||
self.assertEqual(objectchanges.count(), 2)
|
||||
for objectchange in objectchanges:
|
||||
self.assertEqual(objectchange.message, changelog_message)
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||
def test_trace(self):
|
||||
consoleport = ConsolePort.objects.first()
|
||||
|
||||
@@ -1148,6 +1148,7 @@ class BulkComponentCreateView(GetReturnURLMixin, BaseMultiObjectView):
|
||||
|
||||
new_components = []
|
||||
data = deepcopy(form.cleaned_data)
|
||||
changelog_message = data.pop('changelog_message', '')
|
||||
data.pop('background_job', None)
|
||||
replication_data = {
|
||||
field: data.pop(field) for field in form.replication_fields
|
||||
@@ -1170,6 +1171,8 @@ class BulkComponentCreateView(GetReturnURLMixin, BaseMultiObjectView):
|
||||
|
||||
component_form = self.model_form(component_data)
|
||||
if component_form.is_valid():
|
||||
if changelog_message:
|
||||
component_form.instance._changelog_message = changelog_message
|
||||
instance = component_form.save()
|
||||
logger.debug(f"Created {instance} on {instance.parent_object}")
|
||||
new_components.append(instance)
|
||||
|
||||
@@ -64,11 +64,17 @@ Context:
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Meta fields #}
|
||||
{% if form.background_job %}
|
||||
<div class="bg-primary-subtle border border-primary rounded-1 pt-3 px-3 mb-3">
|
||||
{% render_field form.background_job %}
|
||||
</div>
|
||||
{% if form.background_job or form.changelog_message %}
|
||||
<div class="bg-primary-subtle border border-primary rounded-1 pt-3 px-3 mb-3">
|
||||
{% if form.changelog_message %}
|
||||
{% render_field form.changelog_message %}
|
||||
{% endif %}
|
||||
{% if form.background_job %}
|
||||
{% render_field form.background_job %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="form-group text-end">
|
||||
<div class="col col-md-12">
|
||||
|
||||
Reference in New Issue
Block a user