mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 01:41:22 -06:00
Made DeviceRoles hierarchical, had to also change the filtersets for Device, ConfigContext and VirtualMachine to use the TreeNodeMultipleChoiceFilter. Note: The model was changed to use NestedGroupModel, a side-effect of this is it also adds comments field, but I thought that was better then doing a one-off just for DeviceRole and having to define the fields, validators, etc.. - keeps everything DRY / consistent. * 18981 Make Device Roles Hierarchical * 18981 forms, serializer * 18981 fix tests * 18981 fix tests * 18981 fix tests * 18981 fix tests * 18981 fix tests * 18981 fix migration merge * 18981 fix tests * 18981 fix filtersets * 18981 fix tests * 18981 comments * 18981 review changes
This commit is contained in:
parent
7a71c7b8f8
commit
1508e3a770
@ -4,6 +4,10 @@ Devices can be organized by functional roles, which are fully customizable by th
|
|||||||
|
|
||||||
## Fields
|
## Fields
|
||||||
|
|
||||||
|
### Parent
|
||||||
|
|
||||||
|
The parent role of which this role is a child (optional).
|
||||||
|
|
||||||
### Name
|
### Name
|
||||||
|
|
||||||
A unique human-friendly name.
|
A unique human-friendly name.
|
||||||
|
@ -52,6 +52,13 @@ class NestedLocationSerializer(WritableNestedSerializer):
|
|||||||
fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'rack_count', '_depth']
|
fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'rack_count', '_depth']
|
||||||
|
|
||||||
|
|
||||||
|
class NestedDeviceRoleSerializer(WritableNestedSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.DeviceRole
|
||||||
|
fields = ['id', 'url', 'display_url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedDeviceSerializer(WritableNestedSerializer):
|
class NestedDeviceSerializer(WritableNestedSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
from dcim.models import DeviceRole, InventoryItemRole
|
from dcim.models import DeviceRole, InventoryItemRole
|
||||||
from extras.api.serializers_.configtemplates import ConfigTemplateSerializer
|
from extras.api.serializers_.configtemplates import ConfigTemplateSerializer
|
||||||
from netbox.api.fields import RelatedObjectCountField
|
from netbox.api.fields import RelatedObjectCountField
|
||||||
from netbox.api.serializers import NetBoxModelSerializer
|
from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
|
||||||
|
from .nested import NestedDeviceRoleSerializer
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'DeviceRoleSerializer',
|
'DeviceRoleSerializer',
|
||||||
@ -9,7 +10,8 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DeviceRoleSerializer(NetBoxModelSerializer):
|
class DeviceRoleSerializer(NestedGroupModelSerializer):
|
||||||
|
parent = NestedDeviceRoleSerializer(required=False, allow_null=True, default=None)
|
||||||
config_template = ConfigTemplateSerializer(nested=True, required=False, allow_null=True, default=None)
|
config_template = ConfigTemplateSerializer(nested=True, required=False, allow_null=True, default=None)
|
||||||
|
|
||||||
# Related object counts
|
# Related object counts
|
||||||
@ -19,10 +21,13 @@ class DeviceRoleSerializer(NetBoxModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = DeviceRole
|
model = DeviceRole
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'vm_role', 'config_template',
|
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'vm_role', 'config_template', 'parent',
|
||||||
'description', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
|
'description', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
|
||||||
|
'comments', '_depth',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'device_count', 'virtualmachine_count')
|
brief_fields = (
|
||||||
|
'id', 'url', 'display', 'name', 'slug', 'description', 'device_count', 'virtualmachine_count', '_depth'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemRoleSerializer(NetBoxModelSerializer):
|
class InventoryItemRoleSerializer(NetBoxModelSerializer):
|
||||||
|
@ -922,6 +922,29 @@ class DeviceRoleFilterSet(OrganizationalModelFilterSet):
|
|||||||
queryset=ConfigTemplate.objects.all(),
|
queryset=ConfigTemplate.objects.all(),
|
||||||
label=_('Config template (ID)'),
|
label=_('Config template (ID)'),
|
||||||
)
|
)
|
||||||
|
parent_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
queryset=DeviceRole.objects.all(),
|
||||||
|
label=_('Parent device role (ID)'),
|
||||||
|
)
|
||||||
|
parent = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
field_name='parent__slug',
|
||||||
|
queryset=DeviceRole.objects.all(),
|
||||||
|
to_field_name='slug',
|
||||||
|
label=_('Parent device role (slug)'),
|
||||||
|
)
|
||||||
|
ancestor_id = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=DeviceRole.objects.all(),
|
||||||
|
field_name='parent',
|
||||||
|
lookup_expr='in',
|
||||||
|
label=_('Parent device role (ID)'),
|
||||||
|
)
|
||||||
|
ancestor = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=DeviceRole.objects.all(),
|
||||||
|
field_name='parent',
|
||||||
|
lookup_expr='in',
|
||||||
|
to_field_name='slug',
|
||||||
|
label=_('Parent device role (slug)'),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = DeviceRole
|
model = DeviceRole
|
||||||
@ -990,14 +1013,16 @@ class DeviceFilterSet(
|
|||||||
queryset=DeviceType.objects.all(),
|
queryset=DeviceType.objects.all(),
|
||||||
label=_('Device type (ID)'),
|
label=_('Device type (ID)'),
|
||||||
)
|
)
|
||||||
role_id = django_filters.ModelMultipleChoiceFilter(
|
role_id = TreeNodeMultipleChoiceFilter(
|
||||||
field_name='role_id',
|
field_name='role',
|
||||||
queryset=DeviceRole.objects.all(),
|
queryset=DeviceRole.objects.all(),
|
||||||
|
lookup_expr='in',
|
||||||
label=_('Role (ID)'),
|
label=_('Role (ID)'),
|
||||||
)
|
)
|
||||||
role = django_filters.ModelMultipleChoiceFilter(
|
role = TreeNodeMultipleChoiceFilter(
|
||||||
field_name='role__slug',
|
|
||||||
queryset=DeviceRole.objects.all(),
|
queryset=DeviceRole.objects.all(),
|
||||||
|
field_name='role',
|
||||||
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label=_('Role (slug)'),
|
label=_('Role (slug)'),
|
||||||
)
|
)
|
||||||
|
@ -620,6 +620,11 @@ class ModuleTypeBulkEditForm(NetBoxModelBulkEditForm):
|
|||||||
|
|
||||||
|
|
||||||
class DeviceRoleBulkEditForm(NetBoxModelBulkEditForm):
|
class DeviceRoleBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
|
parent = DynamicModelChoiceField(
|
||||||
|
label=_('Parent'),
|
||||||
|
queryset=DeviceRole.objects.all(),
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
color = ColorField(
|
color = ColorField(
|
||||||
label=_('Color'),
|
label=_('Color'),
|
||||||
required=False
|
required=False
|
||||||
@ -639,12 +644,13 @@ class DeviceRoleBulkEditForm(NetBoxModelBulkEditForm):
|
|||||||
max_length=200,
|
max_length=200,
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
comments = CommentField()
|
||||||
|
|
||||||
model = DeviceRole
|
model = DeviceRole
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
FieldSet('color', 'vm_role', 'config_template', 'description'),
|
FieldSet('parent', 'color', 'vm_role', 'config_template', 'description'),
|
||||||
)
|
)
|
||||||
nullable_fields = ('color', 'config_template', 'description')
|
nullable_fields = ('parent', 'color', 'config_template', 'description', 'comments')
|
||||||
|
|
||||||
|
|
||||||
class PlatformBulkEditForm(NetBoxModelBulkEditForm):
|
class PlatformBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
|
@ -460,6 +460,16 @@ class ModuleTypeImportForm(NetBoxModelImportForm):
|
|||||||
|
|
||||||
|
|
||||||
class DeviceRoleImportForm(NetBoxModelImportForm):
|
class DeviceRoleImportForm(NetBoxModelImportForm):
|
||||||
|
parent = CSVModelChoiceField(
|
||||||
|
label=_('Parent'),
|
||||||
|
queryset=DeviceRole.objects.all(),
|
||||||
|
required=False,
|
||||||
|
to_field_name='name',
|
||||||
|
help_text=_('Parent Device Role'),
|
||||||
|
error_messages={
|
||||||
|
'invalid_choice': _('Device role not found.'),
|
||||||
|
}
|
||||||
|
)
|
||||||
config_template = CSVModelChoiceField(
|
config_template = CSVModelChoiceField(
|
||||||
label=_('Config template'),
|
label=_('Config template'),
|
||||||
queryset=ConfigTemplate.objects.all(),
|
queryset=ConfigTemplate.objects.all(),
|
||||||
@ -471,7 +481,9 @@ class DeviceRoleImportForm(NetBoxModelImportForm):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = DeviceRole
|
model = DeviceRole
|
||||||
fields = ('name', 'slug', 'color', 'vm_role', 'config_template', 'description', 'tags')
|
fields = (
|
||||||
|
'name', 'slug', 'parent', 'color', 'vm_role', 'config_template', 'description', 'comments', 'tags'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PlatformImportForm(NetBoxModelImportForm):
|
class PlatformImportForm(NetBoxModelImportForm):
|
||||||
|
@ -689,6 +689,11 @@ class DeviceRoleFilterForm(NetBoxModelFilterSetForm):
|
|||||||
required=False,
|
required=False,
|
||||||
label=_('Config template')
|
label=_('Config template')
|
||||||
)
|
)
|
||||||
|
parent_id = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=DeviceRole.objects.all(),
|
||||||
|
required=False,
|
||||||
|
label=_('Parent')
|
||||||
|
)
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
|
@ -431,17 +431,24 @@ class DeviceRoleForm(NetBoxModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
|
parent = DynamicModelChoiceField(
|
||||||
|
label=_('Parent'),
|
||||||
|
queryset=DeviceRole.objects.all(),
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
comments = CommentField()
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
FieldSet(
|
FieldSet(
|
||||||
'name', 'slug', 'color', 'vm_role', 'config_template', 'description', 'tags', name=_('Device Role')
|
'name', 'slug', 'parent', 'color', 'vm_role', 'config_template', 'description',
|
||||||
|
'tags', name=_('Device Role')
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = DeviceRole
|
model = DeviceRole
|
||||||
fields = [
|
fields = [
|
||||||
'name', 'slug', 'color', 'vm_role', 'config_template', 'description', 'tags',
|
'name', 'slug', 'parent', 'color', 'vm_role', 'config_template', 'description', 'comments', 'tags',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -338,6 +338,8 @@ class InventoryItemTemplateType(ComponentTemplateType):
|
|||||||
pagination=True
|
pagination=True
|
||||||
)
|
)
|
||||||
class DeviceRoleType(OrganizationalObjectType):
|
class DeviceRoleType(OrganizationalObjectType):
|
||||||
|
parent: Annotated['DeviceRoleType', strawberry.lazy('dcim.graphql.types')] | None
|
||||||
|
children: List[Annotated['DeviceRoleType', strawberry.lazy('dcim.graphql.types')]]
|
||||||
color: str
|
color: str
|
||||||
config_template: Annotated["ConfigTemplateType", strawberry.lazy('extras.graphql.types')] | None
|
config_template: Annotated["ConfigTemplateType", strawberry.lazy('extras.graphql.types')] | None
|
||||||
|
|
||||||
|
65
netbox/dcim/migrations/0203_device_role_nested.py
Normal file
65
netbox/dcim/migrations/0203_device_role_nested.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# Generated by Django 5.1.7 on 2025-03-25 18:06
|
||||||
|
|
||||||
|
import django.db.models.manager
|
||||||
|
import mptt.fields
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dcim', '0203_add_rack_outer_height'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='devicerole',
|
||||||
|
name='level',
|
||||||
|
field=models.PositiveIntegerField(default=0, editable=False),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='devicerole',
|
||||||
|
name='lft',
|
||||||
|
field=models.PositiveIntegerField(default=0, editable=False),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='devicerole',
|
||||||
|
name='rght',
|
||||||
|
field=models.PositiveIntegerField(default=0, editable=False),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='devicerole',
|
||||||
|
name='tree_id',
|
||||||
|
field=models.PositiveIntegerField(db_index=True, default=0, editable=False),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='devicerole',
|
||||||
|
name='parent',
|
||||||
|
field=mptt.fields.TreeForeignKey(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name='children',
|
||||||
|
to='dcim.devicerole',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='devicerole',
|
||||||
|
name='comments',
|
||||||
|
field=models.TextField(blank=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='devicerole',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=100),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='devicerole',
|
||||||
|
name='slug',
|
||||||
|
field=models.SlugField(max_length=100),
|
||||||
|
),
|
||||||
|
]
|
22
netbox/dcim/migrations/0204_device_role_rebuild.py
Normal file
22
netbox/dcim/migrations/0204_device_role_rebuild.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
from django.db import migrations
|
||||||
|
import mptt
|
||||||
|
import mptt.managers
|
||||||
|
|
||||||
|
|
||||||
|
def rebuild_mptt(apps, schema_editor):
|
||||||
|
manager = mptt.managers.TreeManager()
|
||||||
|
DeviceRole = apps.get_model('dcim', 'DeviceRole')
|
||||||
|
manager.model = DeviceRole
|
||||||
|
mptt.register(DeviceRole)
|
||||||
|
manager.contribute_to_class(DeviceRole, 'objects')
|
||||||
|
manager.rebuild()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
('dcim', '0203_device_role_nested'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(code=rebuild_mptt, reverse_code=migrations.RunPython.noop),
|
||||||
|
]
|
@ -23,7 +23,7 @@ from extras.models import ConfigContextModel, CustomField
|
|||||||
from extras.querysets import ConfigContextModelQuerySet
|
from extras.querysets import ConfigContextModelQuerySet
|
||||||
from netbox.choices import ColorChoices
|
from netbox.choices import ColorChoices
|
||||||
from netbox.config import ConfigItem
|
from netbox.config import ConfigItem
|
||||||
from netbox.models import OrganizationalModel, PrimaryModel
|
from netbox.models import NestedGroupModel, OrganizationalModel, PrimaryModel
|
||||||
from netbox.models.mixins import WeightMixin
|
from netbox.models.mixins import WeightMixin
|
||||||
from netbox.models.features import ContactsMixin, ImageAttachmentsMixin
|
from netbox.models.features import ContactsMixin, ImageAttachmentsMixin
|
||||||
from utilities.fields import ColorField, CounterCacheField
|
from utilities.fields import ColorField, CounterCacheField
|
||||||
@ -468,7 +468,7 @@ class ModuleType(ImageAttachmentsMixin, PrimaryModel, WeightMixin):
|
|||||||
# Devices
|
# Devices
|
||||||
#
|
#
|
||||||
|
|
||||||
class DeviceRole(OrganizationalModel):
|
class DeviceRole(NestedGroupModel):
|
||||||
"""
|
"""
|
||||||
Devices are organized by functional role; for example, "Core Switch" or "File Server". Each DeviceRole is assigned a
|
Devices are organized by functional role; for example, "Core Switch" or "File Server". Each DeviceRole is assigned a
|
||||||
color to be used when displaying rack elevations. The vm_role field determines whether the role is applicable to
|
color to be used when displaying rack elevations. The vm_role field determines whether the role is applicable to
|
||||||
@ -491,6 +491,8 @@ class DeviceRole(OrganizationalModel):
|
|||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
clone_fields = ('parent', 'description')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('name',)
|
ordering = ('name',)
|
||||||
verbose_name = _('device role')
|
verbose_name = _('device role')
|
||||||
|
@ -59,7 +59,7 @@ MACADDRESS_COPY_BUTTON = """
|
|||||||
#
|
#
|
||||||
|
|
||||||
class DeviceRoleTable(NetBoxTable):
|
class DeviceRoleTable(NetBoxTable):
|
||||||
name = tables.Column(
|
name = columns.MPTTColumn(
|
||||||
verbose_name=_('Name'),
|
verbose_name=_('Name'),
|
||||||
linkify=True
|
linkify=True
|
||||||
)
|
)
|
||||||
|
@ -1149,7 +1149,9 @@ class InventoryItemTemplateTest(APIViewTestCases.APIViewTestCase):
|
|||||||
|
|
||||||
class DeviceRoleTest(APIViewTestCases.APIViewTestCase):
|
class DeviceRoleTest(APIViewTestCases.APIViewTestCase):
|
||||||
model = DeviceRole
|
model = DeviceRole
|
||||||
brief_fields = ['description', 'device_count', 'display', 'id', 'name', 'slug', 'url', 'virtualmachine_count']
|
brief_fields = [
|
||||||
|
'_depth', 'description', 'device_count', 'display', 'id', 'name', 'slug', 'url', 'virtualmachine_count'
|
||||||
|
]
|
||||||
create_data = [
|
create_data = [
|
||||||
{
|
{
|
||||||
'name': 'Device Role 4',
|
'name': 'Device Role 4',
|
||||||
@ -1174,12 +1176,9 @@ class DeviceRoleTest(APIViewTestCases.APIViewTestCase):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
roles = (
|
DeviceRole.objects.create(name='Device Role 1', slug='device-role-1', color='ff0000')
|
||||||
DeviceRole(name='Device Role 1', slug='device-role-1', color='ff0000'),
|
DeviceRole.objects.create(name='Device Role 2', slug='device-role-2', color='00ff00')
|
||||||
DeviceRole(name='Device Role 2', slug='device-role-2', color='00ff00'),
|
DeviceRole.objects.create(name='Device Role 3', slug='device-role-3', color='0000ff')
|
||||||
DeviceRole(name='Device Role 3', slug='device-role-3', color='0000ff'),
|
|
||||||
)
|
|
||||||
DeviceRole.objects.bulk_create(roles)
|
|
||||||
|
|
||||||
|
|
||||||
class PlatformTest(APIViewTestCases.APIViewTestCase):
|
class PlatformTest(APIViewTestCases.APIViewTestCase):
|
||||||
@ -1252,7 +1251,8 @@ class DeviceTest(APIViewTestCases.APIViewTestCase):
|
|||||||
DeviceRole(name='Device Role 1', slug='device-role-1', color='ff0000'),
|
DeviceRole(name='Device Role 1', slug='device-role-1', color='ff0000'),
|
||||||
DeviceRole(name='Device Role 2', slug='device-role-2', color='00ff00'),
|
DeviceRole(name='Device Role 2', slug='device-role-2', color='00ff00'),
|
||||||
)
|
)
|
||||||
DeviceRole.objects.bulk_create(roles)
|
for role in roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
cluster_type = ClusterType.objects.create(name='Cluster Type 1', slug='cluster-type-1')
|
cluster_type = ClusterType.objects.create(name='Cluster Type 1', slug='cluster-type-1')
|
||||||
|
|
||||||
|
@ -2191,12 +2191,65 @@ class DeviceRoleTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
roles = (
|
parent_roles = (
|
||||||
DeviceRole(name='Device Role 1', slug='device-role-1', color='ff0000', vm_role=True, description='foobar1'),
|
DeviceRole(name='Device Role 1', slug='device-role-1', color='ff0000', vm_role=True, description='foobar1'),
|
||||||
DeviceRole(name='Device Role 2', slug='device-role-2', color='00ff00', vm_role=True, description='foobar2'),
|
DeviceRole(name='Device Role 2', slug='device-role-2', color='00ff00', vm_role=True, description='foobar2'),
|
||||||
DeviceRole(name='Device Role 3', slug='device-role-3', color='0000ff', vm_role=False),
|
DeviceRole(name='Device Role 3', slug='device-role-3', color='0000ff', vm_role=False)
|
||||||
)
|
)
|
||||||
DeviceRole.objects.bulk_create(roles)
|
for role in parent_roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
|
roles = (
|
||||||
|
DeviceRole(
|
||||||
|
name='Device Role 1A',
|
||||||
|
slug='device-role-1a',
|
||||||
|
color='aa0000',
|
||||||
|
vm_role=True,
|
||||||
|
parent=parent_roles[0]
|
||||||
|
),
|
||||||
|
DeviceRole(
|
||||||
|
name='Device Role 2A',
|
||||||
|
slug='device-role-2a',
|
||||||
|
color='00aa00',
|
||||||
|
vm_role=True,
|
||||||
|
parent=parent_roles[1]
|
||||||
|
),
|
||||||
|
DeviceRole(
|
||||||
|
name='Device Role 3A',
|
||||||
|
slug='device-role-3a',
|
||||||
|
color='0000aa',
|
||||||
|
vm_role=False,
|
||||||
|
parent=parent_roles[2]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for role in roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
|
child_roles = (
|
||||||
|
DeviceRole(
|
||||||
|
name='Device Role 1A1',
|
||||||
|
slug='device-role-1a1',
|
||||||
|
color='bb0000',
|
||||||
|
vm_role=True,
|
||||||
|
parent=roles[0]
|
||||||
|
),
|
||||||
|
DeviceRole(
|
||||||
|
name='Device Role 2A1',
|
||||||
|
slug='device-role-2a1',
|
||||||
|
color='00bb00',
|
||||||
|
vm_role=True,
|
||||||
|
parent=roles[1]
|
||||||
|
),
|
||||||
|
DeviceRole(
|
||||||
|
name='Device Role 3A1',
|
||||||
|
slug='device-role-3a1',
|
||||||
|
color='0000bb',
|
||||||
|
vm_role=False,
|
||||||
|
parent=roles[2]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for role in child_roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
def test_q(self):
|
def test_q(self):
|
||||||
params = {'q': 'foobar1'}
|
params = {'q': 'foobar1'}
|
||||||
@ -2216,14 +2269,28 @@ class DeviceRoleTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
|
|
||||||
def test_vm_role(self):
|
def test_vm_role(self):
|
||||||
params = {'vm_role': 'true'}
|
params = {'vm_role': 'true'}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6)
|
||||||
params = {'vm_role': 'false'}
|
params = {'vm_role': 'false'}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||||
|
|
||||||
def test_description(self):
|
def test_description(self):
|
||||||
params = {'description': ['foobar1', 'foobar2']}
|
params = {'description': ['foobar1', 'foobar2']}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_parent(self):
|
||||||
|
roles = DeviceRole.objects.filter(parent__isnull=True)[:2]
|
||||||
|
params = {'parent_id': [roles[0].pk, roles[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'parent': [roles[0].slug, roles[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_ancestor(self):
|
||||||
|
roles = DeviceRole.objects.filter(parent__isnull=True)[:2]
|
||||||
|
params = {'ancestor_id': [roles[0].pk, roles[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
|
params = {'ancestor': [roles[0].slug, roles[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
|
|
||||||
|
|
||||||
class PlatformTestCase(TestCase, ChangeLoggedFilterSetTests):
|
class PlatformTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||||
queryset = Platform.objects.all()
|
queryset = Platform.objects.all()
|
||||||
@ -2309,7 +2376,8 @@ class DeviceTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
||||||
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
||||||
)
|
)
|
||||||
DeviceRole.objects.bulk_create(roles)
|
for role in roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
platforms = (
|
platforms = (
|
||||||
Platform(name='Platform 1', slug='platform-1'),
|
Platform(name='Platform 1', slug='platform-1'),
|
||||||
@ -2974,7 +3042,8 @@ class ConsolePortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedF
|
|||||||
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
||||||
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
||||||
)
|
)
|
||||||
DeviceRole.objects.bulk_create(roles)
|
for role in roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
locations = (
|
locations = (
|
||||||
Location(name='Location 1', slug='location-1', site=sites[0]),
|
Location(name='Location 1', slug='location-1', site=sites[0]),
|
||||||
@ -3186,7 +3255,8 @@ class ConsoleServerPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeL
|
|||||||
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
||||||
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
||||||
)
|
)
|
||||||
DeviceRole.objects.bulk_create(roles)
|
for role in roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
locations = (
|
locations = (
|
||||||
Location(name='Location 1', slug='location-1', site=sites[0]),
|
Location(name='Location 1', slug='location-1', site=sites[0]),
|
||||||
@ -3404,7 +3474,8 @@ class PowerPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil
|
|||||||
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
||||||
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
||||||
)
|
)
|
||||||
DeviceRole.objects.bulk_create(roles)
|
for role in roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
locations = (
|
locations = (
|
||||||
Location(name='Location 1', slug='location-1', site=sites[0]),
|
Location(name='Location 1', slug='location-1', site=sites[0]),
|
||||||
@ -3648,7 +3719,8 @@ class PowerOutletTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedF
|
|||||||
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
||||||
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
||||||
)
|
)
|
||||||
DeviceRole.objects.bulk_create(roles)
|
for role in roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
locations = (
|
locations = (
|
||||||
Location(name='Location 1', slug='location-1', site=sites[0]),
|
Location(name='Location 1', slug='location-1', site=sites[0]),
|
||||||
@ -3913,7 +3985,8 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil
|
|||||||
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
||||||
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
||||||
)
|
)
|
||||||
DeviceRole.objects.bulk_create(roles)
|
for role in roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
locations = (
|
locations = (
|
||||||
Location(name='Location 1', slug='location-1', site=sites[0]),
|
Location(name='Location 1', slug='location-1', site=sites[0]),
|
||||||
@ -4492,7 +4565,8 @@ class FrontPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil
|
|||||||
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
||||||
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
||||||
)
|
)
|
||||||
DeviceRole.objects.bulk_create(roles)
|
for role in roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
locations = (
|
locations = (
|
||||||
Location(name='Location 1', slug='location-1', site=sites[0]),
|
Location(name='Location 1', slug='location-1', site=sites[0]),
|
||||||
@ -4764,7 +4838,8 @@ class RearPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFilt
|
|||||||
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
||||||
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
||||||
)
|
)
|
||||||
DeviceRole.objects.bulk_create(roles)
|
for role in roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
locations = (
|
locations = (
|
||||||
Location(name='Location 1', slug='location-1', site=sites[0]),
|
Location(name='Location 1', slug='location-1', site=sites[0]),
|
||||||
@ -5004,7 +5079,8 @@ class ModuleBayTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil
|
|||||||
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
||||||
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
||||||
)
|
)
|
||||||
DeviceRole.objects.bulk_create(roles)
|
for role in roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
locations = (
|
locations = (
|
||||||
Location(name='Location 1', slug='location-1', site=sites[0]),
|
Location(name='Location 1', slug='location-1', site=sites[0]),
|
||||||
@ -5176,7 +5252,8 @@ class DeviceBayTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil
|
|||||||
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
||||||
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
||||||
)
|
)
|
||||||
DeviceRole.objects.bulk_create(roles)
|
for role in roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
locations = (
|
locations = (
|
||||||
Location(name='Location 1', slug='location-1', site=sites[0]),
|
Location(name='Location 1', slug='location-1', site=sites[0]),
|
||||||
@ -5311,7 +5388,8 @@ class InventoryItemTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
||||||
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
||||||
)
|
)
|
||||||
DeviceRole.objects.bulk_create(roles)
|
for role in roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
regions = (
|
regions = (
|
||||||
Region(name='Region 1', slug='region-1'),
|
Region(name='Region 1', slug='region-1'),
|
||||||
|
@ -346,7 +346,8 @@ class DeviceTestCase(TestCase):
|
|||||||
DeviceRole(name='Test Role 1', slug='test-role-1'),
|
DeviceRole(name='Test Role 1', slug='test-role-1'),
|
||||||
DeviceRole(name='Test Role 2', slug='test-role-2'),
|
DeviceRole(name='Test Role 2', slug='test-role-2'),
|
||||||
)
|
)
|
||||||
DeviceRole.objects.bulk_create(roles)
|
for role in roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
# Create a CustomField with a default value & assign it to all component models
|
# Create a CustomField with a default value & assign it to all component models
|
||||||
cf1 = CustomField.objects.create(name='cf1', default='foo')
|
cf1 = CustomField.objects.create(name='cf1', default='foo')
|
||||||
|
@ -1694,13 +1694,16 @@ class DeviceRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
roles = (
|
roles = [
|
||||||
DeviceRole(name='Device Role 1', slug='device-role-1'),
|
DeviceRole(name='Device Role 1', slug='device-role-1'),
|
||||||
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
||||||
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
||||||
)
|
DeviceRole(name='Device Role 4', slug='device-role-4'),
|
||||||
DeviceRole.objects.bulk_create(roles)
|
]
|
||||||
|
for role in roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
|
roles.append(DeviceRole.objects.create(name='Device Role 5', slug='device-role-5', parent=roles[3]))
|
||||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||||
|
|
||||||
cls.form_data = {
|
cls.form_data = {
|
||||||
@ -1724,6 +1727,7 @@ class DeviceRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
|||||||
f"{roles[0].pk},Device Role 7,New description7",
|
f"{roles[0].pk},Device Role 7,New description7",
|
||||||
f"{roles[1].pk},Device Role 8,New description8",
|
f"{roles[1].pk},Device Role 8,New description8",
|
||||||
f"{roles[2].pk},Device Role 9,New description9",
|
f"{roles[2].pk},Device Role 9,New description9",
|
||||||
|
f"{roles[4].pk},Device Role 10,New description10",
|
||||||
)
|
)
|
||||||
|
|
||||||
cls.bulk_edit_data = {
|
cls.bulk_edit_data = {
|
||||||
@ -1809,7 +1813,8 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
DeviceRole(name='Device Role 1', slug='device-role-1'),
|
DeviceRole(name='Device Role 1', slug='device-role-1'),
|
||||||
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
||||||
)
|
)
|
||||||
DeviceRole.objects.bulk_create(roles)
|
for role in roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
platforms = (
|
platforms = (
|
||||||
Platform(name='Platform 1', slug='platform-1'),
|
Platform(name='Platform 1', slug='platform-1'),
|
||||||
|
@ -8,7 +8,9 @@ from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site
|
|||||||
from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, NetBoxModelFilterSet
|
from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, NetBoxModelFilterSet
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
from users.models import Group, User
|
from users.models import Group, User
|
||||||
from utilities.filters import ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter
|
from utilities.filters import (
|
||||||
|
ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter
|
||||||
|
)
|
||||||
from virtualization.models import Cluster, ClusterGroup, ClusterType
|
from virtualization.models import Cluster, ClusterGroup, ClusterType
|
||||||
from .choices import *
|
from .choices import *
|
||||||
from .filters import TagFilter
|
from .filters import TagFilter
|
||||||
|
@ -322,7 +322,7 @@ class ConfigContextFilterForm(SavedFiltersMixin, FilterForm):
|
|||||||
FieldSet('q', 'filter_id', 'tag_id'),
|
FieldSet('q', 'filter_id', 'tag_id'),
|
||||||
FieldSet('data_source_id', 'data_file_id', name=_('Data')),
|
FieldSet('data_source_id', 'data_file_id', name=_('Data')),
|
||||||
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Location')),
|
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Location')),
|
||||||
FieldSet('device_type_id', 'platform_id', 'role_id', name=_('Device')),
|
FieldSet('device_type_id', 'platform_id', 'device_role_id', name=_('Device')),
|
||||||
FieldSet('cluster_type_id', 'cluster_group_id', 'cluster_id', name=_('Cluster')),
|
FieldSet('cluster_type_id', 'cluster_group_id', 'cluster_id', name=_('Cluster')),
|
||||||
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant'))
|
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant'))
|
||||||
)
|
)
|
||||||
@ -364,7 +364,7 @@ class ConfigContextFilterForm(SavedFiltersMixin, FilterForm):
|
|||||||
required=False,
|
required=False,
|
||||||
label=_('Device types')
|
label=_('Device types')
|
||||||
)
|
)
|
||||||
role_id = DynamicModelMultipleChoiceField(
|
device_role_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=DeviceRole.objects.all(),
|
queryset=DeviceRole.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label=_('Roles')
|
label=_('Roles')
|
||||||
|
@ -904,7 +904,8 @@ class ConfigContextTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
||||||
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
||||||
)
|
)
|
||||||
DeviceRole.objects.bulk_create(device_roles)
|
for device_role in device_roles:
|
||||||
|
device_role.save()
|
||||||
|
|
||||||
platforms = (
|
platforms = (
|
||||||
Platform(name='Platform 1', slug='platform-1'),
|
Platform(name='Platform 1', slug='platform-1'),
|
||||||
|
@ -30,6 +30,10 @@
|
|||||||
<th scope="row">{% trans "Description" %}</th>
|
<th scope="row">{% trans "Description" %}</th>
|
||||||
<td>{{ object.description|placeholder }}</td>
|
<td>{{ object.description|placeholder }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">{% trans "Parent" %}</th>
|
||||||
|
<td>{{ object.parent|linkify|placeholder }}</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Color" %}</th>
|
<th scope="row">{% trans "Color" %}</th>
|
||||||
<td>
|
<td>
|
||||||
@ -52,11 +56,25 @@
|
|||||||
<div class="col col-md-6">
|
<div class="col col-md-6">
|
||||||
{% include 'inc/panels/related_objects.html' %}
|
{% include 'inc/panels/related_objects.html' %}
|
||||||
{% include 'inc/panels/custom_fields.html' %}
|
{% include 'inc/panels/custom_fields.html' %}
|
||||||
|
{% include 'inc/panels/comments.html' %}
|
||||||
{% plugin_right_page object %}
|
{% plugin_right_page object %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col col-md-12">
|
<div class="col col-md-12">
|
||||||
|
<div class="card">
|
||||||
|
<h2 class="card-header">
|
||||||
|
{% trans "Child Device Roles" %}
|
||||||
|
{% if perms.dcim.add_devicerole %}
|
||||||
|
<div class="card-actions">
|
||||||
|
<a href="{% url 'dcim:devicerole_add' %}?parent={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-ghost-primary btn-sm">
|
||||||
|
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add a Device Role" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</h2>
|
||||||
|
{% htmx_table 'dcim:devicerole_list' parent_id=object.pk %}
|
||||||
|
</div>
|
||||||
{% plugin_full_width_page object %}
|
{% plugin_full_width_page object %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -391,7 +391,8 @@ class DynamicFilterLookupExpressionTest(TestCase):
|
|||||||
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
||||||
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
||||||
)
|
)
|
||||||
DeviceRole.objects.bulk_create(roles)
|
for role in roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
platforms = (
|
platforms = (
|
||||||
Platform(name='Platform 1', slug='platform-1'),
|
Platform(name='Platform 1', slug='platform-1'),
|
||||||
|
@ -171,13 +171,15 @@ class VirtualMachineFilterSet(
|
|||||||
name = MultiValueCharFilter(
|
name = MultiValueCharFilter(
|
||||||
lookup_expr='iexact'
|
lookup_expr='iexact'
|
||||||
)
|
)
|
||||||
role_id = django_filters.ModelMultipleChoiceFilter(
|
role_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=DeviceRole.objects.all(),
|
queryset=DeviceRole.objects.all(),
|
||||||
|
lookup_expr='in',
|
||||||
label=_('Role (ID)'),
|
label=_('Role (ID)'),
|
||||||
)
|
)
|
||||||
role = django_filters.ModelMultipleChoiceFilter(
|
role = TreeNodeMultipleChoiceFilter(
|
||||||
field_name='role__slug',
|
field_name='role',
|
||||||
queryset=DeviceRole.objects.all(),
|
queryset=DeviceRole.objects.all(),
|
||||||
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label=_('Role (slug)'),
|
label=_('Role (slug)'),
|
||||||
)
|
)
|
||||||
|
@ -294,7 +294,8 @@ class VirtualMachineTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
||||||
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
||||||
)
|
)
|
||||||
DeviceRole.objects.bulk_create(roles)
|
for role in roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
devices = (
|
devices = (
|
||||||
create_test_device('device1', cluster=clusters[0]),
|
create_test_device('device1', cluster=clusters[0]),
|
||||||
|
@ -203,7 +203,8 @@ class VirtualMachineTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
DeviceRole(name='Device Role 1', slug='device-role-1'),
|
DeviceRole(name='Device Role 1', slug='device-role-1'),
|
||||||
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
||||||
)
|
)
|
||||||
DeviceRole.objects.bulk_create(roles)
|
for role in roles:
|
||||||
|
role.save()
|
||||||
|
|
||||||
platforms = (
|
platforms = (
|
||||||
Platform(name='Platform 1', slug='platform-1'),
|
Platform(name='Platform 1', slug='platform-1'),
|
||||||
|
Loading…
Reference in New Issue
Block a user