Просмотр исходного кода

Convert remaining DCIM models to use NaturalOrderingField

Jeremy Stretch 6 лет назад
Родитель
Сommit
099c446f38

+ 67 - 0
netbox/dcim/migrations/0095_primary_model_ordering.py

@@ -0,0 +1,67 @@
+from django.db import migrations
+import utilities.fields
+import utilities.ordering
+
+
+def _update_model_names(model):
+    # Update each unique field value in bulk
+    for name in model.objects.values_list('name', flat=True).order_by('name').distinct():
+        model.objects.filter(name=name).update(_name=utilities.ordering.naturalize(name))
+
+
+def naturalize_sites(apps, schema_editor):
+    _update_model_names(apps.get_model('dcim', 'Site'))
+
+
+def naturalize_racks(apps, schema_editor):
+    _update_model_names(apps.get_model('dcim', 'Rack'))
+
+
+def naturalize_devices(apps, schema_editor):
+    _update_model_names(apps.get_model('dcim', 'Device'))
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0094_device_component_template_ordering'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='device',
+            options={'ordering': ('_name', 'pk'), 'permissions': (('napalm_read', 'Read-only access to devices via NAPALM'), ('napalm_write', 'Read/write access to devices via NAPALM'))},
+        ),
+        migrations.AlterModelOptions(
+            name='rack',
+            options={'ordering': ('site', 'group', '_name', 'pk')},
+        ),
+        migrations.AlterModelOptions(
+            name='site',
+            options={'ordering': ('_name',)},
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='_name',
+            field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
+        ),
+        migrations.AddField(
+            model_name='rack',
+            name='_name',
+            field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
+        ),
+        migrations.AddField(
+            model_name='site',
+            name='_name',
+            field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
+        ),
+        migrations.RunPython(
+            code=naturalize_sites
+        ),
+        migrations.RunPython(
+            code=naturalize_racks
+        ),
+        migrations.RunPython(
+            code=naturalize_devices
+        ),
+    ]

+ 28 - 20
netbox/dcim/models/__init__.py

@@ -22,8 +22,7 @@ from dcim.choices import *
 from dcim.constants import *
 from dcim.fields import ASNField
 from extras.models import ConfigContextModel, CustomFieldModel, TaggedItem
-from utilities.fields import ColorField
-from utilities.managers import NaturalOrderingManager
+from utilities.fields import ColorField, NaturalOrderingField
 from utilities.models import ChangeLoggedModel
 from utilities.utils import foreground_color, to_meters
 from .device_component_templates import (
@@ -134,6 +133,11 @@ class Site(ChangeLoggedModel, CustomFieldModel):
         max_length=50,
         unique=True
     )
+    _name = NaturalOrderingField(
+        target_field='name',
+        max_length=100,
+        blank=True
+    )
     slug = models.SlugField(
         unique=True
     )
@@ -215,8 +219,6 @@ class Site(ChangeLoggedModel, CustomFieldModel):
     images = GenericRelation(
         to='extras.ImageAttachment'
     )
-
-    objects = NaturalOrderingManager()
     tags = TaggableManager(through=TaggedItem)
 
     csv_headers = [
@@ -235,7 +237,7 @@ class Site(ChangeLoggedModel, CustomFieldModel):
     }
 
     class Meta:
-        ordering = ['name']
+        ordering = ('_name',)
 
     def __str__(self):
         return self.name
@@ -516,6 +518,11 @@ class Rack(ChangeLoggedModel, CustomFieldModel, RackElevationHelperMixin):
     name = models.CharField(
         max_length=50
     )
