Coverage for netbox/models/mixins.py: 64%
48 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 18:35 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 18:35 +0000
1from django.core.exceptions import ValidationError
2from django.db import models
3from django.utils.translation import gettext_lazy as _
5from netbox.choices import *
6from utilities.conversion import to_grams, to_meters
8__all__ = (
9 'DistanceMixin',
10 'OwnerMixin',
11 'WeightMixin',
12)
15class OwnerMixin(models.Model):
16 """
17 Adds a ForeignKey to users.Owner to indicate an object's owner.
18 """
19 owner = models.ForeignKey(
20 to='users.Owner',
21 on_delete=models.PROTECT,
22 related_name='+',
23 blank=True,
24 null=True
25 )
27 class Meta:
28 abstract = True
31class WeightMixin(models.Model):
32 weight = models.DecimalField(
33 verbose_name=_('weight'),
34 max_digits=8,
35 decimal_places=2,
36 blank=True,
37 null=True
38 )
39 weight_unit = models.CharField(
40 verbose_name=_('weight unit'),
41 max_length=50,
42 choices=WeightUnitChoices,
43 blank=True,
44 null=True,
45 )
46 # Stores the normalized weight (in grams) for database ordering
47 _abs_weight = models.PositiveBigIntegerField(
48 blank=True,
49 null=True
50 )
52 class Meta:
53 abstract = True
55 @property
56 def abs_weight(self):
57 # Public alias for _abs_weight; Django templates cannot access underscore-prefixed attributes.
58 return self._abs_weight
60 def save(self, *args, **kwargs):
62 # Store the given weight (if any) in grams for use in database ordering
63 if self.weight and self.weight_unit: 63 ↛ 64line 63 didn't jump to line 64 because the condition on line 63 was never true
64 self._abs_weight = to_grams(self.weight, self.weight_unit)
65 else:
66 self._abs_weight = None
68 super().save(*args, **kwargs)
70 def clean(self):
71 super().clean()
73 # Validate weight and weight_unit
74 if self.weight and not self.weight_unit: 74 ↛ 75line 74 didn't jump to line 75 because the condition on line 74 was never true
75 raise ValidationError(_("Must specify a unit when setting a weight"))
78class DistanceMixin(models.Model):
79 distance = models.DecimalField(
80 verbose_name=_('distance'),
81 max_digits=8,
82 decimal_places=2,
83 blank=True,
84 null=True,
85 )
86 distance_unit = models.CharField(
87 verbose_name=_('distance unit'),
88 max_length=50,
89 choices=DistanceUnitChoices,
90 blank=True,
91 null=True,
92 )
93 # Stores the normalized distance (in meters) for database ordering
94 _abs_distance = models.DecimalField(
95 max_digits=13,
96 decimal_places=4,
97 blank=True,
98 null=True
99 )
101 class Meta:
102 abstract = True
104 @property
105 def abs_distance(self):
106 # Public alias for _abs_distance; Django templates cannot access underscore-prefixed attributes.
107 return self._abs_distance
109 def save(self, *args, **kwargs):
110 # Store the given distance (if any) in meters for use in database ordering
111 if self.distance is not None and self.distance_unit:
112 self._abs_distance = to_meters(self.distance, self.distance_unit)
113 else:
114 self._abs_distance = None
116 # Clear distance_unit if no distance is defined
117 if self.distance is None:
118 self.distance_unit = None
120 super().save(*args, **kwargs)
122 def clean(self):
123 super().clean()
125 # Validate distance and distance_unit
126 if self.distance and not self.distance_unit:
127 raise ValidationError(_("Must specify a unit when setting a distance"))