1
0
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:
Jeremy Stretch
2018-11-02 09:51:17 -04:00
parent 4ffe1866c5
commit c60c5502f6
9 changed files with 111 additions and 11 deletions

View File

@ -7,6 +7,7 @@ v2.5.0 (FUTURE)
## Enhancements ## 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 * [#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 * [#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 * [#2104](https://github.com/digitalocean/netbox/issues/2104) - Added a `status` field for racks

View File

@ -121,14 +121,15 @@ class RackSerializer(TaggitSerializer, CustomFieldModelSerializer):
role = NestedRackRoleSerializer(required=False, allow_null=True) role = NestedRackRoleSerializer(required=False, allow_null=True)
type = ChoiceField(choices=RACK_TYPE_CHOICES, required=False, allow_null=True) type = ChoiceField(choices=RACK_TYPE_CHOICES, required=False, allow_null=True)
width = ChoiceField(choices=RACK_WIDTH_CHOICES, required=False) width = ChoiceField(choices=RACK_WIDTH_CHOICES, required=False)
outer_unit = ChoiceField(choices=RACK_DIMENSION_UNIT_CHOICES, required=False)
tags = TagListSerializerField(required=False) tags = TagListSerializerField(required=False)
class Meta: class Meta:
model = Rack model = Rack
fields = [ fields = [
'id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'status', 'role', 'serial', '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', 'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit',
'last_updated', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
] ]
# Omit the UniqueTogetherValidator that would be automatically added to validate (group, facility_id). This # Omit the UniqueTogetherValidator that would be automatically added to validate (group, facility_id). This
# prevents facility_id from being interpreted as a required field. # 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_a = serializers.SerializerMethodField(read_only=True)
termination_b = serializers.SerializerMethodField(read_only=True) termination_b = serializers.SerializerMethodField(read_only=True)
status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, required=False) 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: class Meta:
model = Cable model = Cable

View File

@ -362,11 +362,16 @@ COMPATIBLE_TERMINATION_TYPES = {
LENGTH_UNIT_METER = 'm' LENGTH_UNIT_METER = 'm'
LENGTH_UNIT_CENTIMETER = 'cm' LENGTH_UNIT_CENTIMETER = 'cm'
LENGTH_UNIT_MILLIMETER = 'mm'
LENGTH_UNIT_FOOT = 'ft' LENGTH_UNIT_FOOT = 'ft'
LENGTH_UNIT_INCH = 'in' LENGTH_UNIT_INCH = 'in'
LENGTH_UNIT_CHOICES = ( CABLE_LENGTH_UNIT_CHOICES = (
(LENGTH_UNIT_METER, 'Meters'), (LENGTH_UNIT_METER, 'Meters'),
(LENGTH_UNIT_CENTIMETER, 'Centimeters'), (LENGTH_UNIT_CENTIMETER, 'Centimeters'),
(LENGTH_UNIT_FOOT, 'Feet'), (LENGTH_UNIT_FOOT, 'Feet'),
(LENGTH_UNIT_INCH, 'Inches'), (LENGTH_UNIT_INCH, 'Inches'),
) )
RACK_DIMENSION_UNIT_CHOICES = (
(LENGTH_UNIT_MILLIMETER, 'Millimeters'),
(LENGTH_UNIT_INCH, 'Inches'),
)

View File

@ -201,7 +201,10 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
class Meta: class Meta:
model = Rack 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): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():

View File

@ -307,7 +307,7 @@ class RackForm(BootstrapMixin, TenancyForm, CustomFieldForm):
model = Rack model = Rack
fields = [ fields = [
'site', 'group', 'name', 'facility_id', 'tenant_group', 'tenant', 'status', 'role', 'serial', 'asset_tag', '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 = { help_texts = {
'site': "The site at which the rack exists", 'site': "The site at which the rack exists",
@ -368,6 +368,11 @@ class RackCSVForm(forms.ModelForm):
), ),
help_text='Rail-to-rail width (in inches)' 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: class Meta:
model = Rack model = Rack
@ -458,12 +463,26 @@ class RackBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor
widget=BulkEditNullBooleanSelect, widget=BulkEditNullBooleanSelect,
label='Descending units' 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( comments = CommentField(
widget=SmallTextarea widget=SmallTextarea
) )
class Meta: 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): class RackFilterForm(BootstrapMixin, CustomFieldFilterForm):
@ -1864,7 +1883,7 @@ class CableCSVForm(forms.ModelForm):
help_text='Cable type' help_text='Cable type'
) )
length_unit = CSVChoiceField( length_unit = CSVChoiceField(
choices=LENGTH_UNIT_CHOICES, choices=CABLE_LENGTH_UNIT_CHOICES,
required=False, required=False,
help_text='Length unit' help_text='Length unit'
) )
@ -1962,7 +1981,7 @@ class CableBulkEditForm(BootstrapMixin, BulkEditForm):
required=False required=False
) )
length_unit = forms.ChoiceField( length_unit = forms.ChoiceField(
choices=add_blank_choice(LENGTH_UNIT_CHOICES), choices=add_blank_choice(CABLE_LENGTH_UNIT_CHOICES),
required=False, required=False,
initial='' initial=''
) )

View File

@ -20,4 +20,19 @@ class Migration(migrations.Migration):
name='asset_tag', name='asset_tag',
field=utilities.fields.NullableCharField(blank=True, max_length=50, null=True, unique=True), 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),
),
] ]

View File

@ -510,6 +510,19 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
verbose_name='Descending units', verbose_name='Descending units',
help_text='Units are numbered top-to-bottom' 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( comments = models.TextField(
blank=True blank=True
) )
@ -527,7 +540,7 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
csv_headers = [ csv_headers = [
'site', 'group_name', 'name', 'facility_id', 'tenant', 'status', 'role', 'type', 'serial', 'asset_tag', 'width', '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: class Meta:
@ -545,6 +558,14 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
def clean(self): 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: 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(rack=self).exclude(position__isnull=True).order_by('-position').first() 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.width,
self.u_height, self.u_height,
self.desc_units, self.desc_units,
self.outer_width,
self.outer_depth,
self.outer_unit,
self.comments, self.comments,
) )
@ -2410,7 +2434,7 @@ class Cable(ChangeLoggedModel):
null=True null=True
) )
length_unit = models.CharField( length_unit = models.CharField(
choices=LENGTH_UNIT_CHOICES, choices=CABLE_LENGTH_UNIT_CHOICES,
max_length=2, max_length=2,
blank=True blank=True
) )

View File

@ -172,6 +172,26 @@
<td>Height</td> <td>Height</td>
<td>{{ rack.u_height }}U ({% if rack.desc_units %}descending{% else %}ascending{% endif %})</td> <td>{{ rack.u_height }}U ({% if rack.desc_units %}descending{% else %}ascending{% endif %})</td>
</tr> </tr>
<tr>
<td>Outer Width</td>
<td>
{% if rack.outer_width %}
<span>{{ rack.outer_width }}{{ rack.outer_unit }}</span>
{% else %}
<span class="text-muted">&mdash;</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">&mdash;</span>
{% endif %}
</td>
</tr>
</table> </table>
</div> </div>
{% include 'inc/custom_fields_panel.html' with obj=rack %} {% include 'inc/custom_fields_panel.html' with obj=rack %}

View File

@ -28,6 +28,18 @@
{% render_field form.type %} {% render_field form.type %}
{% render_field form.width %} {% render_field form.width %}
{% render_field form.u_height %} {% 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 %} {% render_field form.desc_units %}
</div> </div>
</div> </div>