mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-24 17:38:37 -06:00
Convert remaining DCIM models to use NaturalOrderingField
This commit is contained in:
parent
705c352885
commit
099c446f38
67
netbox/dcim/migrations/0095_primary_model_ordering.py
Normal file
67
netbox/dcim/migrations/0095_primary_model_ordering.py
Normal file
@ -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
|
||||||
|
),
|
||||||
|
]
|
@ -22,8 +22,7 @@ from dcim.choices import *
|
|||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.fields import ASNField
|
from dcim.fields import ASNField
|
||||||
from extras.models import ConfigContextModel, CustomFieldModel, TaggedItem
|
from extras.models import ConfigContextModel, CustomFieldModel, TaggedItem
|
||||||
from utilities.fields import ColorField
|
from utilities.fields import ColorField, NaturalOrderingField
|
||||||
from utilities.managers import NaturalOrderingManager
|
|
||||||
from utilities.models import ChangeLoggedModel
|
from utilities.models import ChangeLoggedModel
|
||||||
from utilities.utils import foreground_color, to_meters
|
from utilities.utils import foreground_color, to_meters
|
||||||
from .device_component_templates import (
|
from .device_component_templates import (
|
||||||
@ -134,6 +133,11 @@ class Site(ChangeLoggedModel, CustomFieldModel):
|
|||||||
max_length=50,
|
max_length=50,
|
||||||
unique=True
|
unique=True
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
slug = models.SlugField(
|
slug = models.SlugField(
|
||||||
unique=True
|
unique=True
|
||||||
)
|
)
|
||||||
@ -215,8 +219,6 @@ class Site(ChangeLoggedModel, CustomFieldModel):
|
|||||||
images = GenericRelation(
|
images = GenericRelation(
|
||||||
to='extras.ImageAttachment'
|
to='extras.ImageAttachment'
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
@ -235,7 +237,7 @@ class Site(ChangeLoggedModel, CustomFieldModel):
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['name']
|
ordering = ('_name',)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -516,6 +518,11 @@ class Rack(ChangeLoggedModel, CustomFieldModel, RackElevationHelperMixin):
|
|||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=50
|
max_length=50
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
facility_id = models.CharField(
|
facility_id = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
@ -612,8 +619,6 @@ class Rack(ChangeLoggedModel, CustomFieldModel, RackElevationHelperMixin):
|
|||||||
images = GenericRelation(
|
images = GenericRelation(
|
||||||
to='extras.ImageAttachment'
|
to='extras.ImageAttachment'
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
@ -634,12 +639,12 @@ class Rack(ChangeLoggedModel, CustomFieldModel, RackElevationHelperMixin):
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('site', 'group', 'name', 'pk') # (site, group, name) may be non-unique
|
ordering = ('site', 'group', '_name', 'pk') # (site, group, name) may be non-unique
|
||||||
unique_together = [
|
unique_together = (
|
||||||
# Name and facility_id must be unique *only* within a RackGroup
|
# Name and facility_id must be unique *only* within a RackGroup
|
||||||
['group', 'name'],
|
('group', 'name'),
|
||||||
['group', 'facility_id'],
|
('group', 'facility_id'),
|
||||||
]
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.display_name or super().__str__()
|
return self.display_name or super().__str__()
|
||||||
@ -1313,6 +1318,11 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
serial = models.CharField(
|
serial = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
@ -1407,8 +1417,6 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
|
|||||||
images = GenericRelation(
|
images = GenericRelation(
|
||||||
to='extras.ImageAttachment'
|
to='extras.ImageAttachment'
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
@ -1430,12 +1438,12 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('name', 'pk') # Name may be NULL
|
ordering = ('_name', 'pk') # Name may be blank
|
||||||
unique_together = [
|
unique_together = (
|
||||||
['site', 'tenant', 'name'], # See validate_unique below
|
('site', 'tenant', 'name'), # See validate_unique below
|
||||||
['rack', 'position', 'face'],
|
('rack', 'position', 'face'),
|
||||||
['virtual_chassis', 'vc_position'],
|
('virtual_chassis', 'vc_position'),
|
||||||
]
|
)
|
||||||
permissions = (
|
permissions = (
|
||||||
('napalm_read', 'Read-only access to devices via NAPALM'),
|
('napalm_read', 'Read-only access to devices via NAPALM'),
|
||||||
('napalm_write', 'Read/write access to devices via NAPALM'),
|
('napalm_write', 'Read/write access to devices via NAPALM'),
|
||||||
|
@ -229,7 +229,7 @@ class RegionTable(BaseTable):
|
|||||||
|
|
||||||
class SiteTable(BaseTable):
|
class SiteTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn(order_by=('_nat1', '_nat2', '_nat3'))
|
name = tables.LinkColumn()
|
||||||
status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status')
|
status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status')
|
||||||
region = tables.TemplateColumn(template_code=SITE_REGION_LINK)
|
region = tables.TemplateColumn(template_code=SITE_REGION_LINK)
|
||||||
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
||||||
@ -291,7 +291,7 @@ class RackRoleTable(BaseTable):
|
|||||||
|
|
||||||
class RackTable(BaseTable):
|
class RackTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn(order_by=('_nat1', '_nat2', '_nat3'))
|
name = tables.LinkColumn()
|
||||||
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
||||||
group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
|
group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
|
||||||
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
||||||
@ -653,10 +653,7 @@ class PlatformTable(BaseTable):
|
|||||||
|
|
||||||
class DeviceTable(BaseTable):
|
class DeviceTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.TemplateColumn(
|
name = tables.TemplateColumn(template_code=DEVICE_LINK)
|
||||||
order_by=('_nat1', '_nat2', '_nat3'),
|
|
||||||
template_code=DEVICE_LINK
|
|
||||||
)
|
|
||||||
status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status')
|
status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status')
|
||||||
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
||||||
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
||||||
|
@ -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 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)
|
:param integer_places: The number of places to which each integer will be expanded. (Default: 8)
|
||||||
"""
|
"""
|
||||||
|
if not value:
|
||||||
|
return ''
|
||||||
output = []
|
output = []
|
||||||
for segment in re.split(r'(\d+)', value):
|
for segment in re.split(r'(\d+)', value):
|
||||||
if segment.isdigit():
|
if segment.isdigit():
|
||||||
|
Loading…
Reference in New Issue
Block a user