mirror of
https://github.com/netbox-community/netbox.git
synced 2026-04-21 08:21:48 +02:00
* Add message field to ObjectChange model * Set max length on changelog message * Enable changelog messages for single object operations * Fix tests * Add changelog message support for bulk edit & bulk delete * Cosmetic improvements to form fields * Fix bulk operation templates * Add message support for bulk import/update * Add REST API support for changelog messages (WIP) * Fix changelog_message assignment * Enable changelog message support for bulk deletions * Add documentation * Fix changelog message support for VirtualChassis * Add ChangeLoggingMixin to necesssary model forms * Introduce get_random_string() utility function for tests * Incorporate changelog messages for object view tests * Incorporate changelog messages for object bulk view tests * Add missing mixins for changelog message support * Tweak test to generate expected number of change records * Finish adding tests for changelog message functionality * Misc cleanup * Fixes #19956: Prevent duplicate deletion records from cascading deletions * Tweak bulk deletion test to work around cascading deletions issue * Correct API URL
This commit is contained in:
@@ -8,7 +8,7 @@ from dcim.api.serializers_.sites import LocationSerializer, RegionSerializer, Si
|
||||
from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site, SiteGroup
|
||||
from extras.models import ConfigContext, Tag
|
||||
from netbox.api.fields import SerializedPKRelatedField
|
||||
from netbox.api.serializers import ValidatedModelSerializer
|
||||
from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
|
||||
from tenancy.api.serializers_.tenants import TenantSerializer, TenantGroupSerializer
|
||||
from tenancy.models import Tenant, TenantGroup
|
||||
from virtualization.api.serializers_.clusters import ClusterSerializer, ClusterGroupSerializer, ClusterTypeSerializer
|
||||
@@ -19,7 +19,7 @@ __all__ = (
|
||||
)
|
||||
|
||||
|
||||
class ConfigContextSerializer(ValidatedModelSerializer):
|
||||
class ConfigContextSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
|
||||
regions = SerializedPKRelatedField(
|
||||
queryset=Region.objects.all(),
|
||||
serializer=RegionSerializer,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from core.api.serializers_.data import DataFileSerializer, DataSourceSerializer
|
||||
from extras.models import ConfigTemplate
|
||||
from netbox.api.serializers import ValidatedModelSerializer
|
||||
from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
|
||||
from netbox.api.serializers.features import TaggableModelSerializer
|
||||
|
||||
__all__ = (
|
||||
@@ -8,7 +8,7 @@ __all__ = (
|
||||
)
|
||||
|
||||
|
||||
class ConfigTemplateSerializer(TaggableModelSerializer, ValidatedModelSerializer):
|
||||
class ConfigTemplateSerializer(ChangeLogMessageSerializer, TaggableModelSerializer, ValidatedModelSerializer):
|
||||
data_source = DataSourceSerializer(
|
||||
nested=True,
|
||||
required=False
|
||||
|
||||
@@ -7,7 +7,7 @@ from core.models import ObjectType
|
||||
from extras.choices import *
|
||||
from extras.models import CustomField, CustomFieldChoiceSet
|
||||
from netbox.api.fields import ChoiceField, ContentTypeField
|
||||
from netbox.api.serializers import ValidatedModelSerializer
|
||||
from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
|
||||
|
||||
__all__ = (
|
||||
'CustomFieldChoiceSetSerializer',
|
||||
@@ -15,7 +15,7 @@ __all__ = (
|
||||
)
|
||||
|
||||
|
||||
class CustomFieldChoiceSetSerializer(ValidatedModelSerializer):
|
||||
class CustomFieldChoiceSetSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
|
||||
base_choices = ChoiceField(
|
||||
choices=CustomFieldChoiceSetBaseChoices,
|
||||
required=False
|
||||
@@ -36,7 +36,7 @@ class CustomFieldChoiceSetSerializer(ValidatedModelSerializer):
|
||||
brief_fields = ('id', 'url', 'display', 'name', 'description', 'choices_count')
|
||||
|
||||
|
||||
class CustomFieldSerializer(ValidatedModelSerializer):
|
||||
class CustomFieldSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
|
||||
object_types = ContentTypeField(
|
||||
queryset=ObjectType.objects.with_feature('custom_fields'),
|
||||
many=True
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
from core.models import ObjectType
|
||||
from extras.models import CustomLink
|
||||
from netbox.api.fields import ContentTypeField
|
||||
from netbox.api.serializers import ValidatedModelSerializer
|
||||
from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
|
||||
|
||||
__all__ = (
|
||||
'CustomLinkSerializer',
|
||||
)
|
||||
|
||||
|
||||
class CustomLinkSerializer(ValidatedModelSerializer):
|
||||
class CustomLinkSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
|
||||
object_types = ContentTypeField(
|
||||
queryset=ObjectType.objects.with_feature('custom_links'),
|
||||
many=True
|
||||
|
||||
@@ -2,14 +2,14 @@ from core.api.serializers_.data import DataFileSerializer, DataSourceSerializer
|
||||
from core.models import ObjectType
|
||||
from extras.models import ExportTemplate
|
||||
from netbox.api.fields import ContentTypeField
|
||||
from netbox.api.serializers import ValidatedModelSerializer
|
||||
from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
|
||||
|
||||
__all__ = (
|
||||
'ExportTemplateSerializer',
|
||||
)
|
||||
|
||||
|
||||
class ExportTemplateSerializer(ValidatedModelSerializer):
|
||||
class ExportTemplateSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
|
||||
object_types = ContentTypeField(
|
||||
queryset=ObjectType.objects.with_feature('export_templates'),
|
||||
many=True
|
||||
|
||||
@@ -4,7 +4,7 @@ from rest_framework import serializers
|
||||
from core.models import ObjectType
|
||||
from extras.models import Notification, NotificationGroup, Subscription
|
||||
from netbox.api.fields import ContentTypeField, SerializedPKRelatedField
|
||||
from netbox.api.serializers import ValidatedModelSerializer
|
||||
from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
|
||||
from users.api.serializers_.users import GroupSerializer, UserSerializer
|
||||
from users.models import Group, User
|
||||
from utilities.api import get_serializer_for_model
|
||||
@@ -37,7 +37,7 @@ class NotificationSerializer(ValidatedModelSerializer):
|
||||
return serializer(instance.object, nested=True, context=context).data
|
||||
|
||||
|
||||
class NotificationGroupSerializer(ValidatedModelSerializer):
|
||||
class NotificationGroupSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
|
||||
groups = SerializedPKRelatedField(
|
||||
queryset=Group.objects.all(),
|
||||
serializer=GroupSerializer,
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
from core.models import ObjectType
|
||||
from extras.models import SavedFilter
|
||||
from netbox.api.fields import ContentTypeField
|
||||
from netbox.api.serializers import ValidatedModelSerializer
|
||||
from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
|
||||
|
||||
__all__ = (
|
||||
'SavedFilterSerializer',
|
||||
)
|
||||
|
||||
|
||||
class SavedFilterSerializer(ValidatedModelSerializer):
|
||||
class SavedFilterSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
|
||||
object_types = ContentTypeField(
|
||||
queryset=ObjectType.objects.all(),
|
||||
many=True
|
||||
|
||||
@@ -5,7 +5,7 @@ from core.models import ObjectType
|
||||
from extras.models import Tag, TaggedItem
|
||||
from netbox.api.exceptions import SerializerNotFound
|
||||
from netbox.api.fields import ContentTypeField, RelatedObjectCountField
|
||||
from netbox.api.serializers import BaseModelSerializer, ValidatedModelSerializer
|
||||
from netbox.api.serializers import BaseModelSerializer, ChangeLogMessageSerializer, ValidatedModelSerializer
|
||||
from utilities.api import get_serializer_for_model
|
||||
|
||||
__all__ = (
|
||||
@@ -14,7 +14,7 @@ __all__ = (
|
||||
)
|
||||
|
||||
|
||||
class TagSerializer(ValidatedModelSerializer):
|
||||
class TagSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
|
||||
object_types = ContentTypeField(
|
||||
queryset=ObjectType.objects.with_feature('tags'),
|
||||
many=True,
|
||||
|
||||
@@ -5,6 +5,7 @@ from extras.choices import *
|
||||
from extras.models import *
|
||||
from netbox.events import get_event_type_choices
|
||||
from netbox.forms import NetBoxModelBulkEditForm
|
||||
from netbox.forms.mixins import ChangeLoggingMixin
|
||||
from utilities.forms import BulkEditForm, add_blank_choice
|
||||
from utilities.forms.fields import ColorField, CommentField, DynamicModelChoiceField
|
||||
from utilities.forms.rendering import FieldSet
|
||||
@@ -27,7 +28,7 @@ __all__ = (
|
||||
)
|
||||
|
||||
|
||||
class CustomFieldBulkEditForm(BulkEditForm):
|
||||
class CustomFieldBulkEditForm(ChangeLoggingMixin, BulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=CustomField.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
@@ -95,7 +96,7 @@ class CustomFieldBulkEditForm(BulkEditForm):
|
||||
nullable_fields = ('group_name', 'description', 'choice_set')
|
||||
|
||||
|
||||
class CustomFieldChoiceSetBulkEditForm(BulkEditForm):
|
||||
class CustomFieldChoiceSetBulkEditForm(ChangeLoggingMixin, BulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=CustomFieldChoiceSet.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
@@ -115,7 +116,7 @@ class CustomFieldChoiceSetBulkEditForm(BulkEditForm):
|
||||
nullable_fields = ('base_choices', 'description')
|
||||
|
||||
|
||||
class CustomLinkBulkEditForm(BulkEditForm):
|
||||
class CustomLinkBulkEditForm(ChangeLoggingMixin, BulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=CustomLink.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
@@ -141,7 +142,7 @@ class CustomLinkBulkEditForm(BulkEditForm):
|
||||
)
|
||||
|
||||
|
||||
class ExportTemplateBulkEditForm(BulkEditForm):
|
||||
class ExportTemplateBulkEditForm(ChangeLoggingMixin, BulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=ExportTemplate.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
@@ -174,7 +175,7 @@ class ExportTemplateBulkEditForm(BulkEditForm):
|
||||
nullable_fields = ('description', 'mime_type', 'file_name', 'file_extension')
|
||||
|
||||
|
||||
class SavedFilterBulkEditForm(BulkEditForm):
|
||||
class SavedFilterBulkEditForm(ChangeLoggingMixin, BulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=SavedFilter.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
@@ -294,7 +295,7 @@ class EventRuleBulkEditForm(NetBoxModelBulkEditForm):
|
||||
nullable_fields = ('description', 'conditions')
|
||||
|
||||
|
||||
class TagBulkEditForm(BulkEditForm):
|
||||
class TagBulkEditForm(ChangeLoggingMixin, BulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=Tag.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
@@ -316,7 +317,7 @@ class TagBulkEditForm(BulkEditForm):
|
||||
nullable_fields = ('description',)
|
||||
|
||||
|
||||
class ConfigContextBulkEditForm(BulkEditForm):
|
||||
class ConfigContextBulkEditForm(ChangeLoggingMixin, BulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=ConfigContext.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
@@ -340,7 +341,7 @@ class ConfigContextBulkEditForm(BulkEditForm):
|
||||
nullable_fields = ('description',)
|
||||
|
||||
|
||||
class ConfigTemplateBulkEditForm(BulkEditForm):
|
||||
class ConfigTemplateBulkEditForm(ChangeLoggingMixin, BulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=ConfigTemplate.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
@@ -373,7 +374,7 @@ class ConfigTemplateBulkEditForm(BulkEditForm):
|
||||
nullable_fields = ('description', 'mime_type', 'file_name', 'file_extension')
|
||||
|
||||
|
||||
class JournalEntryBulkEditForm(BulkEditForm):
|
||||
class JournalEntryBulkEditForm(ChangeLoggingMixin, BulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=JournalEntry.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
@@ -386,7 +387,7 @@ class JournalEntryBulkEditForm(BulkEditForm):
|
||||
comments = CommentField()
|
||||
|
||||
|
||||
class NotificationGroupBulkEditForm(BulkEditForm):
|
||||
class NotificationGroupBulkEditForm(ChangeLoggingMixin, BulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=NotificationGroup.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
|
||||
@@ -13,6 +13,7 @@ from extras.choices import *
|
||||
from extras.models import *
|
||||
from netbox.events import get_event_type_choices
|
||||
from netbox.forms import NetBoxModelForm
|
||||
from netbox.forms.mixins import ChangeLoggingMixin
|
||||
from tenancy.models import Tenant, TenantGroup
|
||||
from users.models import Group, User
|
||||
from utilities.forms import get_field_value
|
||||
@@ -45,7 +46,7 @@ __all__ = (
|
||||
)
|
||||
|
||||
|
||||
class CustomFieldForm(forms.ModelForm):
|
||||
class CustomFieldForm(ChangeLoggingMixin, forms.ModelForm):
|
||||
object_types = ContentTypeMultipleChoiceField(
|
||||
label=_('Object types'),
|
||||
queryset=ObjectType.objects.with_feature('custom_fields'),
|
||||
@@ -164,7 +165,7 @@ class CustomFieldForm(forms.ModelForm):
|
||||
del self.fields['choice_set']
|
||||
|
||||
|
||||
class CustomFieldChoiceSetForm(forms.ModelForm):
|
||||
class CustomFieldChoiceSetForm(ChangeLoggingMixin, forms.ModelForm):
|
||||
# TODO: The extra_choices field definition diverge from the CustomFieldChoiceSet model
|
||||
extra_choices = forms.CharField(
|
||||
widget=ChoicesWidget(),
|
||||
@@ -217,7 +218,7 @@ class CustomFieldChoiceSetForm(forms.ModelForm):
|
||||
return data
|
||||
|
||||
|
||||
class CustomLinkForm(forms.ModelForm):
|
||||
class CustomLinkForm(ChangeLoggingMixin, forms.ModelForm):
|
||||
object_types = ContentTypeMultipleChoiceField(
|
||||
label=_('Object types'),
|
||||
queryset=ObjectType.objects.with_feature('custom_links')
|
||||
@@ -249,7 +250,7 @@ class CustomLinkForm(forms.ModelForm):
|
||||
}
|
||||
|
||||
|
||||
class ExportTemplateForm(SyncedDataMixin, forms.ModelForm):
|
||||
class ExportTemplateForm(ChangeLoggingMixin, SyncedDataMixin, forms.ModelForm):
|
||||
object_types = ContentTypeMultipleChoiceField(
|
||||
label=_('Object types'),
|
||||
queryset=ObjectType.objects.with_feature('export_templates')
|
||||
@@ -291,7 +292,7 @@ class ExportTemplateForm(SyncedDataMixin, forms.ModelForm):
|
||||
return self.cleaned_data
|
||||
|
||||
|
||||
class SavedFilterForm(forms.ModelForm):
|
||||
class SavedFilterForm(ChangeLoggingMixin, forms.ModelForm):
|
||||
slug = SlugField()
|
||||
object_types = ContentTypeMultipleChoiceField(
|
||||
label=_('Object types'),
|
||||
@@ -388,7 +389,7 @@ class BookmarkForm(forms.ModelForm):
|
||||
fields = ('object_type', 'object_id')
|
||||
|
||||
|
||||
class NotificationGroupForm(forms.ModelForm):
|
||||
class NotificationGroupForm(ChangeLoggingMixin, forms.ModelForm):
|
||||
groups = DynamicModelMultipleChoiceField(
|
||||
label=_('Groups'),
|
||||
required=False,
|
||||
@@ -561,7 +562,7 @@ class EventRuleForm(NetBoxModelForm):
|
||||
return self.cleaned_data
|
||||
|
||||
|
||||
class TagForm(forms.ModelForm):
|
||||
class TagForm(ChangeLoggingMixin, forms.ModelForm):
|
||||
slug = SlugField()
|
||||
object_types = ContentTypeMultipleChoiceField(
|
||||
label=_('Object types'),
|
||||
@@ -584,7 +585,7 @@ class TagForm(forms.ModelForm):
|
||||
]
|
||||
|
||||
|
||||
class ConfigContextForm(SyncedDataMixin, forms.ModelForm):
|
||||
class ConfigContextForm(ChangeLoggingMixin, SyncedDataMixin, forms.ModelForm):
|
||||
regions = DynamicModelMultipleChoiceField(
|
||||
label=_('Regions'),
|
||||
queryset=Region.objects.all(),
|
||||
@@ -696,7 +697,7 @@ class ConfigContextForm(SyncedDataMixin, forms.ModelForm):
|
||||
return self.cleaned_data
|
||||
|
||||
|
||||
class ConfigTemplateForm(SyncedDataMixin, forms.ModelForm):
|
||||
class ConfigTemplateForm(ChangeLoggingMixin, SyncedDataMixin, forms.ModelForm):
|
||||
tags = DynamicModelMultipleChoiceField(
|
||||
label=_('Tags'),
|
||||
queryset=Tag.objects.all(),
|
||||
|
||||
Reference in New Issue
Block a user