mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-02 05:46:25 -06:00
16547 Add distance to Circuit
This commit is contained in:
parent
85396866bc
commit
79e51084bf
@ -0,0 +1,28 @@
|
||||
# Generated by Django 5.0.9 on 2024-09-26 22:14
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('circuits', '0044_circuit_groups'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='circuit',
|
||||
name='_abs_distance',
|
||||
field=models.DecimalField(blank=True, decimal_places=4, max_digits=10, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='circuit',
|
||||
name='distance',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='circuit',
|
||||
name='distance_unit',
|
||||
field=models.CharField(blank=True, max_length=50),
|
||||
),
|
||||
]
|
@ -6,6 +6,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from circuits.choices import *
|
||||
from dcim.models import CabledObjectModel
|
||||
from netbox.models import ChangeLoggedModel, OrganizationalModel, PrimaryModel
|
||||
from netbox.models.mixins import DistanceMixin
|
||||
from netbox.models.features import ContactsMixin, CustomFieldsMixin, CustomLinksMixin, ExportTemplatesMixin, ImageAttachmentsMixin, TagsMixin
|
||||
from utilities.fields import ColorField
|
||||
|
||||
@ -37,7 +38,7 @@ class CircuitType(OrganizationalModel):
|
||||
verbose_name_plural = _('circuit types')
|
||||
|
||||
|
||||
class Circuit(ContactsMixin, ImageAttachmentsMixin, PrimaryModel):
|
||||
class Circuit(ContactsMixin, ImageAttachmentsMixin, DistanceMixin, PrimaryModel):
|
||||
"""
|
||||
A communications circuit connects two points. Each Circuit belongs to a Provider; Providers may have multiple
|
||||
circuits. Each circuit is also assigned a CircuitType and a Site, and may optionally be assigned to a particular
|
||||
|
@ -7,6 +7,7 @@ from dcim.choices import *
|
||||
from dcim.models import DeviceType, ModuleType
|
||||
from netbox.api.fields import ChoiceField, RelatedObjectCountField
|
||||
from netbox.api.serializers import NetBoxModelSerializer
|
||||
from netbox.choices import *
|
||||
from .manufacturers import ManufacturerSerializer
|
||||
from .platforms import PlatformSerializer
|
||||
|
||||
|
@ -6,6 +6,7 @@ from dcim.constants import *
|
||||
from dcim.models import Rack, RackReservation, RackRole, RackType
|
||||
from netbox.api.fields import ChoiceField, RelatedObjectCountField
|
||||
from netbox.api.serializers import NetBoxModelSerializer
|
||||
from netbox.choices import *
|
||||
from netbox.config import ConfigItem
|
||||
from tenancy.api.serializers_.tenants import TenantSerializer
|
||||
from users.api.serializers_.users import UserSerializer
|
||||
|
@ -1546,24 +1546,6 @@ class CableLengthUnitChoices(ChoiceSet):
|
||||
)
|
||||
|
||||
|
||||
class WeightUnitChoices(ChoiceSet):
|
||||
|
||||
# Metric
|
||||
UNIT_KILOGRAM = 'kg'
|
||||
UNIT_GRAM = 'g'
|
||||
|
||||
# Imperial
|
||||
UNIT_POUND = 'lb'
|
||||
UNIT_OUNCE = 'oz'
|
||||
|
||||
CHOICES = (
|
||||
(UNIT_KILOGRAM, _('Kilograms')),
|
||||
(UNIT_GRAM, _('Grams')),
|
||||
(UNIT_POUND, _('Pounds')),
|
||||
(UNIT_OUNCE, _('Ounces')),
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# CableTerminations
|
||||
#
|
||||
|
@ -8,6 +8,7 @@ from dcim.constants import *
|
||||
from dcim.models import *
|
||||
from extras.models import ConfigTemplate
|
||||
from ipam.models import ASN, VLAN, VLANGroup, VRF
|
||||
from netbox.choices import *
|
||||
from netbox.forms import NetBoxModelBulkEditForm
|
||||
from tenancy.models import Tenant
|
||||
from users.models import User
|
||||
|
@ -10,6 +10,7 @@ from dcim.constants import *
|
||||
from dcim.models import *
|
||||
from extras.models import ConfigTemplate
|
||||
from ipam.models import VRF, IPAddress
|
||||
from netbox.choices import *
|
||||
from netbox.forms import NetBoxModelImportForm
|
||||
from tenancy.models import Tenant
|
||||
from utilities.forms.fields import (
|
||||
|
@ -7,6 +7,7 @@ from dcim.models import *
|
||||
from extras.forms import LocalConfigContextFilterForm
|
||||
from extras.models import ConfigTemplate
|
||||
from ipam.models import ASN, VRF
|
||||
from netbox.choices import *
|
||||
from netbox.forms import NetBoxModelFilterSetForm
|
||||
from tenancy.forms import ContactModelFilterForm, TenancyFilterForm
|
||||
from users.models import User
|
||||
|
@ -21,11 +21,12 @@ from extras.querysets import ConfigContextModelQuerySet
|
||||
from netbox.choices import ColorChoices
|
||||
from netbox.config import ConfigItem
|
||||
from netbox.models import OrganizationalModel, PrimaryModel
|
||||
from netbox.models.mixins import WeightMixin
|
||||
from netbox.models.features import ContactsMixin, ImageAttachmentsMixin
|
||||
from utilities.fields import ColorField, CounterCacheField, NaturalOrderingField
|
||||
from utilities.tracking import TrackingModelMixin
|
||||
from .device_components import *
|
||||
from .mixins import RenderConfigMixin, WeightMixin
|
||||
from .mixins import RenderConfigMixin
|
||||
|
||||
|
||||
__all__ = (
|
||||
|
@ -1,56 +1,12 @@
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from dcim.choices import *
|
||||
from utilities.conversion import to_grams
|
||||
|
||||
__all__ = (
|
||||
'RenderConfigMixin',
|
||||
'WeightMixin',
|
||||
)
|
||||
|
||||
|
||||
class WeightMixin(models.Model):
|
||||
weight = models.DecimalField(
|
||||
verbose_name=_('weight'),
|
||||
max_digits=8,
|
||||
decimal_places=2,
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
weight_unit = models.CharField(
|
||||
verbose_name=_('weight unit'),
|
||||
max_length=50,
|
||||
choices=WeightUnitChoices,
|
||||
blank=True,
|
||||
)
|
||||
# Stores the normalized weight (in grams) for database ordering
|
||||
_abs_weight = models.PositiveBigIntegerField(
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
# Store the given weight (if any) in grams for use in database ordering
|
||||
if self.weight and self.weight_unit:
|
||||
self._abs_weight = to_grams(self.weight, self.weight_unit)
|
||||
else:
|
||||
self._abs_weight = None
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
|
||||
# Validate weight and weight_unit
|
||||
if self.weight and not self.weight_unit:
|
||||
raise ValidationError(_("Must specify a unit when setting a weight"))
|
||||
|
||||
|
||||
class RenderConfigMixin(models.Model):
|
||||
config_template = models.ForeignKey(
|
||||
to='extras.ConfigTemplate',
|
||||
|
@ -16,13 +16,13 @@ from dcim.constants import *
|
||||
from dcim.svg import RackElevationSVG
|
||||
from netbox.choices import ColorChoices
|
||||
from netbox.models import OrganizationalModel, PrimaryModel
|
||||
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, NaturalOrderingField
|
||||
from .device_components import PowerPort
|
||||
from .devices import Device, Module
|
||||
from .mixins import WeightMixin
|
||||
from .power import PowerFeed
|
||||
|
||||
__all__ = (
|
||||
|
@ -7,8 +7,10 @@ __all__ = (
|
||||
'ButtonColorChoices',
|
||||
'ColorChoices',
|
||||
'CSVDelimiterChoices',
|
||||
'DistanceUnitChoices',
|
||||
'ImportFormatChoices',
|
||||
'ImportMethodChoices',
|
||||
'WeightUnitChoices',
|
||||
)
|
||||
|
||||
|
||||
@ -157,3 +159,39 @@ class CSVDelimiterChoices(ChoiceSet):
|
||||
(SEMICOLON, _('Semicolon')),
|
||||
(TAB, _('Tab')),
|
||||
]
|
||||
|
||||
|
||||
class DistanceUnitChoices(ChoiceSet):
|
||||
|
||||
# Metric
|
||||
UNIT_KILOMETER = 'km'
|
||||
UNIT_METER = 'm'
|
||||
|
||||
# Imperial
|
||||
UNIT_MILE = 'mi'
|
||||
UNIT_FOOT = 'ft'
|
||||
|
||||
CHOICES = (
|
||||
(UNIT_KILOMETER, _('Kilometers')),
|
||||
(UNIT_METER, _('Meters')),
|
||||
(UNIT_MILE, _('Miles')),
|
||||
(UNIT_FOOT, _('Feet')),
|
||||
)
|
||||
|
||||
|
||||
class WeightUnitChoices(ChoiceSet):
|
||||
|
||||
# Metric
|
||||
UNIT_KILOGRAM = 'kg'
|
||||
UNIT_GRAM = 'g'
|
||||
|
||||
# Imperial
|
||||
UNIT_POUND = 'lb'
|
||||
UNIT_OUNCE = 'oz'
|
||||
|
||||
CHOICES = (
|
||||
(UNIT_KILOGRAM, _('Kilograms')),
|
||||
(UNIT_GRAM, _('Grams')),
|
||||
(UNIT_POUND, _('Pounds')),
|
||||
(UNIT_OUNCE, _('Ounces')),
|
||||
)
|
||||
|
97
netbox/netbox/models/mixins.py
Normal file
97
netbox/netbox/models/mixins.py
Normal file
@ -0,0 +1,97 @@
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from netbox.choices import *
|
||||
from utilities.conversion import to_grams
|
||||
|
||||
__all__ = (
|
||||
'DistanceMixin',
|
||||
'WeightMixin',
|
||||
)
|
||||
|
||||
|
||||
class WeightMixin(models.Model):
|
||||
weight = models.DecimalField(
|
||||
verbose_name=_('weight'),
|
||||
max_digits=8,
|
||||
decimal_places=2,
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
weight_unit = models.CharField(
|
||||
verbose_name=_('weight unit'),
|
||||
max_length=50,
|
||||
choices=WeightUnitChoices,
|
||||
blank=True,
|
||||
)
|
||||
# Stores the normalized weight (in grams) for database ordering
|
||||
_abs_weight = models.PositiveBigIntegerField(
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
# Store the given weight (if any) in grams for use in database ordering
|
||||
if self.weight and self.weight_unit:
|
||||
self._abs_weight = to_grams(self.weight, self.weight_unit)
|
||||
else:
|
||||
self._abs_weight = None
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
|
||||
# Validate weight and weight_unit
|
||||
if self.weight and not self.weight_unit:
|
||||
raise ValidationError(_("Must specify a unit when setting a weight"))
|
||||
|
||||
|
||||
class DistanceMixin(models.Model):
|
||||
distance = models.DecimalField(
|
||||
verbose_name=_('distance'),
|
||||
max_digits=8,
|
||||
decimal_places=2,
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
distance_unit = models.CharField(
|
||||
verbose_name=_('distance unit'),
|
||||
max_length=50,
|
||||
choices=DistanceUnitChoices,
|
||||
blank=True,
|
||||
)
|
||||
# Stores the normalized distance (in meters) for database ordering
|
||||
_abs_distance = models.DecimalField(
|
||||
max_digits=10,
|
||||
decimal_places=4,
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# Store the given distance (if any) in meters for use in database ordering
|
||||
if self.distance is not None and self.distance_unit:
|
||||
self._abs_distance = to_meters(self.distance, self.distance_unit)
|
||||
else:
|
||||
self._abs_distance = None
|
||||
|
||||
# Clear distance_unit if no distance is defined
|
||||
if self.distance is None:
|
||||
self.distance_unit = ''
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
|
||||
# Validate distance and distance_unit
|
||||
if self.distance and not self.distance_unit:
|
||||
raise ValidationError(_("Must specify a unit when setting a distance"))
|
@ -2,7 +2,8 @@ from decimal import Decimal
|
||||
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from dcim.choices import CableLengthUnitChoices, WeightUnitChoices
|
||||
from dcim.choices import CableLengthUnitChoices
|
||||
from netbox.choices import WeightUnitChoices
|
||||
|
||||
__all__ = (
|
||||
'to_grams',
|
||||
|
@ -4,6 +4,7 @@ from dcim.api.serializers_.device_components import InterfaceSerializer
|
||||
from dcim.choices import LinkStatusChoices
|
||||
from netbox.api.fields import ChoiceField
|
||||
from netbox.api.serializers import NetBoxModelSerializer
|
||||
from netbox.choices import *
|
||||
from tenancy.api.serializers_.tenants import TenantSerializer
|
||||
from wireless.choices import *
|
||||
from wireless.models import WirelessLink
|
||||
@ -20,7 +21,7 @@ class WirelessLinkSerializer(NetBoxModelSerializer):
|
||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||
auth_type = ChoiceField(choices=WirelessAuthTypeChoices, required=False, allow_blank=True)
|
||||
auth_cipher = ChoiceField(choices=WirelessAuthCipherChoices, required=False, allow_blank=True)
|
||||
distance_unit = ChoiceField(choices=WirelessLinkDistanceUnitChoices, allow_blank=True, required=False, allow_null=True)
|
||||
distance_unit = ChoiceField(choices=DistanceUnitChoices, allow_blank=True, required=False, allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = WirelessLink
|
||||
|
@ -481,21 +481,3 @@ class WirelessAuthCipherChoices(ChoiceSet):
|
||||
(CIPHER_TKIP, 'TKIP'),
|
||||
(CIPHER_AES, 'AES'),
|
||||
)
|
||||
|
||||
|
||||
class WirelessLinkDistanceUnitChoices(ChoiceSet):
|
||||
|
||||
# Metric
|
||||
UNIT_KILOMETER = 'km'
|
||||
UNIT_METER = 'm'
|
||||
|
||||
# Imperial
|
||||
UNIT_MILE = 'mi'
|
||||
UNIT_FOOT = 'ft'
|
||||
|
||||
CHOICES = (
|
||||
(UNIT_KILOMETER, _('Kilometers')),
|
||||
(UNIT_METER, _('Meters')),
|
||||
(UNIT_MILE, _('Miles')),
|
||||
(UNIT_FOOT, _('Feet')),
|
||||
)
|
||||
|
@ -3,6 +3,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from dcim.choices import LinkStatusChoices
|
||||
from ipam.models import VLAN
|
||||
from netbox.choices import *
|
||||
from netbox.forms import NetBoxModelBulkEditForm
|
||||
from tenancy.models import Tenant
|
||||
from utilities.forms import add_blank_choice
|
||||
@ -132,7 +133,7 @@ class WirelessLinkBulkEditForm(NetBoxModelBulkEditForm):
|
||||
)
|
||||
distance_unit = forms.ChoiceField(
|
||||
label=_('Distance unit'),
|
||||
choices=add_blank_choice(WirelessLinkDistanceUnitChoices),
|
||||
choices=add_blank_choice(DistanceUnitChoices),
|
||||
required=False,
|
||||
initial=''
|
||||
)
|
||||
|
@ -3,6 +3,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from dcim.choices import LinkStatusChoices
|
||||
from dcim.models import Interface
|
||||
from ipam.models import VLAN
|
||||
from netbox.choices import *
|
||||
from netbox.forms import NetBoxModelImportForm
|
||||
from tenancy.models import Tenant
|
||||
from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField, SlugField
|
||||
@ -114,7 +115,7 @@ class WirelessLinkImportForm(NetBoxModelImportForm):
|
||||
)
|
||||
distance_unit = CSVChoiceField(
|
||||
label=_('Distance unit'),
|
||||
choices=WirelessLinkDistanceUnitChoices,
|
||||
choices=DistanceUnitChoices,
|
||||
required=False,
|
||||
help_text=_('Distance unit')
|
||||
)
|
||||
|
@ -2,6 +2,7 @@ from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from dcim.choices import LinkStatusChoices
|
||||
from netbox.choices import *
|
||||
from netbox.forms import NetBoxModelFilterSetForm
|
||||
from tenancy.forms import TenancyFilterForm
|
||||
from utilities.forms import add_blank_choice
|
||||
@ -104,7 +105,7 @@ class WirelessLinkFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||
)
|
||||
distance_unit = forms.ChoiceField(
|
||||
label=_('Distance unit'),
|
||||
choices=add_blank_choice(WirelessLinkDistanceUnitChoices),
|
||||
choices=add_blank_choice(DistanceUnitChoices),
|
||||
required=False
|
||||
)
|
||||
tag = TagFilterField(model)
|
||||
|
@ -6,6 +6,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from dcim.choices import LinkStatusChoices
|
||||
from dcim.constants import WIRELESS_IFACE_TYPES
|
||||
from netbox.models import NestedGroupModel, PrimaryModel
|
||||
from netbox.models.mixins import DistanceMixin
|
||||
from utilities.conversion import to_meters
|
||||
from .choices import *
|
||||
from .constants import *
|
||||
@ -132,7 +133,7 @@ def get_wireless_interface_types():
|
||||
return {'type__in': WIRELESS_IFACE_TYPES}
|
||||
|
||||
|
||||
class WirelessLink(WirelessAuthenticationBase, PrimaryModel):
|
||||
class WirelessLink(WirelessAuthenticationBase, DistanceMixin, PrimaryModel):
|
||||
"""
|
||||
A point-to-point connection between two wireless Interfaces.
|
||||
"""
|
||||
@ -161,26 +162,6 @@ class WirelessLink(WirelessAuthenticationBase, PrimaryModel):
|
||||
choices=LinkStatusChoices,
|
||||
default=LinkStatusChoices.STATUS_CONNECTED
|
||||
)
|
||||
distance = models.DecimalField(
|
||||
verbose_name=_('distance'),
|
||||
max_digits=8,
|
||||
decimal_places=2,
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
distance_unit = models.CharField(
|
||||
verbose_name=_('distance unit'),
|
||||
max_length=50,
|
||||
choices=WirelessLinkDistanceUnitChoices,
|
||||
blank=True,
|
||||
)
|
||||
# Stores the normalized distance (in meters) for database ordering
|
||||
_abs_distance = models.DecimalField(
|
||||
max_digits=10,
|
||||
decimal_places=4,
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
tenant = models.ForeignKey(
|
||||
to='tenancy.Tenant',
|
||||
on_delete=models.PROTECT,
|
||||
@ -231,10 +212,6 @@ class WirelessLink(WirelessAuthenticationBase, PrimaryModel):
|
||||
def clean(self):
|
||||
super().clean()
|
||||
|
||||
# Validate distance and distance_unit
|
||||
if self.distance is not None and not self.distance_unit:
|
||||
raise ValidationError(_("Must specify a unit when setting a wireless distance"))
|
||||
|
||||
# Validate interface types
|
||||
if self.interface_a.type not in WIRELESS_IFACE_TYPES:
|
||||
raise ValidationError({
|
||||
@ -250,16 +227,6 @@ class WirelessLink(WirelessAuthenticationBase, PrimaryModel):
|
||||
})
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# Store the given distance (if any) in meters for use in database ordering
|
||||
if self.distance is not None and self.distance_unit:
|
||||
self._abs_distance = to_meters(self.distance, self.distance_unit)
|
||||
else:
|
||||
self._abs_distance = None
|
||||
|
||||
# Clear distance_unit if no distance is defined
|
||||
if self.distance is None:
|
||||
self.distance_unit = ''
|
||||
|
||||
# Store the parent Device for the A and B interfaces
|
||||
self._interface_a_device = self.interface_a.device
|
||||
self._interface_b_device = self.interface_b.device
|
||||
|
Loading…
Reference in New Issue
Block a user