Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions netbox/dcim/api/serializers_/devicetypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from dcim.choices import *
from dcim.models import DeviceType, ModuleType, ModuleTypeProfile
from netbox.api.fields import AttributesField, ChoiceField, RelatedObjectCountField
from netbox.api.fields import AttributesField, ChoiceField
from netbox.api.serializers import PrimaryModelSerializer
from netbox.choices import *
from .manufacturers import ManufacturerSerializer
Expand Down Expand Up @@ -45,9 +45,7 @@ class DeviceTypeSerializer(PrimaryModelSerializer):
device_bay_template_count = serializers.IntegerField(read_only=True)
module_bay_template_count = serializers.IntegerField(read_only=True)
inventory_item_template_count = serializers.IntegerField(read_only=True)

# Related object counts
device_count = RelatedObjectCountField('instances')
device_count = serializers.IntegerField(read_only=True)

class Meta:
model = DeviceType
Expand Down Expand Up @@ -100,12 +98,13 @@ class ModuleTypeSerializer(PrimaryModelSerializer):
required=False,
allow_null=True
)
module_count = serializers.IntegerField(read_only=True)

class Meta:
model = ModuleType
fields = [
'id', 'url', 'display_url', 'display', 'profile', 'manufacturer', 'model', 'part_number', 'airflow',
'weight', 'weight_unit', 'description', 'attributes', 'owner', 'comments', 'tags', 'custom_fields',
'created', 'last_updated',
'created', 'last_updated', 'module_count',
]
brief_fields = ('id', 'url', 'display', 'profile', 'manufacturer', 'model', 'description')
4 changes: 2 additions & 2 deletions netbox/dcim/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def ready(self):
from netbox.models.features import register_models
from utilities.counters import connect_counters
from . import signals, search # noqa: F401
from .models import CableTermination, Device, DeviceType, VirtualChassis
from .models import CableTermination, Device, DeviceType, ModuleType, VirtualChassis

# Register models
register_models(*self.get_models())
Expand All @@ -31,4 +31,4 @@ def ready(self):
})

# Register counters
connect_counters(Device, DeviceType, VirtualChassis)
connect_counters(Device, DeviceType, ModuleType, VirtualChassis)
8 changes: 7 additions & 1 deletion netbox/dcim/filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,7 @@ class Meta:
'device_bay_template_count',
'module_bay_template_count',
'inventory_item_template_count',
'device_count',
)

def search(self, queryset, name, value):
Expand Down Expand Up @@ -747,7 +748,12 @@ class ModuleTypeFilterSet(AttributeFiltersMixin, PrimaryModelFilterSet):

class Meta:
model = ModuleType
fields = ('id', 'model', 'part_number', 'airflow', 'weight', 'weight_unit', 'description')
fields = (
'id', 'model', 'part_number', 'airflow', 'weight', 'weight_unit', 'description',

# Counters
'module_count',
)

