mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Closes #450: Add 'outer_width' and 'outer_depth' fields to Rack
This commit is contained in:
@ -7,6 +7,7 @@ v2.5.0 (FUTURE)
|
||||
|
||||
## Enhancements
|
||||
|
||||
* [#450](https://github.com/digitalocean/netbox/issues/450) - Added `outer_width` and `outer_depth` fields to rack model
|
||||
* [#1444](https://github.com/digitalocean/netbox/issues/1444) - Added an `asset_tag` field for racks
|
||||
* [#2000](https://github.com/digitalocean/netbox/issues/2000) - Dropped support for Python 2
|
||||
* [#2104](https://github.com/digitalocean/netbox/issues/2104) - Added a `status` field for racks
|
||||
|
@ -121,14 +121,15 @@ class RackSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
role = NestedRackRoleSerializer(required=False, allow_null=True)
|
||||
type = ChoiceField(choices=RACK_TYPE_CHOICES, required=False, allow_null=True)
|
||||
width = ChoiceField(choices=RACK_WIDTH_CHOICES, required=False)
|
||||
outer_unit = ChoiceField(choices=RACK_DIMENSION_UNIT_CHOICES, required=False)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = Rack
|
||||
fields = [
|
||||
'id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'status', 'role', 'serial',
|
||||
'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'comments', 'tags', 'custom_fields', 'created',
|
||||
'last_updated',
|
||||
'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit',
|
||||
'comments', 'tags', 'custom_fields', 'created', 'last_updated',
|
||||
]
|
||||
# Omit the UniqueTogetherValidator that would be automatically added to validate (group, facility_id). This
|
||||
# prevents facility_id from being interpreted as a required field.
|
||||
@ -504,7 +505,7 @@ class CableSerializer(ValidatedModelSerializer):
|
||||
termination_a = serializers.SerializerMethodField(read_only=True)
|
||||
termination_b = serializers.SerializerMethodField(read_only=True)
|
||||
status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, required=False)
|
||||
length_unit = ChoiceField(choices=LENGTH_UNIT_CHOICES, required=False)
|
||||
length_unit = ChoiceField(choices=CABLE_LENGTH_UNIT_CHOICES, required=False)
|
||||
|
||||
class Meta:
|
||||
model = Cable
|
||||
|
@ -362,11 +362,16 @@ COMPATIBLE_TERMINATION_TYPES = {
|
||||
|
||||
LENGTH_UNIT_METER = 'm'
|
||||
LENGTH_UNIT_CENTIMETER = 'cm'
|
||||
LENGTH_UNIT_MILLIMETER = 'mm'
|
||||
LENGTH_UNIT_FOOT = 'ft'
|
||||
LENGTH_UNIT_INCH = 'in'
|
||||
LENGTH_UNIT_CHOICES = (
|
||||
CABLE_LENGTH_UNIT_CHOICES = (
|
||||
(LENGTH_UNIT_METER, 'Meters'),
|
||||
(LENGTH_UNIT_CENTIMETER, 'Centimeters'),
|
||||
(LENGTH_UNIT_FOOT, 'Feet'),
|
||||
(LENGTH_UNIT_INCH, 'Inches'),
|
||||
)
|
||||
RACK_DIMENSION_UNIT_CHOICES = (
|
||||
(LENGTH_UNIT_MILLIMETER, 'Millimeters'),
|
||||
(LENGTH_UNIT_INCH, 'Inches'),
|
||||
)
|
||||
|
@ -201,7 +201,10 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
|
||||
class Meta:
|
||||
model = Rack
|
||||
fields = ['name', 'serial', 'asset_tag', 'type', 'width', 'u_height', 'desc_units']
|
||||
fields = [
|
||||
'name', 'serial', 'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth',
|
||||
'outer_unit',
|
||||
]
|
||||
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
|
@ -307,7 +307,7 @@ class RackForm(BootstrapMixin, TenancyForm, CustomFieldForm):
|
||||
model = Rack
|
||||
fields = [
|
||||
'site', 'group', 'name', 'facility_id', 'tenant_group', 'tenant', 'status', 'role', 'serial', 'asset_tag',
|
||||
'type', 'width', 'u_height', 'desc_units', 'comments', 'tags',
|
||||
'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'comments', 'tags',
|
||||
]
|
||||
help_texts = {
|
||||
'site': "The site at which the rack exists",
|
||||
@ -368,6 +368,11 @@ class RackCSVForm(forms.ModelForm):
|
||||
),
|
||||
help_text='Rail-to-rail width (in inches)'
|
||||
)
|
||||
outer_unit = CSVChoiceField(
|
||||
choices=RACK_DIMENSION_UNIT_CHOICES,
|
||||
required=False,
|
||||
help_text='Unit for outer dimensions'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Rack
|
||||
@ -458,12 +463,26 @@ class RackBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor
|
||||
widget=BulkEditNullBooleanSelect,
|
||||
label='Descending units'
|
||||
)
|
||||
outer_width = forms.IntegerField(
|
||||
required=False,
|
||||
min_value=1
|
||||
)
|
||||
outer_depth = forms.IntegerField(
|
||||
required=False,
|
||||
min_value=1
|
||||
)
|
||||
outer_unit = forms.ChoiceField(
|
||||
choices=add_blank_choice(RACK_DIMENSION_UNIT_CHOICES),
|
||||
required=False
|
||||
)
|
||||
comments = CommentField(
|
||||
widget=SmallTextarea
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = ['group', 'tenant', 'role', 'serial', 'asset_tag', 'comments']
|
||||
nullable_fields = [
|
||||
'group', 'tenant', 'role', 'serial', 'asset_tag', 'outer_width', 'outer_depth', 'outer_unit', 'comments',
|
||||
]
|
||||
|
||||
|
||||
class RackFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
@ -1864,7 +1883,7 @@ class CableCSVForm(forms.ModelForm):
|
||||
help_text='Cable type'
|
||||
)
|
||||
length_unit = CSVChoiceField(
|
||||
choices=LENGTH_UNIT_CHOICES,
|
||||
choices=CABLE_LENGTH_UNIT_CHOICES,
|
||||
required=False,
|
||||
help_text='Length unit'
|
||||
)
|
||||
@ -1962,7 +1981,7 @@ class CableBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||
required=False
|
||||
)
|
||||
length_unit = forms.ChoiceField(
|
||||
choices=add_blank_choice(LENGTH_UNIT_CHOICES),
|
||||
choices=add_blank_choice(CABLE_LENGTH_UNIT_CHOICES),
|
||||
required=False,
|
||||
initial=''
|
||||
)
|
||||
|
@ -20,4 +20,19 @@ class Migration(migrations.Migration):
|
||||
name='asset_tag',
|
||||
field=utilities.fields.NullableCharField(blank=True, max_length=50, null=True, unique=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='outer_depth',
|
||||
field=models.PositiveSmallIntegerField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='outer_unit',
|
||||
field=models.CharField(blank=True, max_length=2),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rack',
|
||||
name='outer_width',
|
||||
field=models.PositiveSmallIntegerField(blank=True, null=True),
|
||||
),
|
||||
]
|
||||
|
@ -510,6 +510,19 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
|
||||
verbose_name='Descending units',
|
||||
help_text='Units are numbered top-to-bottom'
|
||||
)
|
||||
outer_width = models.PositiveSmallIntegerField(
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
outer_depth = models.PositiveSmallIntegerField(
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
outer_unit = models.CharField(
|
||||
choices=RACK_DIMENSION_UNIT_CHOICES,
|
||||
max_length=2,
|
||||
blank=True
|
||||
)
|
||||
comments = models.TextField(
|
||||
blank=True
|
||||
)
|
||||
@ -527,7 +540,7 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
|
||||
|
||||
csv_headers = [
|
||||
'site', 'group_name', 'name', 'facility_id', 'tenant', 'status', 'role', 'type', 'serial', 'asset_tag', 'width',
|
||||
'u_height', 'desc_units', 'comments',
|
||||
'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'comments',
|
||||
]
|
||||
|
||||
class Meta:
|
||||
@ -545,6 +558,14 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
|
||||
|
||||
def clean(self):
|
||||
|
||||
# Validate outer dimensions and unit
|
||||
if self.outer_width and not self.outer_unit:
|
||||
raise ValidationError("Must specify a unit when setting an outer width")
|
||||
if self.outer_depth and not self.outer_unit:
|
||||
raise ValidationError("Must specify a unit when setting an outer depth")
|
||||
if self.outer_unit and self.outer_width is None and self.outer_depth is None:
|
||||
self.length_unit = ''
|
||||
|
||||
if self.pk:
|
||||
# Validate that Rack is tall enough to house the installed Devices
|
||||
top_device = Device.objects.filter(rack=self).exclude(position__isnull=True).order_by('-position').first()
|
||||
@ -591,6 +612,9 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
|
||||
self.width,
|
||||
self.u_height,
|
||||
self.desc_units,
|
||||
self.outer_width,
|
||||
self.outer_depth,
|
||||
self.outer_unit,
|
||||
self.comments,
|
||||
)
|
||||
|
||||
@ -2410,7 +2434,7 @@ class Cable(ChangeLoggedModel):
|
||||
null=True
|
||||
)
|
||||
length_unit = models.CharField(
|
||||
choices=LENGTH_UNIT_CHOICES,
|
||||
choices=CABLE_LENGTH_UNIT_CHOICES,
|
||||
max_length=2,
|
||||
blank=True
|
||||
)
|
||||
|
@ -172,6 +172,26 @@
|
||||
<td>Height</td>
|
||||
<td>{{ rack.u_height }}U ({% if rack.desc_units %}descending{% else %}ascending{% endif %})</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Outer Width</td>
|
||||
<td>
|
||||
{% if rack.outer_width %}
|
||||
<span>{{ rack.outer_width }}{{ rack.outer_unit }}</span>
|
||||
{% else %}
|
||||
<span class="text-muted">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Outer Depth</td>
|
||||
<td>
|
||||
{% if rack.outer_depth %}
|
||||
<span>{{ rack.outer_depth }}{{ rack.outer_unit }}</span>
|
||||
{% else %}
|
||||
<span class="text-muted">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% include 'inc/custom_fields_panel.html' with obj=rack %}
|
||||
|
@ -28,6 +28,18 @@
|
||||
{% render_field form.type %}
|
||||
{% render_field form.width %}
|
||||
{% render_field form.u_height %}
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label">Outer dimensions</label>
|
||||
<div class="col-md-3">
|
||||
{{ form.outer_width }}
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
{{ form.outer_depth }}
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
{{ form.outer_unit }}
|
||||
</div>
|
||||
</div>
|
||||
{% render_field form.desc_units %}
|
||||
</div>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user