Enable MPTT for module bays

This commit is contained in:
Jeremy Stretch 2024-08-02 16:08:05 -04:00
parent f8d99ffe5e
commit 6372d065be
5 changed files with 106 additions and 24 deletions

View File

@ -496,12 +496,18 @@ class ModuleType(NetBoxObjectType):
@strawberry_django.type( @strawberry_django.type(
models.ModuleBay, models.ModuleBay,
fields='__all__', # fields='__all__',
exclude=('parent',),
filters=ModuleBayFilter filters=ModuleBayFilter
) )
class ModuleBayType(ModularComponentType): class ModuleBayType(ModularComponentType):
installed_module: Annotated["ModuleType", strawberry.lazy('dcim.graphql.types')] | None installed_module: Annotated["ModuleType", strawberry.lazy('dcim.graphql.types')] | None
children: List[Annotated["ModuleBayType", strawberry.lazy('dcim.graphql.types')]]
@strawberry_django.field
def parent(self) -> Annotated["ModuleBayType", strawberry.lazy('dcim.graphql.types')] | None:
return self.parent
@strawberry_django.type( @strawberry_django.type(

View File

@ -0,0 +1,44 @@
# Generated by Django 5.0.7 on 2024-08-02 20:07
import django.db.models.deletion
import mptt.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dcim', '0190_nested_modules'),
]
operations = [
migrations.AddField(
model_name='modulebay',
name='level',
field=models.PositiveIntegerField(default=0, editable=False),
preserve_default=False,
),
migrations.AddField(
model_name='modulebay',
name='lft',
field=models.PositiveIntegerField(default=0, editable=False),
preserve_default=False,
),
migrations.AddField(
model_name='modulebay',
name='parent',
field=mptt.fields.TreeForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='dcim.modulebay'),
),
migrations.AddField(
model_name='modulebay',
name='rght',
field=models.PositiveIntegerField(default=0, editable=False),
preserve_default=False,
),
migrations.AddField(
model_name='modulebay',
name='tree_id',
field=models.PositiveIntegerField(db_index=True, default=0, editable=False),
preserve_default=False,
),
]

View File

@ -4,7 +4,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelatio
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models from django.db import models
from django.db.models import Sum from django.db.models import F, Sum
from django.urls import reverse from django.urls import reverse
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from mptt.models import MPTTModel, TreeForeignKey from mptt.models import MPTTModel, TreeForeignKey
@ -1087,10 +1087,19 @@ class RearPort(ModularComponentModel, CabledObjectModel, TrackingModelMixin):
# Bays # Bays
# #
class ModuleBay(ModularComponentModel, TrackingModelMixin): class ModuleBay(ModularComponentModel, TrackingModelMixin, MPTTModel):
""" """
An empty space within a Device which can house a child device An empty space within a Device which can house a child device
""" """
parent = TreeForeignKey(
to='self',
on_delete=models.CASCADE,
related_name='children',
blank=True,
null=True,
editable=False,
db_index=True
)
position = models.CharField( position = models.CharField(
verbose_name=_('position'), verbose_name=_('position'),
max_length=30, max_length=30,
@ -1098,6 +1107,8 @@ class ModuleBay(ModularComponentModel, TrackingModelMixin):
help_text=_('Identifier to reference when renaming installed components') help_text=_('Identifier to reference when renaming installed components')
) )
objects = TreeManager()
clone_fields = ('device',) clone_fields = ('device',)
class Meta(ModularComponentModel.Meta): class Meta(ModularComponentModel.Meta):
@ -1110,6 +1121,9 @@ class ModuleBay(ModularComponentModel, TrackingModelMixin):
verbose_name = _('module bay') verbose_name = _('module bay')
verbose_name_plural = _('module bays') verbose_name_plural = _('module bays')
class MPTTMeta:
order_insertion_by = ('module',)
def get_absolute_url(self): def get_absolute_url(self):
return reverse('dcim:modulebay', kwargs={'pk': self.pk}) return reverse('dcim:modulebay', kwargs={'pk': self.pk})
@ -1127,6 +1141,11 @@ class ModuleBay(ModularComponentModel, TrackingModelMixin):
module_bays.append(module.module_bay.pk) module_bays.append(module.module_bay.pk)
module = module.module_bay.module if module.module_bay else None module = module.module_bay.module if module.module_bay else None
def save(self, *args, **kwargs):
if self.module:
self.parent = self.module.module_bay
super().save(*args, **kwargs)
class DeviceBay(ComponentModel, TrackingModelMixin): class DeviceBay(ComponentModel, TrackingModelMixin):
""" """

View File

