18245 add forms, views, fitlersets, api

This commit is contained in:
Arthur 2025-03-20 18:03:58 -07:00
parent 3724c6c5b2
commit 7c8862cb1e
16 changed files with 243 additions and 3 deletions

View File

@ -7,6 +7,7 @@ from dcim import models
__all__ = (
'NestedDeviceBaySerializer',
'NestedDeviceSerializer',
'NestedDeviceRoleGroupSerializer',
'NestedInterfaceSerializer',
'NestedInterfaceTemplateSerializer',
'NestedLocationSerializer',
@ -52,6 +53,18 @@ class NestedLocationSerializer(WritableNestedSerializer):
fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'rack_count', '_depth']
@extend_schema_serializer(
exclude_fields=('tenant_count',),
)
class NestedDeviceRoleGroupSerializer(WritableNestedSerializer):
role_count = serializers.IntegerField(read_only=True)
_depth = serializers.IntegerField(source='level', read_only=True)
class Meta:
model = models.DeviceRoleGroup
fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'role_count', '_depth']
class NestedDeviceSerializer(WritableNestedSerializer):
class Meta:

View File

@ -1,14 +1,31 @@
from dcim.models import DeviceRole, InventoryItemRole
from rest_framework import serializers
from dcim.models import DeviceRole, DeviceRoleGroup, InventoryItemRole
from extras.api.serializers_.configtemplates import ConfigTemplateSerializer
from netbox.api.fields import RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer
from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
from .nested import NestedDeviceRoleGroupSerializer
__all__ = (
'DeviceRoleSerializer',
'DeviceRoleGroupSerializer',
'InventoryItemRoleSerializer',
)
class DeviceRoleGroupSerializer(NestedGroupModelSerializer):
parent = NestedDeviceRoleGroupSerializer(required=False, allow_null=True)
tenant_count = serializers.IntegerField(read_only=True, default=0)
class Meta:
model = DeviceRoleGroup
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields',
'created', 'last_updated', 'role_count', 'comments', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'role_count', '_depth')
class DeviceRoleSerializer(NetBoxModelSerializer):
config_template = ConfigTemplateSerializer(nested=True, required=False, allow_null=True, default=None)

View File

@ -36,6 +36,7 @@ router.register('inventory-item-templates', views.InventoryItemTemplateViewSet)
# Device/modules
router.register('device-roles', views.DeviceRoleViewSet)
router.register('device-role-groups', views.DeviceRoleGroupViewSet)
router.register('platforms', views.PlatformViewSet)
router.register('devices', views.DeviceViewSet)
router.register('virtual-device-contexts', views.VirtualDeviceContextViewSet)

View File

@ -343,6 +343,18 @@ class InventoryItemTemplateViewSet(MPTTLockedMixin, NetBoxModelViewSet):
# Device roles
#
class DeviceRoleGroupViewSet(MPTTLockedMixin, NetBoxModelViewSet):
queryset = DeviceRoleGroup.objects.add_related_count(
DeviceRoleGroup.objects.all(),
DeviceRole,
'group',
'role_count',
cumulative=True
)
serializer_class = serializers.DeviceRoleGroupSerializer
filterset_class = filtersets.DeviceRoleGroupFilterSet
class DeviceRoleViewSet(NetBoxModelViewSet):
queryset = DeviceRole.objects.all()
serializer_class = serializers.DeviceRoleSerializer

View File

