Arthur 2 дней назад
Родитель
Сommit
8632ec3be1

+ 2 - 2
netbox/circuits/migrations/0061_generated_fields.py

@@ -7,7 +7,7 @@ import django.db.models.expressions
 import django.db.models.functions.comparison
 import django.db.models.functions.comparison
 from django.db import migrations, models
 from django.db import migrations, models
 
 
-import utilities.conversion
+import utilities.fields
 
 
 
 
 # PostgreSQL cannot convert an existing column to a generated column in place, so each normalized
 # PostgreSQL cannot convert an existing column to a generated column in place, so each normalized
@@ -26,7 +26,7 @@ class Migration(migrations.Migration):
         migrations.AddField(
         migrations.AddField(
             model_name="circuit",
             model_name="circuit",
             name="_abs_distance",
             name="_abs_distance",
-            field=utilities.conversion.NonNegativeGeneratedField(
+            field=utilities.fields.NonNegativeGeneratedField(
                 db_persist=True,
                 db_persist=True,
                 expression=models.Case(
                 expression=models.Case(
                     models.When(
                     models.When(

+ 15 - 15
netbox/dcim/migrations/0252_generated_fields.py

@@ -8,7 +8,7 @@ import django.db.models.functions.comparison
 import django.db.models.functions.math
 import django.db.models.functions.math
 from django.db import migrations, models
 from django.db import migrations, models
 
 
-import utilities.conversion
+import utilities.fields
 
 
 
 
 # PostgreSQL cannot convert an existing column to a generated column in place, so each normalized
 # PostgreSQL cannot convert an existing column to a generated column in place, so each normalized
@@ -27,7 +27,7 @@ class Migration(migrations.Migration):
         migrations.AddField(
         migrations.AddField(
             model_name="cable",
             model_name="cable",
             name="_abs_length",
             name="_abs_length",
-            field=utilities.conversion.NonNegativeGeneratedField(
+            field=utilities.fields.NonNegativeGeneratedField(
                 db_persist=True,
                 db_persist=True,
                 expression=models.Case(
                 expression=models.Case(
                     models.When(
                     models.When(
@@ -104,7 +104,7 @@ class Migration(migrations.Migration):
         migrations.AddField(
         migrations.AddField(
             model_name="coolingfeed",
             model_name="coolingfeed",
             name="_abs_max_flow",
             name="_abs_max_flow",
-            field=utilities.conversion.NonNegativeGeneratedField(
+            field=utilities.fields.NonNegativeGeneratedField(
                 db_persist=True,
                 db_persist=True,
                 expression=models.Case(
                 expression=models.Case(
                     models.When(
                     models.When(
@@ -151,7 +151,7 @@ class Migration(migrations.Migration):
         migrations.AddField(
         migrations.AddField(
             model_name="coolingintake",
             model_name="coolingintake",
             name="_abs_diameter",
             name="_abs_diameter",
-            field=utilities.conversion.NonNegativeGeneratedField(
+            field=utilities.fields.NonNegativeGeneratedField(
                 db_persist=True,
                 db_persist=True,
                 expression=models.Case(
                 expression=models.Case(
                     models.When(
                     models.When(
@@ -198,7 +198,7 @@ class Migration(migrations.Migration):
         migrations.AddField(
         migrations.AddField(
             model_name="coolingintake",
             model_name="coolingintake",
             name="_abs_max_flow",
             name="_abs_max_flow",
-            field=utilities.conversion.NonNegativeGeneratedField(
+            field=utilities.fields.NonNegativeGeneratedField(
                 db_persist=True,
                 db_persist=True,
                 expression=models.Case(
                 expression=models.Case(
                     models.When(
                     models.When(
@@ -245,7 +245,7 @@ class Migration(migrations.Migration):
         migrations.AddField(
         migrations.AddField(
             model_name="coolingintaketemplate",
             model_name="coolingintaketemplate",
             name="_abs_diameter",
             name="_abs_diameter",
-            field=utilities.conversion.NonNegativeGeneratedField(
+            field=utilities.fields.NonNegativeGeneratedField(
                 db_persist=True,
                 db_persist=True,
                 expression=models.Case(
                 expression=models.Case(
                     models.When(
                     models.When(
@@ -292,7 +292,7 @@ class Migration(migrations.Migration):
         migrations.AddField(
         migrations.AddField(
             model_name="coolingintaketemplate",
             model_name="coolingintaketemplate",
             name="_abs_max_flow",
             name="_abs_max_flow",
-            field=utilities.conversion.NonNegativeGeneratedField(
+            field=utilities.fields.NonNegativeGeneratedField(
                 db_persist=True,
                 db_persist=True,
                 expression=models.Case(
                 expression=models.Case(
                     models.When(
                     models.When(
@@ -339,7 +339,7 @@ class Migration(migrations.Migration):
         migrations.AddField(
         migrations.AddField(
             model_name="coolingoutflow",
             model_name="coolingoutflow",
             name="_abs_diameter",
             name="_abs_diameter",
-            field=utilities.conversion.NonNegativeGeneratedField(
+            field=utilities.fields.NonNegativeGeneratedField(
                 db_persist=True,
                 db_persist=True,
                 expression=models.Case(
                 expression=models.Case(
                     models.When(
                     models.When(
@@ -386,7 +386,7 @@ class Migration(migrations.Migration):
         migrations.AddField(
         migrations.AddField(
             model_name="coolingoutflowtemplate",
             model_name="coolingoutflowtemplate",
             name="_abs_diameter",
             name="_abs_diameter",
-            field=utilities.conversion.NonNegativeGeneratedField(
+            field=utilities.fields.NonNegativeGeneratedField(
                 db_persist=True,
                 db_persist=True,
                 expression=models.Case(
                 expression=models.Case(
                     models.When(
                     models.When(
@@ -433,7 +433,7 @@ class Migration(migrations.Migration):
         migrations.AddField(
         migrations.AddField(
             model_name="devicetype",
             model_name="devicetype",
             name="_abs_weight",
             name="_abs_weight",
-            field=utilities.conversion.NonNegativeGeneratedField(
+            field=utilities.fields.NonNegativeGeneratedField(
                 db_persist=True,
                 db_persist=True,
                 expression=django.db.models.functions.comparison.Cast(
                 expression=django.db.models.functions.comparison.Cast(
                     django.db.models.functions.math.Floor(
                     django.db.models.functions.math.Floor(
@@ -497,7 +497,7 @@ class Migration(migrations.Migration):
         migrations.AddField(
         migrations.AddField(
             model_name="moduletype",
             model_name="moduletype",
             name="_abs_weight",
             name="_abs_weight",
-            field=utilities.conversion.NonNegativeGeneratedField(
+            field=utilities.fields.NonNegativeGeneratedField(
                 db_persist=True,
                 db_persist=True,
                 expression=django.db.models.functions.comparison.Cast(
                 expression=django.db.models.functions.comparison.Cast(
                     django.db.models.functions.math.Floor(
                     django.db.models.functions.math.Floor(
@@ -561,7 +561,7 @@ class Migration(migrations.Migration):
         migrations.AddField(
         migrations.AddField(
             model_name="rack",
             model_name="rack",
             name="_abs_max_weight",
             name="_abs_max_weight",
-            field=utilities.conversion.NonNegativeGeneratedField(
+            field=utilities.fields.NonNegativeGeneratedField(
                 db_persist=True,
                 db_persist=True,
                 expression=django.db.models.functions.comparison.Cast(
                 expression=django.db.models.functions.comparison.Cast(
                     django.db.models.functions.math.Floor(
                     django.db.models.functions.math.Floor(
@@ -625,7 +625,7 @@ class Migration(migrations.Migration):
         migrations.AddField(
         migrations.AddField(
             model_name="rack",
             model_name="rack",
             name="_abs_weight",
             name="_abs_weight",
-            field=utilities.conversion.NonNegativeGeneratedField(
+            field=utilities.fields.NonNegativeGeneratedField(
                 db_persist=True,
                 db_persist=True,
                 expression=django.db.models.functions.comparison.Cast(
                 expression=django.db.models.functions.comparison.Cast(
                     django.db.models.functions.math.Floor(
                     django.db.models.functions.math.Floor(
@@ -689,7 +689,7 @@ class Migration(migrations.Migration):
         migrations.AddField(
         migrations.AddField(
             model_name="racktype",
             model_name="racktype",
             name="_abs_max_weight",
             name="_abs_max_weight",
-            field=utilities.conversion.NonNegativeGeneratedField(
+            field=utilities.fields.NonNegativeGeneratedField(
                 db_persist=True,
                 db_persist=True,
                 expression=django.db.models.functions.comparison.Cast(
                 expression=django.db.models.functions.comparison.Cast(
                     django.db.models.functions.math.Floor(
                     django.db.models.functions.math.Floor(
@@ -753,7 +753,7 @@ class Migration(migrations.Migration):
         migrations.AddField(
         migrations.AddField(
             model_name="racktype",
             model_name="racktype",
             name="_abs_weight",
             name="_abs_weight",
-            field=utilities.conversion.NonNegativeGeneratedField(
+            field=utilities.fields.NonNegativeGeneratedField(
                 db_persist=True,
                 db_persist=True,
                 expression=django.db.models.functions.comparison.Cast(
                 expression=django.db.models.functions.comparison.Cast(
                     django.db.models.functions.math.Floor(
                     django.db.models.functions.math.Floor(

+ 2 - 2
netbox/dcim/models/cables.py

@@ -22,10 +22,10 @@ from dcim.fields import PathField
 from dcim.utils import decompile_path_node, object_to_path_node, rebuild_cable_paths
 from dcim.utils import decompile_path_node, object_to_path_node, rebuild_cable_paths
 from netbox.choices import ColorChoices
 from netbox.choices import ColorChoices
 from netbox.models import ChangeLoggedModel, PrimaryModel
 from netbox.models import ChangeLoggedModel, PrimaryModel
-from utilities.conversion import LENGTH_TO_METERS, NonNegativeGeneratedField, normalized_unit_expression
+from utilities.conversion import LENGTH_TO_METERS, normalized_unit_expression
 from utilities.data import normalize_update_fields
 from utilities.data import normalize_update_fields
 from utilities.exceptions import AbortRequest
 from utilities.exceptions import AbortRequest
-from utilities.fields import ColorField, GenericArrayForeignKey
+from utilities.fields import ColorField, GenericArrayForeignKey, NonNegativeGeneratedField
 from utilities.querysets import RestrictedQuerySet, chunked_update
 from utilities.querysets import RestrictedQuerySet, chunked_update
 from utilities.serialization import deserialize_object, serialize_object
 from utilities.serialization import deserialize_object, serialize_object
 from wireless.models import WirelessLink
 from wireless.models import WirelessLink

+ 1 - 1
netbox/dcim/models/mixins.py

@@ -13,10 +13,10 @@ from netbox.choices import *
 from utilities.conversion import (
 from utilities.conversion import (
     DIAMETER_TO_MILLIMETERS,
     DIAMETER_TO_MILLIMETERS,
     FLOW_RATE_TO_LITERS_PER_MINUTE,
     FLOW_RATE_TO_LITERS_PER_MINUTE,
-    NonNegativeGeneratedField,
     normalized_unit_expression,
     normalized_unit_expression,
 )
 )
 from utilities.data import normalize_update_fields
 from utilities.data import normalize_update_fields
+from utilities.fields import NonNegativeGeneratedField
 
 
 __all__ = (
 __all__ = (
     'CachedScopeMixin',
     'CachedScopeMixin',

+ 2 - 2
netbox/dcim/models/racks.py

@@ -17,9 +17,9 @@ from netbox.choices import ColorChoices
 from netbox.models import OrganizationalModel, PrimaryModel
 from netbox.models import OrganizationalModel, PrimaryModel
 from netbox.models.features import ContactsMixin, ImageAttachmentsMixin
 from netbox.models.features import ContactsMixin, ImageAttachmentsMixin
 from netbox.models.mixins import WeightMixin
 from netbox.models.mixins import WeightMixin
-from utilities.conversion import WEIGHT_TO_GRAMS, NonNegativeGeneratedField, normalized_unit_expression
+from utilities.conversion import WEIGHT_TO_GRAMS, normalized_unit_expression
 from utilities.data import array_to_string, drange
 from utilities.data import array_to_string, drange
-from utilities.fields import ColorField, CounterCacheField
+from utilities.fields import ColorField, CounterCacheField, NonNegativeGeneratedField
 from utilities.tracking import TrackingModelMixin
 from utilities.tracking import TrackingModelMixin
 
 
 from .device_components import PowerPort
 from .device_components import PowerPort

+ 2 - 6
netbox/netbox/models/mixins.py

@@ -4,12 +4,8 @@ from django.db import models
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 
 
 from netbox.choices import *
 from netbox.choices import *
-from utilities.conversion import (
-    LENGTH_TO_METERS,
-    WEIGHT_TO_GRAMS,
-    NonNegativeGeneratedField,
-    normalized_unit_expression,
-)
+from utilities.conversion import LENGTH_TO_METERS, WEIGHT_TO_GRAMS, normalized_unit_expression
+from utilities.fields import NonNegativeGeneratedField
 
 
 __all__ = (
 __all__ = (
     'DistanceMixin',
     'DistanceMixin',

+ 1 - 14
netbox/utilities/conversion.py

@@ -1,6 +1,6 @@
 from decimal import ROUND_HALF_UP, Decimal, InvalidOperation
 from decimal import ROUND_HALF_UP, Decimal, InvalidOperation
 
 
-from django.db.models import BigIntegerField, Case, DecimalField, F, GeneratedField, Value, When
+from django.db.models import BigIntegerField, Case, DecimalField, F, Value, When
 from django.db.models.functions import Cast, Floor
 from django.db.models.functions import Cast, Floor
 from django.utils.translation import gettext as _
 from django.utils.translation import gettext as _
 
 
@@ -16,7 +16,6 @@ __all__ = (
     'FLOW_RATE_TO_LITERS_PER_MINUTE',
     'FLOW_RATE_TO_LITERS_PER_MINUTE',
     'LENGTH_TO_METERS',
     'LENGTH_TO_METERS',
     'WEIGHT_TO_GRAMS',
     'WEIGHT_TO_GRAMS',
-    'NonNegativeGeneratedField',
     'normalized_unit_expression',
     'normalized_unit_expression',
     'to_grams',
     'to_grams',
     'to_liters_per_minute',
     'to_liters_per_minute',
@@ -66,18 +65,6 @@ FLOW_RATE_TO_LITERS_PER_MINUTE = {
 # Database expressions
 # Database expressions
 #
 #
 
 
-class NonNegativeGeneratedField(GeneratedField):
-    """
-    A generated column which the database constrains to non-negative values via an inline CHECK, as it would a
-    PositiveIntegerField. Django emits no check for a GeneratedField's output_field because that field is never
-    bound to a column, so the check is supplied here from the generated column itself.
-    """
-    def db_parameters(self, connection):
-        params = super().db_parameters(connection)
-        params['check'] = f'{connection.ops.quote_name(self.column)} >= 0'
-        return params
-
-
 def normalized_unit_expression(value_field, unit_field, factors, integer=False):
 def normalized_unit_expression(value_field, unit_field, factors, integer=False):
     """
     """
     Return a database expression converting `value_field` to its base unit according to the value of `unit_field`,
     Return a database expression converting `value_field` to its base unit according to the value of `unit_field`,

+ 15 - 1
netbox/utilities/fields.py

@@ -8,7 +8,6 @@ from django.db.models.fields.mixins import FieldCacheMixin
 from django.utils.functional import cached_property
 from django.utils.functional import cached_property
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 
 
-from .forms.widgets import ColorSelect
 from .validators import ColorValidator
 from .validators import ColorValidator
 
 
 __all__ = (
 __all__ = (
@@ -16,6 +15,7 @@ __all__ = (
     'CounterCacheField',
     'CounterCacheField',
     'GenericArrayForeignKey',
     'GenericArrayForeignKey',
     'NaturalOrderingField',
     'NaturalOrderingField',
+    'NonNegativeGeneratedField',
     'RestrictedGenericForeignKey',
     'RestrictedGenericForeignKey',
 )
 )
 
 
@@ -29,10 +29,24 @@ class ColorField(models.CharField):
         super().__init__(*args, **kwargs)
         super().__init__(*args, **kwargs)
 
 
     def formfield(self, **kwargs):
     def formfield(self, **kwargs):
+        # Imported here because utilities.forms indirectly imports netbox.models, which imports this module
+        from .forms.widgets import ColorSelect
         kwargs['widget'] = ColorSelect
         kwargs['widget'] = ColorSelect
         return super().formfield(**kwargs)
         return super().formfield(**kwargs)
 
 
 
 
+class NonNegativeGeneratedField(models.GeneratedField):
+    """
+    A generated column which the database constrains to non-negative values via an inline CHECK, as it would a
+    PositiveIntegerField. Django emits no check for a GeneratedField's output_field because that field is never
+    bound to a column, so the check is supplied here from the generated column itself.
+    """
+    def db_parameters(self, connection):
+        params = super().db_parameters(connection)
+        params['check'] = f'{connection.ops.quote_name(self.column)} >= 0'
+        return params
+
+
 class NaturalOrderingField(models.CharField):
 class NaturalOrderingField(models.CharField):
     """
     """
     A field which stores a naturalized representation of its target field, to be used for ordering its parent model.
     A field which stores a naturalized representation of its target field, to be used for ordering its parent model.

+ 2 - 2
netbox/wireless/migrations/0025_generated_fields.py

@@ -7,7 +7,7 @@ import django.db.models.expressions
 import django.db.models.functions.comparison
 import django.db.models.functions.comparison
 from django.db import migrations, models
 from django.db import migrations, models
 
 
-import utilities.conversion
+import utilities.fields
 
 
 
 
 # PostgreSQL cannot convert an existing column to a generated column in place, so each normalized
 # PostgreSQL cannot convert an existing column to a generated column in place, so each normalized
@@ -26,7 +26,7 @@ class Migration(migrations.Migration):
         migrations.AddField(
         migrations.AddField(
             model_name="wirelesslink",
             model_name="wirelesslink",
             name="_abs_distance",
             name="_abs_distance",
-            field=utilities.conversion.NonNegativeGeneratedField(
+            field=utilities.fields.NonNegativeGeneratedField(
                 db_persist=True,
                 db_persist=True,
                 expression=models.Case(
                 expression=models.Case(
                     models.When(
                     models.When(