18245 add group to device role

This commit is contained in:
Arthur 2025-03-21 09:37:19 -07:00
parent 20ab96ce87
commit 37d136386b
11 changed files with 132 additions and 9 deletions

View File

@ -12,6 +12,10 @@ A unique human-friendly name.
A unique URL-friendly identifier. (This value can be used for filtering.)
### Group
The [device role group](./devicerolegroup.md) to which this device role belongs (if any).
### Color
The color used when displaying the role in the NetBox UI.

View File

@ -28,6 +28,7 @@ class DeviceRoleGroupSerializer(NestedGroupModelSerializer):
class DeviceRoleSerializer(NetBoxModelSerializer):
config_template = ConfigTemplateSerializer(nested=True, required=False, allow_null=True, default=None)
group = DeviceRoleGroupSerializer(nested=True, required=False, allow_null=True, default=None)
# Related object counts
device_count = RelatedObjectCountField('devices')
@ -36,7 +37,7 @@ class DeviceRoleSerializer(NetBoxModelSerializer):
class Meta:
model = DeviceRole
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'vm_role', 'config_template',
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'group', 'vm_role', 'config_template',
'description', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'device_count', 'virtualmachine_count')

View File

@ -953,6 +953,19 @@ class DeviceRoleFilterSet(OrganizationalModelFilterSet):
queryset=ConfigTemplate.objects.all(),
label=_('Config template (ID)'),
)
group_id = TreeNodeMultipleChoiceFilter(
queryset=DeviceRoleGroup.objects.all(),
field_name='group',
lookup_expr='in',
label=_('Device role group (ID)'),
)
group = TreeNodeMultipleChoiceFilter(
queryset=DeviceRoleGroup.objects.all(),
field_name='group',
lookup_expr='in',
to_field_name='slug',
label=_('Device role group (slug)'),
)
class Meta:
model = DeviceRole

View File

@ -634,6 +634,11 @@ class DeviceRoleBulkEditForm(NetBoxModelBulkEditForm):
label=_('Color'),
required=False
)
group = DynamicModelChoiceField(
label=_('Group'),
queryset=DeviceRoleGroup.objects.all(),
required=False
)
vm_role = forms.NullBooleanField(
required=False,
widget=BulkEditNullBooleanSelect,
@ -652,9 +657,9 @@ class DeviceRoleBulkEditForm(NetBoxModelBulkEditForm):
model = DeviceRole
fieldsets = (
FieldSet('color', 'vm_role', 'config_template', 'description'),
FieldSet('color', 'group', 'vm_role', 'config_template', 'description'),
)
nullable_fields = ('color', 'config_template', 'description')
nullable_fields = ('color', 'group', 'config_template', 'description')
class PlatformBulkEditForm(NetBoxModelBulkEditForm):

View File

@ -483,11 +483,18 @@ class DeviceRoleImportForm(NetBoxModelImportForm):
required=False,
help_text=_('Config template')
)
group = CSVModelChoiceField(
label=_('Group'),
queryset=DeviceRoleGroup.objects.all(),
required=False,
to_field_name='name',
help_text=_('Assigned group')
)
slug = SlugField()
class Meta:
model = DeviceRole
fields = ('name', 'slug', 'color', 'vm_role', 'config_template', 'description', 'tags')
fields = ('name', 'slug', 'group', 'color', 'vm_role', 'config_template', 'description', 'tags')
class PlatformImportForm(NetBoxModelImportForm):

View File

@ -700,6 +700,12 @@ class DeviceRoleFilterForm(NetBoxModelFilterSetForm):
required=False,
label=_('Config template')
)
group_id = DynamicModelMultipleChoiceField(
queryset=DeviceRoleGroup.objects.all(),
required=False,
null_option='None',
label=_('Group')
)
tag = TagFilterField(model)

View File

@ -450,18 +450,23 @@ class DeviceRoleForm(NetBoxModelForm):
queryset=ConfigTemplate.objects.all(),
required=False
)
group = DynamicModelChoiceField(
label=_('Group'),
queryset=DeviceRoleGroup.objects.all(),
required=False
)
slug = SlugField()
fieldsets = (
FieldSet(
'name', 'slug', 'color', 'vm_role', 'config_template', 'description', 'tags', name=_('Device Role')
'name', 'slug', 'group', 'color', 'vm_role', 'config_template', 'description', 'tags', name=_('Device Role')
),
)
class Meta:
model = DeviceRole
fields = [
'name', 'slug', 'color', 'vm_role', 'config_template', 'description', 'tags',
'name', 'slug', 'group', 'color', 'vm_role', 'config_template', 'description', 'tags',
]