def search(self, queryset, name, value):
if not value.strip():
Expand Down
18 changes: 16 additions & 2 deletions netbox/dcim/forms/filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,8 @@ class DeviceTypeFilterForm(PrimaryModelFilterSetForm):
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet(
'manufacturer_id', 'default_platform_id', 'part_number', 'subdevice_role', 'airflow', name=_('Hardware')
'manufacturer_id', 'default_platform_id', 'part_number', 'device_count',
'subdevice_role', 'airflow', name=_('Hardware')
),
FieldSet('has_front_image', 'has_rear_image', name=_('Images')),
FieldSet(
Expand All @@ -522,6 +523,11 @@ class DeviceTypeFilterForm(PrimaryModelFilterSetForm):
label=_('Part number'),
required=False
)
device_count = forms.IntegerField(
label=_('Device count'),
required=False,
min_value=0,
)
subdevice_role = forms.MultipleChoiceField(
label=_('Subdevice role'),
choices=add_blank_choice(SubdeviceRoleChoices),
Expand Down Expand Up @@ -633,7 +639,10 @@ class ModuleTypeFilterForm(PrimaryModelFilterSetForm):
model = ModuleType
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('profile_id', 'manufacturer_id', 'part_number', 'airflow', name=_('Hardware')),
FieldSet(
'profile_id', 'manufacturer_id', 'part_number', 'module_count',
'airflow', name=_('Hardware')
),
FieldSet(
'console_ports', 'console_server_ports', 'power_ports', 'power_outlets', 'interfaces',
'pass_through_ports', name=_('Components')
Expand All @@ -655,6 +664,11 @@ class ModuleTypeFilterForm(PrimaryModelFilterSetForm):
label=_('Part number'),
required=False
)
module_count = forms.IntegerField(
label=_('Module count'),
required=False,
min_value=0,
)
console_ports = forms.NullBooleanField(
required=False,
label=_('Has console ports'),
Expand Down
10 changes: 9 additions & 1 deletion netbox/dcim/graphql/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import strawberry
import strawberry_django
from strawberry.scalars import ID
from strawberry_django import FilterLookup
from strawberry_django import ComparisonFilterLookup, FilterLookup

from core.graphql.filter_mixins import ChangeLogFilterMixin
from dcim import models
Expand Down Expand Up @@ -328,6 +328,9 @@ class DeviceTypeFilter(ImageAttachmentFilterMixin, PrimaryModelFilterMixin, Weig
)
default_platform_id: ID | None = strawberry_django.filter_field()
part_number: FilterLookup[str] | None = strawberry_django.filter_field()
instances: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
strawberry_django.filter_field()
)
u_height: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
strawberry_django.filter_field()
)
Expand Down Expand Up @@ -385,6 +388,7 @@ class DeviceTypeFilter(ImageAttachmentFilterMixin, PrimaryModelFilterMixin, Weig
device_bay_template_count: FilterLookup[int] | None = strawberry_django.filter_field()
module_bay_template_count: FilterLookup[int] | None = strawberry_django.filter_field()
inventory_item_template_count: FilterLookup[int] | None = strawberry_django.filter_field()
device_count: ComparisonFilterLookup[int] | None = strawberry_django.filter_field()


@strawberry_django.filter_type(models.FrontPort, lookups=True)
Expand Down Expand Up @@ -685,6 +689,9 @@ class ModuleTypeFilter(ImageAttachmentFilterMixin, PrimaryModelFilterMixin, Weig
profile_id: ID | None = strawberry_django.filter_field()
model: FilterLookup[str] | None = strawberry_django.filter_field()
part_number: FilterLookup[str] | None = strawberry_django.filter_field()
instances: Annotated['ModuleFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
strawberry_django.filter_field()
)
airflow: Annotated['ModuleAirflowEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
strawberry_django.filter_field()
)
Expand Down Expand Up @@ -718,6 +725,7 @@ class ModuleTypeFilter(ImageAttachmentFilterMixin, PrimaryModelFilterMixin, Weig
inventory_item_templates: (
Annotated['InventoryItemTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None
) = strawberry_django.filter_field()
module_count: ComparisonFilterLookup[int] | None = strawberry_django.filter_field()


@strawberry_django.filter_type(models.Platform, lookups=True)
Expand Down
2 changes: 2 additions & 0 deletions netbox/dcim/graphql/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ class DeviceTypeType(PrimaryObjectType):
device_bay_template_count: BigInt
module_bay_template_count: BigInt
inventory_item_template_count: BigInt
device_count: BigInt
front_image: strawberry_django.fields.types.DjangoImageType | None
rear_image: strawberry_django.fields.types.DjangoImageType | None
manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')]
Expand Down Expand Up @@ -605,6 +606,7 @@ class ModuleTypeProfileType(PrimaryObjectType):
pagination=True
)
class ModuleTypeType(PrimaryObjectType):
module_count: BigInt
profile: Annotated["ModuleTypeProfileType", strawberry.lazy('dcim.graphql.types')] | None
manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')]

Expand Down
25 changes: 25 additions & 0 deletions netbox/dcim/migrations/0218_devicetype_device_count.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import utilities.fields
from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
('dcim', '0217_owner'),
]

operations = [
migrations.AddField(
model_name='devicetype',
name='device_count',
field=utilities.fields.CounterCacheField(
default=0, editable=False, to_field='device_type', to_model='dcim.Device'
),
),
migrations.AddField(
model_name='moduletype',
name='module_count',
field=utilities.fields.CounterCacheField(
default=0, editable=False, to_field='module_type', to_model='dcim.Module'
),
),
]
4 changes: 4 additions & 0 deletions netbox/dcim/models/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ class DeviceType(ImageAttachmentsMixin, PrimaryModel, WeightMixin):
to_model='dcim.InventoryItemTemplate',
to_field='device_type'
)
device_count = CounterCacheField(
to_model='dcim.Device',
to_field='device_type'
)

clone_fields = (
'manufacturer', 'default_platform', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow', 'weight',
Expand Down
8 changes: 7 additions & 1 deletion netbox/dcim/models/modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
from netbox.models import PrimaryModel
from netbox.models.features import ImageAttachmentsMixin
from netbox.models.mixins import WeightMixin
from utilities.fields import CounterCacheField
from utilities.jsonschema import validate_schema
from utilities.string import title
from utilities.tracking import TrackingModelMixin
from .device_components import *

__all__ = (
Expand Down Expand Up @@ -92,6 +94,10 @@ class ModuleType(ImageAttachmentsMixin, PrimaryModel, WeightMixin):
null=True,
verbose_name=_('attributes')
)
module_count = CounterCacheField(
to_model='dcim.Module',
to_field='module_type'
)

clone_fields = ('profile', 'manufacturer', 'weight', 'weight_unit', 'airflow')
prerequisite_models = (
Expand Down Expand Up @@ -186,7 +192,7 @@ def to_yaml(self):
return yaml.dump(dict(data), sort_keys=False)


class Module(PrimaryModel, ConfigContextModel):
class Module(TrackingModelMixin, PrimaryModel, ConfigContextModel):
"""
A Module represents a field-installable component within a Device which may itself hold multiple device components
(for example, a line card within a chassis switch). Modules are instantiated from ModuleTypes.
Expand Down
12 changes: 5 additions & 7 deletions netbox/dcim/tables/devicetypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,6 @@ class DeviceTypeTable(PrimaryModelTable):
template_code=WEIGHT,
order_by=('_abs_weight', 'weight_unit')
)
instance_count = columns.LinkedCountColumn(
viewname='dcim:device_list',
url_params={'device_type_id': 'pk'},
verbose_name=_('Instances')
)
console_port_template_count = tables.Column(
verbose_name=_('Console Ports')
)
Expand Down Expand Up @@ -144,16 +139,19 @@ class DeviceTypeTable(PrimaryModelTable):
inventory_item_template_count = tables.Column(
verbose_name=_('Inventory Items')
)
device_count = tables.Column(
verbose_name=_('Device Count')
)

class Meta(PrimaryModelTable.Meta):
model = models.DeviceType
fields = (
'pk', 'id', 'model', 'manufacturer', 'default_platform', 'slug', 'part_number', 'u_height',
'exclude_from_utilization', 'is_full_depth', 'subdevice_role', 'airflow', 'weight',
'description', 'comments', 'instance_count', 'tags', 'created', 'last_updated',
'description', 'comments', 'device_count', 'tags', 'created', 'last_updated',
)
default_columns = (
'pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'instance_count',
'pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'device_count',
)


Expand Down
7 changes: 5 additions & 2 deletions netbox/dcim/tables/modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ class ModuleTypeTable(PrimaryModelTable):
url_params={'module_type_id': 'pk'},
verbose_name=_('Instances')
)
module_count = tables.Column(
verbose_name=_('Module Count')
)
tags = columns.TagColumn(
url_name='dcim:moduletype_list'
)
Expand All @@ -69,10 +72,10 @@ class Meta(PrimaryModelTable.Meta):
model = ModuleType
fields = (
'pk', 'id', 'model', 'profile', 'manufacturer', 'part_number', 'airflow', 'weight', 'description',
'attributes', 'comments', 'tags', 'created', 'last_updated',
'attributes', 'module_count', 'comments', 'tags', 'created', 'last_updated',
)
default_columns = (
'pk', 'model', 'profile', 'manufacturer', 'part_number',
'pk', 'model', 'profile', 'manufacturer', 'part_number', 'module_count',
)


Expand Down
16 changes: 4 additions & 12 deletions netbox/dcim/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1111,9 +1111,7 @@ class ManufacturerBulkDeleteView(generic.BulkDeleteView):

@register_model_view(DeviceType, 'list', path='', detail=False)
class DeviceTypeListView(generic.ObjectListView):
queryset = DeviceType.objects.annotate(
instance_count=count_related(Device, 'device_type')
)
queryset = DeviceType.objects.all()
filterset = filtersets.DeviceTypeFilterSet
filterset_form = forms.DeviceTypeFilterForm
table = tables.DeviceTypeTable
Expand Down Expand Up @@ -1332,9 +1330,7 @@ def prep_related_object_data(self, parent, data):

@register_model_view(DeviceType, 'bulk_edit', path='edit', detail=False)
class DeviceTypeBulkEditView(generic.BulkEditView):
queryset = DeviceType.objects.annotate(
instance_count=count_related(Device, 'device_type')
)
queryset = DeviceType.objects.all()
filterset = filtersets.DeviceTypeFilterSet
table = tables.DeviceTypeTable
form = forms.DeviceTypeBulkEditForm
Expand All @@ -1349,9 +1345,7 @@ class DeviceTypeBulkRenameView(generic.BulkRenameView):

@register_model_view(DeviceType, 'bulk_delete', path='delete', detail=False)
class DeviceTypeBulkDeleteView(generic.BulkDeleteView):
queryset = DeviceType.objects.annotate(
instance_count=count_related(Device, 'device_type')
)
queryset = DeviceType.objects.all()
filterset = filtersets.DeviceTypeFilterSet
table = tables.DeviceTypeTable

Expand Down Expand Up @@ -1424,9 +1418,7 @@ class ModuleTypeProfileBulkDeleteView(generic.BulkDeleteView):

@register_model_view(ModuleType, 'list', path='', detail=False)
class ModuleTypeListView(generic.ObjectListView):
queryset = ModuleType.objects.annotate(
instance_count=count_related(Module, 'module_type')
)
queryset = ModuleType.objects.all()
filterset = filtersets.ModuleTypeFilterSet
filterset_form = forms.ModuleTypeFilterForm
table = tables.ModuleTypeTable
Expand Down
Loading