+    _name = NaturalOrderingField(
+        target_field='name',
+        max_length=100,
+        blank=True
+    )
     facility_id = models.CharField(
         max_length=50,
         blank=True,
@@ -612,8 +619,6 @@ class Rack(ChangeLoggedModel, CustomFieldModel, RackElevationHelperMixin):
     images = GenericRelation(
         to='extras.ImageAttachment'
     )
-
-    objects = NaturalOrderingManager()
     tags = TaggableManager(through=TaggedItem)
 
     csv_headers = [
@@ -634,12 +639,12 @@ class Rack(ChangeLoggedModel, CustomFieldModel, RackElevationHelperMixin):
     }
 
     class Meta:
-        ordering = ('site', 'group', 'name', 'pk')  # (site, group, name) may be non-unique
-        unique_together = [
+        ordering = ('site', 'group', '_name', 'pk')  # (site, group, name) may be non-unique
+        unique_together = (
             # Name and facility_id must be unique *only* within a RackGroup
-            ['group', 'name'],
-            ['group', 'facility_id'],
-        ]
+            ('group', 'name'),
+            ('group', 'facility_id'),
+        )
 
     def __str__(self):
         return self.display_name or super().__str__()
@@ -1313,6 +1318,11 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
         blank=True,
         null=True
     )
+    _name = NaturalOrderingField(
+        target_field='name',
+        max_length=100,
+        blank=True
+    )
     serial = models.CharField(
         max_length=50,
         blank=True,
@@ -1407,8 +1417,6 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
     images = GenericRelation(
         to='extras.ImageAttachment'
     )
-
-    objects = NaturalOrderingManager()
     tags = TaggableManager(through=TaggedItem)
 
     csv_headers = [
@@ -1430,12 +1438,12 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
     }
 
     class Meta:
-        ordering = ('name', 'pk')  # Name may be NULL
-        unique_together = [
-            ['site', 'tenant', 'name'],  # See validate_unique below
-            ['rack', 'position', 'face'],
-            ['virtual_chassis', 'vc_position'],
-        ]
+        ordering = ('_name', 'pk')  # Name may be blank
+        unique_together = (
+            ('site', 'tenant', 'name'),  # See validate_unique below
+            ('rack', 'position', 'face'),
+            ('virtual_chassis', 'vc_position'),
+        )
         permissions = (
             ('napalm_read', 'Read-only access to devices via NAPALM'),
             ('napalm_write', 'Read/write access to devices via NAPALM'),

+ 3 - 6
netbox/dcim/tables.py

@@ -229,7 +229,7 @@ class RegionTable(BaseTable):
 
 class SiteTable(BaseTable):
     pk = ToggleColumn()
-    name = tables.LinkColumn(order_by=('_nat1', '_nat2', '_nat3'))
+    name = tables.LinkColumn()
     status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status')
     region = tables.TemplateColumn(template_code=SITE_REGION_LINK)
     tenant = tables.TemplateColumn(template_code=COL_TENANT)
@@ -291,7 +291,7 @@ class RackRoleTable(BaseTable):
 
 class RackTable(BaseTable):
     pk = ToggleColumn()
-    name = tables.LinkColumn(order_by=('_nat1', '_nat2', '_nat3'))
+    name = tables.LinkColumn()
     site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
     group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
     tenant = tables.TemplateColumn(template_code=COL_TENANT)
@@ -653,10 +653,7 @@ class PlatformTable(BaseTable):
 
 class DeviceTable(BaseTable):
     pk = ToggleColumn()
-    name = tables.TemplateColumn(
-        order_by=('_nat1', '_nat2', '_nat3'),
-        template_code=DEVICE_LINK
-    )
+    name = tables.TemplateColumn(template_code=DEVICE_LINK)
     status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status')
     tenant = tables.TemplateColumn(template_code=COL_TENANT)
     site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])

+ 2 - 0
netbox/utilities/ordering.py

@@ -20,6 +20,8 @@ def naturalize(value, max_length=None, integer_places=8):
     :param max_length: The maximum length of the returned string. Characters beyond this length will be stripped.
     :param integer_places: The number of places to which each integer will be expanded. (Default: 8)
     """
+    if not value:
+        return ''
     output = []
     for segment in re.split(r'(\d+)', value):
         if segment.isdigit():