View File

@ -522,6 +522,10 @@ class DeviceRole(OrganizationalModel):
null=True
)
clone_fields = (
'group', 'description',
)
class Meta:
ordering = ('name',)
verbose_name = _('device role')

View File

@ -90,6 +90,10 @@ class DeviceRoleTable(NetBoxTable):
verbose_name=_('Name'),
linkify=True
)
group = tables.Column(
verbose_name=_('Group'),
linkify=True
)
device_count = columns.LinkedCountColumn(
viewname='dcim:device_list',
url_params={'role_id': 'pk'},
@ -115,10 +119,10 @@ class DeviceRoleTable(NetBoxTable):
class Meta(NetBoxTable.Meta):
model = models.DeviceRole
fields = (
'pk', 'id', 'name', 'device_count', 'vm_count', 'color', 'vm_role', 'config_template', 'description',
'slug', 'tags', 'actions', 'created', 'last_updated',
'pk', 'id', 'name', 'group', 'device_count', 'vm_count', 'color', 'vm_role',
'config_template', 'description', 'slug', 'tags', 'actions', 'created', 'last_updated',
)
default_columns = ('pk', 'name', 'device_count', 'vm_count', 'color', 'vm_role', 'description')
default_columns = ('pk', 'name', 'group', 'device_count', 'vm_count', 'color', 'vm_role', 'description')
#

View File

@ -26,6 +26,10 @@
<th scope="row">{% trans "Name" %}</th>
<td>{{ object.name }}</td>
</tr>
<tr>
<th scope="row">{% trans "Group" %}</th>
<td>{{ object.group|linkify|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Description" %}</th>
<td>{{ object.description|placeholder }}</td>

View File

@ -0,0 +1,70 @@
{% extends 'generic/object.html' %}
{% load helpers %}
{% load plugins %}
{% load render_table from django_tables2 %}
{% load i18n %}
{% block breadcrumbs %}
{{ block.super }}
{% for devicerolegroup in object.get_ancestors %}
<li class="breadcrumb-item"><a href="{% url 'dcim:devicerolegroup_list' %}?parent_id={{ devicerolegroup.pk }}">{{ devicerolegroup }}</a></li>
{% endfor %}
{% endblock %}
{% block extra_controls %}
{% if perms.tenancy.add_tenant %}
<a href="{% url 'dcim:devicerole_add' %}?group={{ object.pk }}" class="btn btn-primary">
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> {% trans "Add Device Role" %}
</a>
{% endif %}
{% endblock extra_controls %}
{% block content %}
<div class="row mb-3">
<div class="col col-md-6">
<div class="card">
<h2 class="card-header">{% trans "Device Role Group" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Name" %}</th>
<td>{{ object.name }}</td>
</tr>
<tr>
<th scope="row">{% trans "Description" %}</th>
<td>{{ object.description|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Parent" %}</th>
<td>{{ object.parent|linkify|placeholder }}</td>
</tr>
</table>
</div>
{% include 'inc/panels/tags.html' %}
{% include 'inc/panels/comments.html' %}
{% plugin_left_page object %}
</div>
<div class="col col-md-6">
{% include 'inc/panels/related_objects.html' %}
{% include 'inc/panels/custom_fields.html' %}
{% plugin_right_page object %}
</div>
</div>
<div class="row mb-3">
<div class="col col-md-12">
<div class="card">
<h2 class="card-header">
{% trans "Child Groups" %}
{% if perms.dcim.add_devicerolegroup %}
<div class="card-actions">
<a href="{% url 'dcim:devicerolegroup_add' %}?parent={{ object.pk }}" class="btn btn-ghost-primary btn-sm">
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> {% trans "Add Device Role Group" %}
</a>
</div>
{% endif %}
</h2>
{% htmx_table 'dcim:devicerolegroup_list' parent_id=object.pk %}
</div>
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}