@ -43,6 +43,7 @@ __all__ = (
'DeviceBayTemplateFilterSet',
'DeviceFilterSet',
'DeviceRoleFilterSet',
'DeviceRoleGroupFilterSet',
'DeviceTypeFilterSet',
'FrontPortFilterSet',
'FrontPortTemplateFilterSet',
@ -917,6 +918,36 @@ class InventoryItemTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeCompo
return queryset.filter(qs_filter)
class DeviceRoleGroupFilterSet(NestedGroupModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=DeviceRoleGroup.objects.all(),
label=_('Parent device role group (ID)'),
)
parent = django_filters.ModelMultipleChoiceFilter(
field_name='parent__slug',
queryset=DeviceRoleGroup.objects.all(),
to_field_name='slug',
label=_('Parent device role group (slug)'),
)
ancestor_id = TreeNodeMultipleChoiceFilter(
queryset=DeviceRoleGroup.objects.all(),
field_name='parent',
lookup_expr='in',
label=_('Device role group (ID)'),
)
ancestor = TreeNodeMultipleChoiceFilter(
queryset=DeviceRoleGroup.objects.all(),
field_name='parent',
lookup_expr='in',
to_field_name='slug',
label=_('Device role group (slug)'),
)
class Meta:
model = DeviceRoleGroup
fields = ('id', 'name', 'slug', 'description')
class DeviceRoleFilterSet(OrganizationalModelFilterSet):
config_template_id = django_filters.ModelMultipleChoiceFilter(
queryset=ConfigTemplate.objects.all(),

View File

@ -31,6 +31,7 @@ __all__ = (
'DeviceBayTemplateBulkEditForm',
'DeviceBulkEditForm',
'DeviceRoleBulkEditForm',
'DeviceRoleGroupBulkEditForm',
'DeviceTypeBulkEditForm',
'FrontPortBulkEditForm',
'FrontPortTemplateBulkEditForm',
@ -611,6 +612,23 @@ class ModuleTypeBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('part_number', 'weight', 'weight_unit', 'description', 'comments')
class DeviceRoleGroupBulkEditForm(NetBoxModelBulkEditForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=DeviceRoleGroup.objects.all(),
required=False
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = DeviceRoleGroup
nullable_fields = ('parent', 'description', 'comments')
class DeviceRoleBulkEditForm(NetBoxModelBulkEditForm):
color = ColorField(
label=_('Color'),

View File

@ -28,6 +28,7 @@ __all__ = (
'DeviceBayImportForm',
'DeviceImportForm',
'DeviceRoleImportForm',
'DeviceRoleGroupImportForm',
'DeviceTypeImportForm',
'FrontPortImportForm',
'InterfaceImportForm',
@ -459,6 +460,21 @@ class ModuleTypeImportForm(NetBoxModelImportForm):
]
class DeviceRoleGroupImportForm(NetBoxModelImportForm):
parent = CSVModelChoiceField(
label=_('Parent'),
queryset=DeviceRoleGroup.objects.all(),
required=False,
to_field_name='name',
help_text=_('Parent group')
)
slug = SlugField()
class Meta:
model = DeviceRoleGroup
fields = ('name', 'slug', 'parent', 'description', 'tags', 'comments')
class DeviceRoleImportForm(NetBoxModelImportForm):
config_template = CSVModelChoiceField(
label=_('Config template'),

View File

@ -27,6 +27,7 @@ __all__ = (
'DeviceBayFilterForm',
'DeviceFilterForm',
'DeviceRoleFilterForm',
'DeviceRoleGroupFilterForm',
'DeviceTypeFilterForm',
'FrontPortFilterForm',
'InterfaceConnectionFilterForm',
@ -682,6 +683,16 @@ class ModuleTypeFilterForm(NetBoxModelFilterSetForm):
)
class DeviceRoleGroupFilterForm(NetBoxModelFilterSetForm):
model = DeviceRoleGroup
parent_id = DynamicModelMultipleChoiceField(
queryset=DeviceRoleGroup.objects.all(),
required=False,
label=_('Parent group')
)
tag = TagFilterField(model)
class DeviceRoleFilterForm(NetBoxModelFilterSetForm):
model = DeviceRole
config_template_id = DynamicModelMultipleChoiceField(

View File

@ -32,6 +32,7 @@ __all__ = (
'DeviceBayTemplateForm',
'DeviceForm',
'DeviceRoleForm',
'DeviceRoleGroupForm',
'DeviceTypeForm',
'DeviceVCMembershipForm',
'FrontPortForm',
@ -423,6 +424,26 @@ class ModuleTypeForm(NetBoxModelForm):
]
class DeviceRoleGroupForm(NetBoxModelForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=DeviceRoleGroup.objects.all(),
required=False
)
slug = SlugField()
comments = CommentField()
fieldsets = (
FieldSet('parent', 'name', 'slug', 'description', 'tags', name=_('Device Role Group')),
)
class Meta:
model = DeviceRoleGroup
fields = [
'parent', 'name', 'slug', 'description', 'tags', 'comments'
]
class DeviceRoleForm(NetBoxModelForm):
config_template = DynamicModelChoiceField(
label=_('Config template'),

View File

@ -36,6 +36,7 @@ from .mixins import RenderConfigMixin
__all__ = (
'Device',
'DeviceRole',
'DeviceRoleGroup',
'DeviceType',
'MACAddress',
'Manufacturer',
@ -471,7 +472,7 @@ class ModuleType(ImageAttachmentsMixin, PrimaryModel, WeightMixin):
class DeviceRoleGroup(NestedGroupModel):
"""
An arbitrary collection of Tenants.
An arbitrary collection of DeviceRoles.
"""
name = models.CharField(
verbose_name=_('name'),

View File

@ -75,6 +75,18 @@ class DeviceRoleIndex(SearchIndex):
display_attrs = ('description',)
@register_search
class DeviceRoleGroupIndex(SearchIndex):
model = models.DeviceRoleGroup
fields = (
('name', 100),
('slug', 110),
('description', 500),
('comments', 5000),
)
display_attrs = ('description',)
@register_search
class DeviceTypeIndex(SearchIndex):
model = models.DeviceType

View File

@ -24,6 +24,7 @@ __all__ = (
'DevicePowerOutletTable',
'DeviceRearPortTable',
'DeviceRoleTable',
'DeviceRoleGroupTable',
'DeviceTable',
'FrontPortTable',
'InterfaceTable',
@ -58,6 +59,32 @@ MACADDRESS_COPY_BUTTON = """
# Device roles
#
class DeviceRoleGroupTable(NetBoxTable):
name = columns.MPTTColumn(
verbose_name=_('Name'),
linkify=True
)
role_count = columns.LinkedCountColumn(
viewname='dcim:devicerole_list',
url_params={'group_id': 'pk'},
verbose_name=_('Device Roles')
)
tags = columns.TagColumn(
url_name='dcim:devicerolegroup_list'
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
)
class Meta(NetBoxTable.Meta):
model = models.DeviceRoleGroup
fields = (
'pk', 'id', 'name', 'role_count', 'description', 'comments', 'slug', 'tags', 'created',
'last_updated', 'actions',
)
default_columns = ('pk', 'name', 'role_count', 'description')
class DeviceRoleTable(NetBoxTable):
name = tables.Column(
verbose_name=_('Name'),

View File

@ -73,6 +73,9 @@ urlpatterns = [
path('device-roles/', include(get_model_urls('dcim', 'devicerole', detail=False))),
path('device-roles/<int:pk>/', include(get_model_urls('dcim', 'devicerole'))),
path('device-role-groups/', include(get_model_urls('dcim', 'devicerolegroup', detail=False))),
path('device-role-groups/<int:pk>/', include(get_model_urls('dcim', 'devicerolegroup'))),
path('platforms/', include(get_model_urls('dcim', 'platform', detail=False))),
path('platforms/<int:pk>/', include(get_model_urls('dcim', 'platform'))),

View File

@ -1897,6 +1897,61 @@ class InventoryItemTemplateBulkDeleteView(generic.BulkDeleteView):
table = tables.InventoryItemTemplateTable
#
# Device role groups
#
@register_model_view(DeviceRoleGroup, 'list', path='', detail=False)
class DeviceRoleGroupListView(generic.ObjectListView):
queryset = DeviceRoleGroup.objects.all()
filterset = filtersets.DeviceRoleGroupFilterSet
filterset_form = forms.DeviceRoleGroupFilterForm
table = tables.DeviceRoleGroupTable
@register_model_view(DeviceRoleGroup)
class DeviceRoleGroupView(GetRelatedModelsMixin, generic.ObjectView):
queryset = DeviceRoleGroup.objects.all()
def get_extra_context(self, request, instance):
return {
'related_models': self.get_related_models(request, instance),
}
@register_model_view(DeviceRoleGroup, 'add', detail=False)
@register_model_view(DeviceRoleGroup, 'edit')
class DeviceRoleGroupEditView(generic.ObjectEditView):
queryset = DeviceRoleGroup.objects.all()
form = forms.DeviceRoleGroupForm
@register_model_view(DeviceRoleGroup, 'delete')
class DeviceRoleGroupDeleteView(generic.ObjectDeleteView):
queryset = DeviceRoleGroup.objects.all()
@register_model_view(DeviceRoleGroup, 'bulk_import', detail=False)
class DeviceRoleGroupBulkImportView(generic.BulkImportView):
queryset = DeviceRoleGroup.objects.all()
model_form = forms.DeviceRoleGroupImportForm
@register_model_view(DeviceRoleGroup, 'bulk_edit', path='edit', detail=False)
class DeviceRoleGroupBulkEditView(generic.BulkEditView):
queryset = DeviceRoleGroup.objects.all()
filterset = filtersets.DeviceRoleGroupFilterSet
table = tables.DeviceRoleGroupTable
form = forms.DeviceRoleGroupBulkEditForm
@register_model_view(DeviceRoleGroup, 'bulk_delete', path='delete', detail=False)
class DeviceRoleGroupBulkDeleteView(generic.BulkDeleteView):
queryset = DeviceRoleGroup.objects.all()
filterset = filtersets.DeviceRoleGroupFilterSet
table = tables.DeviceRoleGroupTable
#
# Device roles
#

View File

@ -23,6 +23,7 @@ ADVISORY_LOCK_KEYS = {
'wirelesslangroup': 105600,
'inventoryitem': 105700,
'inventoryitemtemplate': 105800,
'devicerolegroup': 105900,
# Jobs
'job-schedules': 110100,

View File

@ -75,6 +75,7 @@ DEVICES_MENU = Menu(
get_model_item('dcim', 'device', _('Devices')),
get_model_item('dcim', 'module', _('Modules')),
get_model_item('dcim', 'devicerole', _('Device Roles')),
get_model_item('dcim', 'devicerolegroup', _('Device Role Groups')),
get_model_item('dcim', 'platform', _('Platforms')),
get_model_item('dcim', 'virtualchassis', _('Virtual Chassis')),
get_model_item('dcim', 'virtualdevicecontext', _('Virtual Device Contexts')),