feat(dcim): Add device, module and rack count filters
CI / build (20.x, 3.12) (push) Has been cancelled
CI / build (20.x, 3.13) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, actions) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, python) (push) Has been cancelled

Introduces `device_count`, `module_count` and `rack_count` filters to
enable queries based on the existence and count of the associated
device, module or rack instances.
Updates forms, filtersets, and GraphQL schema to support these filters,
along with tests for validation.

Fixes #19523
This commit is contained in:
Martin Hauser
2025-11-13 17:17:39 +01:00
committed by Jeremy Stretch
parent 01cbdbb968
commit cee2a5e0ed
17 changed files with 202 additions and 57 deletions
+12 -5
View File
@@ -19,9 +19,11 @@ from netbox.models.mixins import WeightMixin
from netbox.models.features import ContactsMixin, ImageAttachmentsMixin
from utilities.conversion import to_grams
from utilities.data import array_to_string, drange
from utilities.fields import ColorField
from utilities.fields import ColorField, CounterCacheField
from utilities.tracking import TrackingModelMixin
from .device_components import PowerPort
from .devices import Device, Module
from .devices import Device
from .modules import Module
from .power import PowerFeed
__all__ = (
@@ -144,6 +146,10 @@ class RackType(RackBase):
max_length=100,
unique=True
)
rack_count = CounterCacheField(
to_model='dcim.Rack',
to_field='rack_type'
)
clone_fields = (
'manufacturer', 'form_factor', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_height', 'outer_depth',
@@ -234,7 +240,7 @@ class RackRole(OrganizationalModel):
verbose_name_plural = _('rack roles')
class Rack(ContactsMixin, ImageAttachmentsMixin, RackBase):
class Rack(ContactsMixin, ImageAttachmentsMixin, TrackingModelMixin, RackBase):
"""
Devices are housed within Racks. Each rack has a defined height measured in rack units, and a front and rear face.
Each Rack is assigned to a Site and (optionally) a Location.
@@ -509,7 +515,7 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, RackBase):
return [u for u in elevation.values()]
def get_available_units(self, u_height=1, rack_face=None, exclude=None, ignore_excluded_devices=False):
def get_available_units(self, u_height=1.0, rack_face=None, exclude=None, ignore_excluded_devices=False):
"""
Return a list of units within the rack available to accommodate a device of a given U height (default 1).
Optionally exclude one or more devices when calculating empty units (needed when moving a device from one
@@ -581,9 +587,10 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, RackBase):
:param unit_height: Height of each rack unit for the rendered drawing. Note this is not the total
height of the elevation
:param legend_width: Width of the unit legend, in pixels
:param margin_width: Width of the rigth-hand margin, in pixels
:param margin_width: Width of the right-hand margin, in pixels
:param include_images: Embed front/rear device images where available
:param base_url: Base URL for links and images. If none, URLs will be relative.
:param highlight_params: Dictionary of parameters to be passed to the RackElevationSVG.render_highlight() method
"""
elevation = RackElevationSVG(
self,