@ -1046,7 +1046,8 @@ class Device(
self._instantiate_components(self.device_type.interfacetemplates.all()) self._instantiate_components(self.device_type.interfacetemplates.all())
self._instantiate_components(self.device_type.rearporttemplates.all()) self._instantiate_components(self.device_type.rearporttemplates.all())
self._instantiate_components(self.device_type.frontporttemplates.all()) self._instantiate_components(self.device_type.frontporttemplates.all())
self._instantiate_components(self.device_type.modulebaytemplates.all()) # Disable bulk_create to accommodate MPTT
self._instantiate_components(self.device_type.modulebaytemplates.all(), bulk_create=False)
self._instantiate_components(self.device_type.devicebaytemplates.all()) self._instantiate_components(self.device_type.devicebaytemplates.all())
# Disable bulk_create to accommodate MPTT # Disable bulk_create to accommodate MPTT
self._instantiate_components(self.device_type.inventoryitemtemplates.all(), bulk_create=False) self._instantiate_components(self.device_type.inventoryitemtemplates.all(), bulk_create=False)
@ -1269,17 +1270,19 @@ class Module(PrimaryModel, ConfigContextModel):
if not disable_replication: if not disable_replication:
create_instances.append(template_instance) create_instances.append(template_instance)
component_model.objects.bulk_create(create_instances) # component_model.objects.bulk_create(create_instances)
# Emit the post_save signal for each newly created object # # Emit the post_save signal for each newly created object
for component in create_instances: # for component in create_instances:
post_save.send( # post_save.send(
sender=component_model, # sender=component_model,
instance=component, # instance=component,
created=True, # created=True,
raw=False, # raw=False,
using='default', # using='default',
update_fields=None # update_fields=None
) # )
for instance in create_instances:
instance.save()
update_fields = ['module'] update_fields = ['module']
component_model.objects.bulk_update(update_instances, update_fields) component_model.objects.bulk_update(update_instances, update_fields)

View File

@ -313,6 +313,10 @@ class ModularDeviceComponentTable(DeviceComponentTable):
verbose_name=_('Inventory Items'), verbose_name=_('Inventory Items'),
) )
class Meta(NetBoxTable.Meta):
pass
# order_by = ('device', 'module', 'name')
class CableTerminationTable(NetBoxTable): class CableTerminationTable(NetBoxTable):
cable = tables.Column( cable = tables.Column(
@ -852,10 +856,9 @@ class ModuleBayTable(ModularDeviceComponentTable):
'args': [Accessor('device_id')], 'args': [Accessor('device_id')],
} }
) )
parent_bay = tables.Column( parent = tables.Column(
accessor=tables.A('module__module_bay'),
linkify=True, linkify=True,
verbose_name=_('Parent Bay') verbose_name=_('Parent'),
) )
installed_module = tables.Column( installed_module = tables.Column(
linkify=True, linkify=True,
@ -878,14 +881,14 @@ class ModuleBayTable(ModularDeviceComponentTable):
verbose_name=_('Module Status') verbose_name=_('Module Status')
) )
class Meta(DeviceComponentTable.Meta): class Meta(ModularDeviceComponentTable.Meta):
model = models.ModuleBay model = models.ModuleBay
fields = ( fields = (
'pk', 'id', 'name', 'device', 'parent_bay', 'label', 'position', 'installed_module', 'module_status', 'pk', 'id', 'name', 'device', 'parent', 'label', 'position', 'installed_module', 'module_status',
'module_serial', 'module_asset_tag', 'description', 'tags', 'module_serial', 'module_asset_tag', 'description', 'tags',
) )
default_columns = ( default_columns = (
'pk', 'name', 'device', 'parent_bay', 'label', 'installed_module', 'module_status', 'description', 'pk', 'name', 'device', 'parent', 'label', 'installed_module', 'module_status', 'description',
) )
def render_parent_bay(self, value): def render_parent_bay(self, value):
@ -896,17 +899,24 @@ class ModuleBayTable(ModularDeviceComponentTable):
class DeviceModuleBayTable(ModuleBayTable): class DeviceModuleBayTable(ModuleBayTable):
name = tables.TemplateColumn(
verbose_name=_('Name'),
template_code='<a href="{{ record.get_absolute_url }}" style="padding-left: {{ record.level }}0px">'
'{{ value }}</a>',
order_by=Accessor('_name'),
attrs={'td': {'class': 'text-nowrap'}}
)
actions = columns.ActionsColumn( actions = columns.ActionsColumn(
extra_buttons=MODULEBAY_BUTTONS extra_buttons=MODULEBAY_BUTTONS
) )
class Meta(DeviceComponentTable.Meta): class Meta(ModuleBayTable.Meta):
model = models.ModuleBay model = models.ModuleBay
fields = ( fields = (
'pk', 'id', 'parent_bay', 'name', 'label', 'position', 'installed_module', 'module_status', 'module_serial', 'pk', 'id', 'parent', 'name', 'label', 'position', 'installed_module', 'module_status', 'module_serial',
'module_asset_tag', 'description', 'tags', 'actions', 'module_asset_tag', 'description', 'tags', 'actions',
) )
default_columns = ('pk', 'parent_bay', 'name', 'label', 'installed_module', 'module_status', 'description') default_columns = ('pk', 'name', 'label', 'installed_module', 'module_status', 'description')
class InventoryItemTable(DeviceComponentTable): class InventoryItemTable(DeviceComponentTable):