mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 09:51:22 -06:00
Closes #10675: Add max_weight field to track maximum load capacity for racks
This commit is contained in:
parent
2b12138c41
commit
0b100b8fc8
@ -73,6 +73,10 @@ The maximum depth of a mounted device that the rack can accommodate, in millimet
|
|||||||
|
|
||||||
The numeric weight of the rack, including a unit designation (e.g. 10 kilograms or 20 pounds).
|
The numeric weight of the rack, including a unit designation (e.g. 10 kilograms or 20 pounds).
|
||||||
|
|
||||||
|
### Maximum Weight
|
||||||
|
|
||||||
|
The maximum total weight capacity for all installed devices, inclusive of the rack itself.
|
||||||
|
|
||||||
### Descending Units
|
### Descending Units
|
||||||
|
|
||||||
If selected, the rack's elevation will display unit 1 at the top of the rack. (Most racks use asceneding numbering, with unit 1 assigned to the bottommost position.)
|
If selected, the rack's elevation will display unit 1 at the top of the rack. (Most racks use ascending numbering, with unit 1 assigned to the bottommost position.)
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
* [#815](https://github.com/netbox-community/netbox/issues/815) - Enable specifying terminations when bulk importing circuits
|
* [#815](https://github.com/netbox-community/netbox/issues/815) - Enable specifying terminations when bulk importing circuits
|
||||||
* [#10371](https://github.com/netbox-community/netbox/issues/10371) - Add operational status field for modules
|
* [#10371](https://github.com/netbox-community/netbox/issues/10371) - Add operational status field for modules
|
||||||
|
* [#10675](https://github.com/netbox-community/netbox/issues/10675) - Add `max_weight` field to track maximum load capacity for racks
|
||||||
* [#10945](https://github.com/netbox-community/netbox/issues/10945) - Enabled recurring execution of scheduled reports & scripts
|
* [#10945](https://github.com/netbox-community/netbox/issues/10945) - Enabled recurring execution of scheduled reports & scripts
|
||||||
* [#11090](https://github.com/netbox-community/netbox/issues/11090) - Add regular expression support to global search engine
|
* [#11090](https://github.com/netbox-community/netbox/issues/11090) - Add regular expression support to global search engine
|
||||||
* [#11022](https://github.com/netbox-community/netbox/issues/11022) - Introduce `QUEUE_MAPPINGS` configuration parameter to allow customization of background task prioritization
|
* [#11022](https://github.com/netbox-community/netbox/issues/11022) - Introduce `QUEUE_MAPPINGS` configuration parameter to allow customization of background task prioritization
|
||||||
@ -146,7 +147,7 @@ This release introduces a new programmatic API that enables plugins and custom s
|
|||||||
* Added `description` and `comments` fields
|
* Added `description` and `comments` fields
|
||||||
* dcim.Rack
|
* dcim.Rack
|
||||||
* Added a `description` field
|
* Added a `description` field
|
||||||
* Added optional `weight` and `weight_unit` fields
|
* Added optional `weight`, `max_weight`, and `weight_unit` fields
|
||||||
* dcim.RackReservation
|
* dcim.RackReservation
|
||||||
* Added a `comments` field
|
* Added a `comments` field
|
||||||
* dcim.VirtualChassis
|
* dcim.VirtualChassis
|
||||||
|
@ -210,9 +210,9 @@ class RackSerializer(NetBoxModelSerializer):
|
|||||||
model = Rack
|
model = Rack
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'facility_id', 'site', 'location', 'tenant', 'status', 'role', 'serial',
|
'id', 'url', 'display', 'name', 'facility_id', 'site', 'location', 'tenant', 'status', 'role', 'serial',
|
||||||
'asset_tag', 'type', 'width', 'u_height', 'weight', 'weight_unit', 'desc_units', 'outer_width',
|
'asset_tag', 'type', 'width', 'u_height', 'weight', 'max_weight', 'weight_unit', 'desc_units',
|
||||||
'outer_depth', 'outer_unit', 'mounting_depth', 'description', 'comments', 'tags', 'custom_fields',
|
'outer_width', 'outer_depth', 'outer_unit', 'mounting_depth', 'description', 'comments', 'tags',
|
||||||
'created', 'last_updated', 'device_count', 'powerfeed_count',
|
'custom_fields', 'created', 'last_updated', 'device_count', 'powerfeed_count',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -322,7 +322,7 @@ class RackFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSe
|
|||||||
model = Rack
|
model = Rack
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'name', 'facility_id', 'asset_tag', 'u_height', 'desc_units', 'outer_width', 'outer_depth',
|
'id', 'name', 'facility_id', 'asset_tag', 'u_height', 'desc_units', 'outer_width', 'outer_depth',
|
||||||
'outer_unit', 'mounting_depth', 'weight', 'weight_unit'
|
'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit'
|
||||||
]
|
]
|
||||||
|
|
||||||
def search(self, queryset, name, value):
|
def search(self, queryset, name, value):
|
||||||
|
@ -294,6 +294,10 @@ class RackBulkEditForm(NetBoxModelBulkEditForm):
|
|||||||
min_value=0,
|
min_value=0,
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
max_weight = forms.IntegerField(
|
||||||
|
min_value=0,
|
||||||
|
required=False
|
||||||
|
)
|
||||||
weight_unit = forms.ChoiceField(
|
weight_unit = forms.ChoiceField(
|
||||||
choices=add_blank_choice(WeightUnitChoices),
|
choices=add_blank_choice(WeightUnitChoices),
|
||||||
required=False,
|
required=False,
|
||||||
@ -316,11 +320,11 @@ class RackBulkEditForm(NetBoxModelBulkEditForm):
|
|||||||
('Hardware', (
|
('Hardware', (
|
||||||
'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'mounting_depth',
|
'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'mounting_depth',
|
||||||
)),
|
)),
|
||||||
('Weight', ('weight', 'weight_unit')),
|
('Weight', ('weight', 'max_weight', 'weight_unit')),
|
||||||
)
|
)
|
||||||
nullable_fields = (
|
nullable_fields = (
|
||||||
'location', 'tenant', 'role', 'serial', 'asset_tag', 'outer_width', 'outer_depth', 'outer_unit', 'weight',
|
'location', 'tenant', 'role', 'serial', 'asset_tag', 'outer_width', 'outer_depth', 'outer_unit', 'weight',
|
||||||
'weight_unit', 'description', 'comments',
|
'max_weight', 'weight_unit', 'description', 'comments',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -195,13 +195,18 @@ class RackImportForm(NetBoxModelImportForm):
|
|||||||
required=False,
|
required=False,
|
||||||
help_text=_('Unit for outer dimensions')
|
help_text=_('Unit for outer dimensions')
|
||||||
)
|
)
|
||||||
|
weight_unit = CSVChoiceField(
|
||||||
|
choices=WeightUnitChoices,
|
||||||
|
required=False,
|
||||||
|
help_text=_('Unit for rack weights')
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Rack
|
model = Rack
|
||||||
fields = (
|
fields = (
|
||||||
'site', 'location', 'name', 'facility_id', 'tenant', 'status', 'role', 'type', 'serial', 'asset_tag',
|
'site', 'location', 'name', 'facility_id', 'tenant', 'status', 'role', 'type', 'serial', 'asset_tag',
|
||||||
'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'mounting_depth',
|
'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'mounting_depth', 'weight',
|
||||||
'description', 'comments', 'tags',
|
'max_weight', 'weight_unit', 'description', 'comments', 'tags',
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, data=None, *args, **kwargs):
|
def __init__(self, data=None, *args, **kwargs):
|
||||||
|
@ -229,7 +229,7 @@ class RackFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilte
|
|||||||
('Hardware', ('type', 'width', 'serial', 'asset_tag')),
|
('Hardware', ('type', 'width', 'serial', 'asset_tag')),
|
||||||
('Tenant', ('tenant_group_id', 'tenant_id')),
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
('Contacts', ('contact', 'contact_role', 'contact_group')),
|
('Contacts', ('contact', 'contact_role', 'contact_group')),
|
||||||
('Weight', ('weight', 'weight_unit')),
|
('Weight', ('weight', 'max_weight', 'weight_unit')),
|
||||||
)
|
)
|
||||||
region_id = DynamicModelMultipleChoiceField(
|
region_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
@ -284,7 +284,12 @@ class RackFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilte
|
|||||||
)
|
)
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
weight = forms.DecimalField(
|
weight = forms.DecimalField(
|
||||||
required=False
|
required=False,
|
||||||
|
min_value=1
|
||||||
|
)
|
||||||
|
max_weight = forms.IntegerField(
|
||||||
|
required=False,
|
||||||
|
min_value=1
|
||||||
)
|
)
|
||||||
weight_unit = forms.ChoiceField(
|
weight_unit = forms.ChoiceField(
|
||||||
choices=add_blank_choice(WeightUnitChoices),
|
choices=add_blank_choice(WeightUnitChoices),
|
||||||
|
@ -279,7 +279,7 @@ class RackForm(TenancyForm, NetBoxModelForm):
|
|||||||
fields = [
|
fields = [
|
||||||
'region', 'site_group', 'site', 'location', 'name', 'facility_id', 'tenant_group', 'tenant', 'status',
|
'region', 'site_group', 'site', 'location', 'name', 'facility_id', 'tenant_group', 'tenant', 'status',
|
||||||
'role', 'serial', 'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth',
|
'role', 'serial', 'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth',
|
||||||
'outer_unit', 'mounting_depth', 'weight', 'weight_unit', 'description', 'comments', 'tags',
|
'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit', 'description', 'comments', 'tags',
|
||||||
]
|
]
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'site': _("The site at which the rack exists"),
|
'site': _("The site at which the rack exists"),
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
# Generated by Django 4.0.7 on 2022-09-23 01:01
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
@ -10,11 +8,8 @@ class Migration(migrations.Migration):
|
|||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
|
||||||
model_name='devicetype',
|
# Device types
|
||||||
name='_abs_weight',
|
|
||||||
field=models.PositiveBigIntegerField(blank=True, null=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='devicetype',
|
model_name='devicetype',
|
||||||
name='weight',
|
name='weight',
|
||||||
@ -26,10 +21,12 @@ class Migration(migrations.Migration):
|
|||||||
field=models.CharField(blank=True, max_length=50),
|
field=models.CharField(blank=True, max_length=50),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='moduletype',
|
model_name='devicetype',
|
||||||
name='_abs_weight',
|
name='_abs_weight',
|
||||||
field=models.PositiveBigIntegerField(blank=True, null=True),
|
field=models.PositiveBigIntegerField(blank=True, null=True),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
# Module types
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='moduletype',
|
model_name='moduletype',
|
||||||
name='weight',
|
name='weight',
|
||||||
@ -41,18 +38,35 @@ class Migration(migrations.Migration):
|
|||||||
field=models.CharField(blank=True, max_length=50),
|
field=models.CharField(blank=True, max_length=50),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='rack',
|
model_name='moduletype',
|
||||||
name='_abs_weight',
|
name='_abs_weight',
|
||||||
field=models.PositiveBigIntegerField(blank=True, null=True),
|
field=models.PositiveBigIntegerField(blank=True, null=True),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
# Racks
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='rack',
|
model_name='rack',
|
||||||
name='weight',
|
name='weight',
|
||||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True),
|
field=models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True),
|
||||||
),
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='rack',
|
||||||
|
name='max_weight',
|
||||||
|
field=models.PositiveIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='rack',
|
model_name='rack',
|
||||||
name='weight_unit',
|
name='weight_unit',
|
||||||
field=models.CharField(blank=True, max_length=50),
|
field=models.CharField(blank=True, max_length=50),
|
||||||
),
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='rack',
|
||||||
|
name='_abs_weight',
|
||||||
|
field=models.PositiveBigIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='rack',
|
||||||
|
name='_abs_max_weight',
|
||||||
|
field=models.PositiveBigIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
@ -39,7 +39,5 @@ class WeightMixin(models.Model):
|
|||||||
super().clean()
|
super().clean()
|
||||||
|
|
||||||
# Validate weight and weight_unit
|
# Validate weight and weight_unit
|
||||||
if self.weight is not None and not self.weight_unit:
|
if self.weight and not self.weight_unit:
|
||||||
raise ValidationError("Must specify a unit when setting a weight")
|
raise ValidationError("Must specify a unit when setting a weight")
|
||||||
elif self.weight is None:
|
|
||||||
self.weight_unit = ''
|
|
||||||
|
@ -17,7 +17,7 @@ from dcim.svg import RackElevationSVG
|
|||||||
from netbox.models import OrganizationalModel, PrimaryModel
|
from netbox.models import OrganizationalModel, PrimaryModel
|
||||||
from utilities.choices import ColorChoices
|
from utilities.choices import ColorChoices
|
||||||
from utilities.fields import ColorField, NaturalOrderingField
|
from utilities.fields import ColorField, NaturalOrderingField
|
||||||
from utilities.utils import array_to_string, drange
|
from utilities.utils import array_to_string, drange, to_grams
|
||||||
from .device_components import PowerPort
|
from .device_components import PowerPort
|
||||||
from .devices import Device, Module
|
from .devices import Device, Module
|
||||||
from .mixins import WeightMixin
|
from .mixins import WeightMixin
|
||||||
@ -149,6 +149,16 @@ class Rack(PrimaryModel, WeightMixin):
|
|||||||
choices=RackDimensionUnitChoices,
|
choices=RackDimensionUnitChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
)
|
)
|
||||||
|
max_weight = models.PositiveIntegerField(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
help_text=_('Maximum load capacity for the rack')
|
||||||
|
)
|
||||||
|
# Stores the normalized max weight (in grams) for database ordering
|
||||||
|
_abs_max_weight = models.PositiveBigIntegerField(
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
mounting_depth = models.PositiveSmallIntegerField(
|
mounting_depth = models.PositiveSmallIntegerField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
@ -174,7 +184,7 @@ class Rack(PrimaryModel, WeightMixin):
|
|||||||
|
|
||||||
clone_fields = (
|
clone_fields = (
|
||||||
'site', 'location', 'tenant', 'status', 'role', 'type', 'width', 'u_height', 'desc_units', 'outer_width',
|
'site', 'location', 'tenant', 'status', 'role', 'type', 'width', 'u_height', 'desc_units', 'outer_width',
|
||||||
'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'weight_unit',
|
'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit',
|
||||||
)
|
)
|
||||||
prerequisite_models = (
|
prerequisite_models = (
|
||||||
'dcim.Site',
|
'dcim.Site',
|
||||||
@ -215,6 +225,10 @@ class Rack(PrimaryModel, WeightMixin):
|
|||||||
elif self.outer_width is None and self.outer_depth is None:
|
elif self.outer_width is None and self.outer_depth is None:
|
||||||
self.outer_unit = ''
|
self.outer_unit = ''
|
||||||
|
|
||||||
|
# Validate max_weight and weight_unit
|
||||||
|
if self.max_weight and not self.weight_unit:
|
||||||
|
raise ValidationError("Must specify a unit when setting a maximum weight")
|
||||||
|
|
||||||
if self.pk:
|
if self.pk:
|
||||||
# Validate that Rack is tall enough to house the installed Devices
|
# Validate that Rack is tall enough to house the installed Devices
|
||||||
top_device = Device.objects.filter(
|
top_device = Device.objects.filter(
|
||||||
@ -237,6 +251,16 @@ class Rack(PrimaryModel, WeightMixin):
|
|||||||
'location': f"Location must be from the same site, {self.site}."
|
'location': f"Location must be from the same site, {self.site}."
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
|
||||||
|
# Store the given max weight (if any) in grams for use in database ordering
|
||||||
|
if self.max_weight and self.weight_unit:
|
||||||
|
self._abs_max_weight = to_grams(self.max_weight, self.weight_unit)
|
||||||
|
else:
|
||||||
|
self._abs_max_weight = None
|
||||||
|
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def units(self):
|
def units(self):
|
||||||
"""
|
"""
|
||||||
|
@ -3,7 +3,7 @@ import django_tables2 as tables
|
|||||||
from dcim import models
|
from dcim import models
|
||||||
from netbox.tables import NetBoxTable, columns
|
from netbox.tables import NetBoxTable, columns
|
||||||
from tenancy.tables import ContactsColumnMixin
|
from tenancy.tables import ContactsColumnMixin
|
||||||
from .template_code import MODULAR_COMPONENT_TEMPLATE_BUTTONS, DEVICE_WEIGHT
|
from .template_code import MODULAR_COMPONENT_TEMPLATE_BUTTONS, WEIGHT
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'ConsolePortTemplateTable',
|
'ConsolePortTemplateTable',
|
||||||
@ -84,7 +84,7 @@ class DeviceTypeTable(NetBoxTable):
|
|||||||
template_code='{{ value|floatformat }}'
|
template_code='{{ value|floatformat }}'
|
||||||
)
|
)
|
||||||
weight = columns.TemplateColumn(
|
weight = columns.TemplateColumn(
|
||||||
template_code=DEVICE_WEIGHT,
|
template_code=WEIGHT,
|
||||||
order_by=('_abs_weight', 'weight_unit')
|
order_by=('_abs_weight', 'weight_unit')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import django_tables2 as tables
|
|||||||
|
|
||||||
from dcim.models import Module, ModuleType
|
from dcim.models import Module, ModuleType
|
||||||
from netbox.tables import NetBoxTable, columns
|
from netbox.tables import NetBoxTable, columns
|
||||||
from .template_code import DEVICE_WEIGHT
|
from .template_code import WEIGHT
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'ModuleTable',
|
'ModuleTable',
|
||||||
@ -28,7 +28,7 @@ class ModuleTypeTable(NetBoxTable):
|
|||||||
url_name='dcim:moduletype_list'
|
url_name='dcim:moduletype_list'
|
||||||
)
|
)
|
||||||
weight = columns.TemplateColumn(
|
weight = columns.TemplateColumn(
|
||||||
template_code=DEVICE_WEIGHT,
|
template_code=WEIGHT,
|
||||||
order_by=('_abs_weight', 'weight_unit')
|
order_by=('_abs_weight', 'weight_unit')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ from django_tables2.utils import Accessor
|
|||||||
from dcim.models import Rack, RackReservation, RackRole
|
from dcim.models import Rack, RackReservation, RackRole
|
||||||
from netbox.tables import NetBoxTable, columns
|
from netbox.tables import NetBoxTable, columns
|
||||||
from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
|
from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
|
||||||
from .template_code import DEVICE_WEIGHT
|
from .template_code import WEIGHT
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'RackTable',
|
'RackTable',
|
||||||
@ -81,17 +81,21 @@ class RackTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
|
|||||||
verbose_name='Outer Depth'
|
verbose_name='Outer Depth'
|
||||||
)
|
)
|
||||||
weight = columns.TemplateColumn(
|
weight = columns.TemplateColumn(
|
||||||
template_code=DEVICE_WEIGHT,
|
template_code=WEIGHT,
|
||||||
order_by=('_abs_weight', 'weight_unit')
|
order_by=('_abs_weight', 'weight_unit')
|
||||||
)
|
)
|
||||||
|
max_weight = columns.TemplateColumn(
|
||||||
|
template_code=WEIGHT,
|
||||||
|
order_by=('_abs_max_weight', 'weight_unit')
|
||||||
|
)
|
||||||
|
|
||||||
class Meta(NetBoxTable.Meta):
|
class Meta(NetBoxTable.Meta):
|
||||||
model = Rack
|
model = Rack
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'tenant_group', 'role', 'serial',
|
'pk', 'id', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'tenant_group', 'role', 'serial',
|
||||||
'asset_tag', 'type', 'u_height', 'width', 'outer_width', 'outer_depth', 'mounting_depth', 'weight',
|
'asset_tag', 'type', 'u_height', 'width', 'outer_width', 'outer_depth', 'mounting_depth', 'weight',
|
||||||
'comments', 'device_count', 'get_utilization', 'get_power_utilization', 'description', 'contacts', 'tags',
|
'max_weight', 'comments', 'device_count', 'get_utilization', 'get_power_utilization', 'description',
|
||||||
'created', 'last_updated',
|
'contacts', 'tags', 'created', 'last_updated',
|
||||||
)
|
)
|
||||||
default_columns = (
|
default_columns = (
|
||||||
'pk', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'u_height', 'device_count',
|
'pk', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'u_height', 'device_count',
|
||||||
|
@ -15,9 +15,9 @@ CABLE_LENGTH = """
|
|||||||
{% if record.length %}{{ record.length|simplify_decimal }} {{ record.length_unit }}{% endif %}
|
{% if record.length %}{{ record.length|simplify_decimal }} {{ record.length_unit }}{% endif %}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
DEVICE_WEIGHT = """
|
WEIGHT = """
|
||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
{% if record.weight %}{{ record.weight|simplify_decimal }} {{ record.weight_unit }}{% endif %}
|
{% if value %}{{ value|simplify_decimal }} {{ record.weight_unit }}{% endif %}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
DEVICE_LINK = """
|
DEVICE_LINK = """
|
||||||
|
@ -409,9 +409,9 @@ class RackTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
Tenant.objects.bulk_create(tenants)
|
Tenant.objects.bulk_create(tenants)
|
||||||
|
|
||||||
racks = (
|
racks = (
|
||||||
Rack(name='Rack 1', facility_id='rack-1', site=sites[0], location=locations[0], tenant=tenants[0], status=RackStatusChoices.STATUS_ACTIVE, role=rack_roles[0], serial='ABC', asset_tag='1001', type=RackTypeChoices.TYPE_2POST, width=RackWidthChoices.WIDTH_19IN, u_height=42, desc_units=False, outer_width=100, outer_depth=100, outer_unit=RackDimensionUnitChoices.UNIT_MILLIMETER, weight=10, weight_unit=WeightUnitChoices.UNIT_POUND),
|
Rack(name='Rack 1', facility_id='rack-1', site=sites[0], location=locations[0], tenant=tenants[0], status=RackStatusChoices.STATUS_ACTIVE, role=rack_roles[0], serial='ABC', asset_tag='1001', type=RackTypeChoices.TYPE_2POST, width=RackWidthChoices.WIDTH_19IN, u_height=42, desc_units=False, outer_width=100, outer_depth=100, outer_unit=RackDimensionUnitChoices.UNIT_MILLIMETER, weight=10, max_weight=1000, weight_unit=WeightUnitChoices.UNIT_POUND),
|
||||||
Rack(name='Rack 2', facility_id='rack-2', site=sites[1], location=locations[1], tenant=tenants[1], status=RackStatusChoices.STATUS_PLANNED, role=rack_roles[1], serial='DEF', asset_tag='1002', type=RackTypeChoices.TYPE_4POST, width=RackWidthChoices.WIDTH_21IN, u_height=43, desc_units=False, outer_width=200, outer_depth=200, outer_unit=RackDimensionUnitChoices.UNIT_MILLIMETER, weight=20, weight_unit=WeightUnitChoices.UNIT_POUND),
|
Rack(name='Rack 2', facility_id='rack-2', site=sites[1], location=locations[1], tenant=tenants[1], status=RackStatusChoices.STATUS_PLANNED, role=rack_roles[1], serial='DEF', asset_tag='1002', type=RackTypeChoices.TYPE_4POST, width=RackWidthChoices.WIDTH_21IN, u_height=43, desc_units=False, outer_width=200, outer_depth=200, outer_unit=RackDimensionUnitChoices.UNIT_MILLIMETER, weight=20, max_weight=2000, weight_unit=WeightUnitChoices.UNIT_POUND),
|
||||||
Rack(name='Rack 3', facility_id='rack-3', site=sites[2], location=locations[2], tenant=tenants[2], status=RackStatusChoices.STATUS_RESERVED, role=rack_roles[2], serial='GHI', asset_tag='1003', type=RackTypeChoices.TYPE_CABINET, width=RackWidthChoices.WIDTH_23IN, u_height=44, desc_units=True, outer_width=300, outer_depth=300, outer_unit=RackDimensionUnitChoices.UNIT_INCH, weight=30, weight_unit=WeightUnitChoices.UNIT_KILOGRAM),
|
Rack(name='Rack 3', facility_id='rack-3', site=sites[2], location=locations[2], tenant=tenants[2], status=RackStatusChoices.STATUS_RESERVED, role=rack_roles[2], serial='GHI', asset_tag='1003', type=RackTypeChoices.TYPE_CABINET, width=RackWidthChoices.WIDTH_23IN, u_height=44, desc_units=True, outer_width=300, outer_depth=300, outer_unit=RackDimensionUnitChoices.UNIT_INCH, weight=30, max_weight=3000, weight_unit=WeightUnitChoices.UNIT_KILOGRAM),
|
||||||
)
|
)
|
||||||
Rack.objects.bulk_create(racks)
|
Rack.objects.bulk_create(racks)
|
||||||
|
|
||||||
@ -521,6 +521,10 @@ class RackTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
params = {'weight': [10, 20]}
|
params = {'weight': [10, 20]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_max_weight(self):
|
||||||
|
params = {'max_weight': [1000, 2000]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_weight_unit(self):
|
def test_weight_unit(self):
|
||||||
params = {'weight_unit': WeightUnitChoices.UNIT_POUND}
|
params = {'weight_unit': WeightUnitChoices.UNIT_POUND}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
@ -388,15 +388,18 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
'outer_width': 500,
|
'outer_width': 500,
|
||||||
'outer_depth': 500,
|
'outer_depth': 500,
|
||||||
'outer_unit': RackDimensionUnitChoices.UNIT_MILLIMETER,
|
'outer_unit': RackDimensionUnitChoices.UNIT_MILLIMETER,
|
||||||
|
'weight': 100,
|
||||||
|
'max_weight': 2000,
|
||||||
|
'weight_unit': WeightUnitChoices.UNIT_POUND,
|
||||||
'comments': 'Some comments',
|
'comments': 'Some comments',
|
||||||
'tags': [t.pk for t in tags],
|
'tags': [t.pk for t in tags],
|
||||||
}
|
}
|
||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
"site,location,name,status,width,u_height",
|
"site,location,name,status,width,u_height,weight,max_weight,weight_unit",
|
||||||
"Site 1,,Rack 4,active,19,42",
|
"Site 1,,Rack 4,active,19,42,100,2000,kg",
|
||||||
"Site 1,Location 1,Rack 5,active,19,42",
|
"Site 1,Location 1,Rack 5,active,19,42,100,2000,kg",
|
||||||
"Site 2,Location 2,Rack 6,active,19,42",
|
"Site 2,Location 2,Rack 6,active,19,42,100,2000,kg",
|
||||||
)
|
)
|
||||||
|
|
||||||
cls.csv_update_data = (
|
cls.csv_update_data = (
|
||||||
@ -420,6 +423,9 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
'outer_width': 30,
|
'outer_width': 30,
|
||||||
'outer_depth': 30,
|
'outer_depth': 30,
|
||||||
'outer_unit': RackDimensionUnitChoices.UNIT_INCH,
|
'outer_unit': RackDimensionUnitChoices.UNIT_INCH,
|
||||||
|
'weight': 200,
|
||||||
|
'max_weight': 4000,
|
||||||
|
'weight_unit': WeightUnitChoices.UNIT_POUND,
|
||||||
'comments': 'New comments',
|
'comments': 'New comments',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,6 +169,16 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Maximum Weight</th>
|
||||||
|
<td>
|
||||||
|
{% if object.max_weight %}
|
||||||
|
{{ object.max_weight }} {{ object.get_weight_unit_display }}
|
||||||
|
{% else %}
|
||||||
|
{{ ''|placeholder }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Total Weight</th>
|
<th scope="row">Total Weight</th>
|
||||||
<td>
|
<td>
|
||||||
|
@ -58,10 +58,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label class="col col-md-3 col-form-label text-lg-end">Weight</label>
|
<label class="col col-md-3 col-form-label text-lg-end">Weight</label>
|
||||||
<div class="col col-md-6 mb-1">
|
<div class="col col-md-3 mb-1">
|
||||||
{{ form.weight }}
|
{{ form.weight }}
|
||||||
<div class="form-text">Weight</div>
|
<div class="form-text">Weight</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col col-md-3 mb-1">
|
||||||
|
{{ form.max_weight }}
|
||||||
|
<div class="form-text">Maximum Weight</div>
|
||||||
|
</div>
|
||||||
<div class="col col-md-3 mb-1">
|
<div class="col col-md-3 mb-1">
|
||||||
{{ form.weight_unit }}
|
{{ form.weight_unit }}
|
||||||
<div class="form-text">Unit</div>
|
<div class="form-text">Unit</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user