mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-22 20:12:00 -06:00
* 11279 add collation * 11279 add collation * 11279 add collation * 11279 add collation * 11279 fix tables /tests * 11279 fix tests * 11279 refactor VirtualDisk * Clean up migrations * Misc cleanup * Correct errant file inclusion --------- Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
This commit is contained in:
parent
75aeaab8ee
commit
6ab0792f02
22
netbox/circuits/migrations/0049_natural_ordering.py
Normal file
22
netbox/circuits/migrations/0049_natural_ordering.py
Normal file
@ -0,0 +1,22 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('circuits', '0048_circuitterminations_cached_relations'),
|
||||
('dcim', '0197_natural_sort_collation'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='provider',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='providernetwork',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100),
|
||||
),
|
||||
]
|
@ -21,7 +21,8 @@ class Provider(ContactsMixin, PrimaryModel):
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True,
|
||||
help_text=_('Full name of the provider')
|
||||
help_text=_('Full name of the provider'),
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
@ -95,7 +96,8 @@ class ProviderNetwork(PrimaryModel):
|
||||
"""
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100
|
||||
max_length=100,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
provider = models.ForeignKey(
|
||||
to='circuits.Provider',
|
||||
|
@ -76,10 +76,6 @@ class ChangeLogViewTest(ModelViewTestCase):
|
||||
self.assertEqual(oc.postchange_data['custom_fields']['cf2'], form_data['cf_cf2'])
|
||||
self.assertEqual(oc.postchange_data['tags'], ['Tag 1', 'Tag 2'])
|
||||
|
||||
# Check that private attributes were included in raw data but not display data
|
||||
self.assertIn('_name', oc.postchange_data)
|
||||
self.assertNotIn('_name', oc.postchange_data_clean)
|
||||
|
||||
def test_update_object(self):
|
||||
site = Site(name='Site 1', slug='site-1')
|
||||
site.save()
|
||||
@ -117,12 +113,6 @@ class ChangeLogViewTest(ModelViewTestCase):
|
||||
self.assertEqual(oc.postchange_data['custom_fields']['cf2'], form_data['cf_cf2'])
|
||||
self.assertEqual(oc.postchange_data['tags'], ['Tag 3'])
|
||||
|
||||
# Check that private attributes were included in raw data but not display data
|
||||
self.assertIn('_name', oc.prechange_data)
|
||||
self.assertNotIn('_name', oc.prechange_data_clean)
|
||||
self.assertIn('_name', oc.postchange_data)
|
||||
self.assertNotIn('_name', oc.postchange_data_clean)
|
||||
|
||||
def test_delete_object(self):
|
||||
site = Site(
|
||||
name='Site 1',
|
||||
@ -153,10 +143,6 @@ class ChangeLogViewTest(ModelViewTestCase):
|
||||
self.assertEqual(oc.prechange_data['tags'], ['Tag 1', 'Tag 2'])
|
||||
self.assertEqual(oc.postchange_data, None)
|
||||
|
||||
# Check that private attributes were included in raw data but not display data
|
||||
self.assertIn('_name', oc.prechange_data)
|
||||
self.assertNotIn('_name', oc.prechange_data_clean)
|
||||
|
||||
def test_bulk_update_objects(self):
|
||||
sites = (
|
||||
Site(name='Site 1', slug='site-1', status=SiteStatusChoices.STATUS_ACTIVE),
|
||||
@ -353,10 +339,6 @@ class ChangeLogAPITest(APITestCase):
|
||||
self.assertEqual(oc.postchange_data['custom_fields'], data['custom_fields'])
|
||||
self.assertEqual(oc.postchange_data['tags'], ['Tag 1', 'Tag 2'])
|
||||
|
||||
# Check that private attributes were included in raw data but not display data
|
||||
self.assertIn('_name', oc.postchange_data)
|
||||
self.assertNotIn('_name', oc.postchange_data_clean)
|
||||
|
||||
def test_update_object(self):
|
||||
site = Site(name='Site 1', slug='site-1')
|
||||
site.save()
|
||||
@ -389,12 +371,6 @@ class ChangeLogAPITest(APITestCase):
|
||||
self.assertEqual(oc.postchange_data['custom_fields'], data['custom_fields'])
|
||||
self.assertEqual(oc.postchange_data['tags'], ['Tag 3'])
|
||||
|
||||
# Check that private attributes were included in raw data but not display data
|
||||
self.assertIn('_name', oc.prechange_data)
|
||||
self.assertNotIn('_name', oc.prechange_data_clean)
|
||||
self.assertIn('_name', oc.postchange_data)
|
||||
self.assertNotIn('_name', oc.postchange_data_clean)
|
||||
|
||||
def test_delete_object(self):
|
||||
site = Site(
|
||||
name='Site 1',
|
||||
@ -423,10 +399,6 @@ class ChangeLogAPITest(APITestCase):
|
||||
self.assertEqual(oc.prechange_data['tags'], ['Tag 1', 'Tag 2'])
|
||||
self.assertEqual(oc.postchange_data, None)
|
||||
|
||||
# Check that private attributes were included in raw data but not display data
|
||||
self.assertIn('_name', oc.prechange_data)
|
||||
self.assertNotIn('_name', oc.prechange_data_clean)
|
||||
|
||||
def test_bulk_create_objects(self):
|
||||
data = (
|
||||
{
|
||||
|
@ -76,7 +76,6 @@ class ComponentType(
|
||||
"""
|
||||
Base type for device/VM components
|
||||
"""
|
||||
_name: str
|
||||
device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]
|
||||
|
||||
|
||||
@ -93,7 +92,6 @@ class ComponentTemplateType(
|
||||
"""
|
||||
Base type for device/VM components
|
||||
"""
|
||||
_name: str
|
||||
device_type: Annotated["DeviceTypeType", strawberry.lazy('dcim.graphql.types')]
|
||||
|
||||
|
||||
@ -181,7 +179,7 @@ class ConsolePortType(ModularComponentType, CabledObjectMixin, PathEndpointMixin
|
||||
filters=ConsolePortTemplateFilter
|
||||
)
|
||||
class ConsolePortTemplateType(ModularComponentTemplateType):
|
||||
_name: str
|
||||
pass
|
||||
|
||||
|
||||
@strawberry_django.type(
|
||||
@ -199,7 +197,7 @@ class ConsoleServerPortType(ModularComponentType, CabledObjectMixin, PathEndpoin
|
||||
filters=ConsoleServerPortTemplateFilter
|
||||
)
|
||||
class ConsoleServerPortTemplateType(ModularComponentTemplateType):
|
||||
_name: str
|
||||
pass
|
||||
|
||||
|
||||
@strawberry_django.type(
|
||||
@ -208,7 +206,6 @@ class ConsoleServerPortTemplateType(ModularComponentTemplateType):
|
||||
filters=DeviceFilter
|
||||
)
|
||||
class DeviceType(ConfigContextMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType):
|
||||
_name: str
|
||||
console_port_count: BigInt
|
||||
console_server_port_count: BigInt
|
||||
power_port_count: BigInt
|
||||
@ -273,7 +270,7 @@ class DeviceBayType(ComponentType):
|
||||
filters=DeviceBayTemplateFilter
|
||||
)
|
||||
class DeviceBayTemplateType(ComponentTemplateType):
|
||||
_name: str
|
||||
pass
|
||||
|
||||
|
||||
@strawberry_django.type(
|
||||
@ -282,7 +279,6 @@ class DeviceBayTemplateType(ComponentTemplateType):
|
||||
filters=InventoryItemTemplateFilter
|
||||
)
|
||||
class InventoryItemTemplateType(ComponentTemplateType):
|
||||
_name: str
|
||||
role: Annotated["InventoryItemRoleType", strawberry.lazy('dcim.graphql.types')] | None
|
||||
manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')]
|
||||
|
||||
@ -366,7 +362,6 @@ class FrontPortType(ModularComponentType, CabledObjectMixin):
|
||||
filters=FrontPortTemplateFilter
|
||||
)
|
||||
class FrontPortTemplateType(ModularComponentTemplateType):
|
||||
_name: str
|
||||
color: str
|
||||
rear_port: Annotated["RearPortTemplateType", strawberry.lazy('dcim.graphql.types')]
|
||||
|
||||
@ -377,6 +372,7 @@ class FrontPortTemplateType(ModularComponentTemplateType):
|
||||
filters=InterfaceFilter
|
||||
)
|
||||
class InterfaceType(IPAddressesMixin, ModularComponentType, CabledObjectMixin, PathEndpointMixin):
|
||||
_name: str
|
||||
mac_address: str | None
|
||||
wwn: str | None
|
||||
parent: Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')] | None
|
||||
@ -527,7 +523,7 @@ class ModuleBayType(ModularComponentType):
|
||||
filters=ModuleBayTemplateFilter
|
||||
)
|
||||
class ModuleBayTemplateType(ModularComponentTemplateType):
|
||||
_name: str
|
||||
pass
|
||||
|
||||
|
||||
@strawberry_django.type(
|
||||
@ -588,7 +584,6 @@ class PowerOutletType(ModularComponentType, CabledObjectMixin, PathEndpointMixin
|
||||
filters=PowerOutletTemplateFilter
|
||||
)
|
||||
class PowerOutletTemplateType(ModularComponentTemplateType):
|
||||
_name: str
|
||||
power_port: Annotated["PowerPortTemplateType", strawberry.lazy('dcim.graphql.types')] | None
|
||||
|
||||
|
||||
@ -620,8 +615,6 @@ class PowerPortType(ModularComponentType, CabledObjectMixin, PathEndpointMixin):
|
||||
filters=PowerPortTemplateFilter
|
||||
)
|
||||
class PowerPortTemplateType(ModularComponentTemplateType):
|
||||
_name: str
|
||||
|
||||
poweroutlet_templates: List[Annotated["PowerOutletTemplateType", strawberry.lazy('dcim.graphql.types')]]
|
||||
|
||||
|
||||
@ -640,7 +633,6 @@ class RackTypeType(NetBoxObjectType):
|
||||
filters=RackFilter
|
||||
)
|
||||
class RackType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType):
|
||||
_name: str
|
||||
site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]
|
||||
location: Annotated["LocationType", strawberry.lazy('dcim.graphql.types')] | None
|
||||
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
|
||||
@ -693,7 +685,6 @@ class RearPortType(ModularComponentType, CabledObjectMixin):
|
||||
filters=RearPortTemplateFilter
|
||||
)
|
||||
class RearPortTemplateType(ModularComponentTemplateType):
|
||||
_name: str
|
||||
color: str
|
||||
|
||||
frontport_templates: List[Annotated["FrontPortTemplateType", strawberry.lazy('dcim.graphql.types')]]
|
||||
@ -729,7 +720,6 @@ class RegionType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType):
|
||||
filters=SiteFilter
|
||||
)
|
||||
class SiteType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType):
|
||||
_name: str
|
||||
time_zone: str | None
|
||||
region: Annotated["RegionType", strawberry.lazy('dcim.graphql.types')] | None
|
||||
group: Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')] | None
|
||||
|
17
netbox/dcim/migrations/0197_natural_sort_collation.py
Normal file
17
netbox/dcim/migrations/0197_natural_sort_collation.py
Normal file
@ -0,0 +1,17 @@
|
||||
from django.contrib.postgres.operations import CreateCollation
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0196_qinq_svlan'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
CreateCollation(
|
||||
"natural_sort",
|
||||
provider="icu",
|
||||
locale="und-u-kn-true",
|
||||
),
|
||||
]
|
318
netbox/dcim/migrations/0198_natural_ordering.py
Normal file
318
netbox/dcim/migrations/0198_natural_ordering.py
Normal file
@ -0,0 +1,318 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0197_natural_sort_collation'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='site',
|
||||
options={'ordering': ('name',)},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='site',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='consoleport',
|
||||
options={'ordering': ('device', 'name')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='consoleporttemplate',
|
||||
options={'ordering': ('device_type', 'module_type', 'name')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='consoleserverport',
|
||||
options={'ordering': ('device', 'name')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='consoleserverporttemplate',
|
||||
options={'ordering': ('device_type', 'module_type', 'name')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='device',
|
||||
options={'ordering': ('name', 'pk')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='devicebay',
|
||||
options={'ordering': ('device', 'name')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='devicebaytemplate',
|
||||
options={'ordering': ('device_type', 'name')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='frontport',
|
||||
options={'ordering': ('device', 'name')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='frontporttemplate',
|
||||
options={'ordering': ('device_type', 'module_type', 'name')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='interfacetemplate',
|
||||
options={'ordering': ('device_type', 'module_type', 'name')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='inventoryitem',
|
||||
options={'ordering': ('device__id', 'parent__id', 'name')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='inventoryitemtemplate',
|
||||
options={'ordering': ('device_type__id', 'parent__id', 'name')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='modulebay',
|
||||
options={'ordering': ('device', 'name')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='modulebaytemplate',
|
||||
options={'ordering': ('device_type', 'module_type', 'name')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='poweroutlet',
|
||||
options={'ordering': ('device', 'name')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='poweroutlettemplate',
|
||||
options={'ordering': ('device_type', 'module_type', 'name')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='powerport',
|
||||
options={'ordering': ('device', 'name')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='powerporttemplate',
|
||||
options={'ordering': ('device_type', 'module_type', 'name')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='rack',
|
||||
options={'ordering': ('site', 'location', 'name', 'pk')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='rearport',
|
||||
options={'ordering': ('device', 'name')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='rearporttemplate',
|
||||
options={'ordering': ('device_type', 'module_type', 'name')},
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='consoleport',
|
||||
name='_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='consoleporttemplate',
|
||||
name='_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='consoleserverport',
|
||||
name='_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='consoleserverporttemplate',
|
||||
name='_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='device',
|
||||
name='_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='devicebay',
|
||||
name='_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='devicebaytemplate',
|
||||
name='_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='frontport',
|
||||
name='_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='frontporttemplate',
|
||||
name='_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='inventoryitem',
|
||||
name='_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='inventoryitemtemplate',
|
||||
name='_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='modulebay',
|
||||
name='_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='modulebaytemplate',
|
||||
name='_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='poweroutlet',
|
||||
name='_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='poweroutlettemplate',
|
||||
name='_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='powerport',
|
||||
name='_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='powerporttemplate',
|
||||
name='_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='rack',
|
||||
name='_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='rearport',
|
||||
name='_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='rearporttemplate',
|
||||
name='_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='site',
|
||||
name='_name',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='consoleport',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='consoleporttemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='consoleserverport',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='consoleserverporttemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='name',
|
||||
field=models.CharField(blank=True, db_collation='natural_sort', max_length=64, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicebay',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicebaytemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='frontport',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='frontporttemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interfacetemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='inventoryitem',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='inventoryitemtemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='modulebay',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='modulebaytemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='poweroutlet',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='poweroutlettemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='powerport',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='powerporttemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rack',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rearport',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rearporttemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='powerfeed',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='powerpanel',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='virtualchassis',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='virtualdevicecontext',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
]
|
@ -44,12 +44,8 @@ class ComponentTemplateModel(ChangeLoggedModel, TrackingModelMixin):
|
||||
max_length=64,
|
||||
help_text=_(
|
||||
"{module} is accepted as a substitution for the module bay position when attached to a module type."
|
||||
)
|
||||
)
|
||||
_name = NaturalOrderingField(
|
||||
target_field='name',
|
||||
max_length=100,
|
||||
blank=True
|
||||
),
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
label = models.CharField(
|
||||
verbose_name=_('label'),
|
||||
@ -65,7 +61,7 @@ class ComponentTemplateModel(ChangeLoggedModel, TrackingModelMixin):
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
ordering = ('device_type', '_name')
|
||||
ordering = ('device_type', 'name')
|
||||
constraints = (
|
||||
models.UniqueConstraint(
|
||||
fields=('device_type', 'name'),
|
||||
@ -125,7 +121,7 @@ class ModularComponentTemplateModel(ComponentTemplateModel):
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
ordering = ('device_type', 'module_type', '_name')
|
||||
ordering = ('device_type', 'module_type', 'name')
|
||||
constraints = (
|
||||
models.UniqueConstraint(
|
||||
fields=('device_type', 'name'),
|
||||
@ -782,7 +778,7 @@ class InventoryItemTemplate(MPTTModel, ComponentTemplateModel):
|
||||
component_model = InventoryItem
|
||||
|
||||
class Meta:
|
||||
ordering = ('device_type__id', 'parent__id', '_name')
|
||||
ordering = ('device_type__id', 'parent__id', 'name')
|
||||
indexes = (
|
||||
models.Index(fields=('component_type', 'component_id')),
|
||||
)
|
||||
|
@ -50,12 +50,8 @@ class ComponentModel(NetBoxModel):
|
||||
)
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=64
|
||||
)
|
||||
_name = NaturalOrderingField(
|
||||
target_field='name',
|
||||
max_length=100,
|
||||
blank=True
|
||||
max_length=64,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
label = models.CharField(
|
||||
verbose_name=_('label'),
|
||||
@ -71,7 +67,7 @@ class ComponentModel(NetBoxModel):
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
ordering = ('device', '_name')
|
||||
ordering = ('device', 'name')
|
||||
constraints = (
|
||||
models.UniqueConstraint(
|
||||
fields=('device', 'name'),
|
||||
@ -1301,7 +1297,7 @@ class InventoryItem(MPTTModel, ComponentModel, TrackingModelMixin):
|
||||
clone_fields = ('device', 'parent', 'role', 'manufacturer', 'status', 'part_id')
|
||||
|
||||
class Meta:
|
||||
ordering = ('device__id', 'parent__id', '_name')
|
||||
ordering = ('device__id', 'parent__id', 'name')
|
||||
indexes = (
|
||||
models.Index(fields=('component_type', 'component_id')),
|
||||
)
|
||||
|
@ -23,7 +23,7 @@ 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.fields import ColorField, CounterCacheField
|
||||
from utilities.tracking import TrackingModelMixin
|
||||
from .device_components import *
|
||||
from .mixins import RenderConfigMixin
|
||||
@ -582,13 +582,8 @@ class Device(
|
||||
verbose_name=_('name'),
|
||||
max_length=64,
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
_name = NaturalOrderingField(
|
||||
target_field='name',
|
||||
max_length=100,
|
||||
blank=True,
|
||||
null=True
|
||||
null=True,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
serial = models.CharField(
|
||||
max_length=50,
|
||||
@ -775,7 +770,7 @@ class Device(
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ('_name', 'pk') # Name may be null
|
||||
ordering = ('name', 'pk') # Name may be null
|
||||
constraints = (
|
||||
models.UniqueConstraint(
|
||||
Lower('name'), 'site', 'tenant',
|
||||
@ -1320,7 +1315,8 @@ class VirtualChassis(PrimaryModel):
|
||||
)
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=64
|
||||
max_length=64,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
domain = models.CharField(
|
||||
verbose_name=_('domain'),
|
||||
@ -1382,7 +1378,8 @@ class VirtualDeviceContext(PrimaryModel):
|
||||
)
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=64
|
||||
max_length=64,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
status = models.CharField(
|
||||
verbose_name=_('status'),
|
||||
|
@ -36,7 +36,8 @@ class PowerPanel(ContactsMixin, ImageAttachmentsMixin, PrimaryModel):
|
||||
)
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100
|
||||
max_length=100,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
|
||||
prerequisite_models = (
|
||||
@ -86,7 +87,8 @@ class PowerFeed(PrimaryModel, PathEndpoint, CabledObjectModel):
|
||||
)
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100
|
||||
max_length=100,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
status = models.CharField(
|
||||
verbose_name=_('status'),
|
||||
|
@ -19,7 +19,7 @@ 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 utilities.fields import ColorField
|
||||
from .device_components import PowerPort
|
||||
from .devices import Device, Module
|
||||
from .power import PowerFeed
|
||||
@ -255,12 +255,8 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, RackBase):
|
||||
)
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100
|
||||
)
|
||||
_name = NaturalOrderingField(
|
||||
target_field='name',
|
||||
max_length=100,
|
||||
blank=True
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
facility_id = models.CharField(
|
||||
max_length=50,
|
||||
@ -340,7 +336,7 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, RackBase):
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ('site', 'location', '_name', 'pk') # (site, location, name) may be non-unique
|
||||
ordering = ('site', 'location', 'name', 'pk') # (site, location, name) may be non-unique
|
||||
constraints = (
|
||||
# Name and facility_id must be unique *only* within a Location
|
||||
models.UniqueConstraint(
|
||||
|
@ -8,7 +8,6 @@ from dcim.choices import *
|
||||
from dcim.constants import *
|
||||
from netbox.models import NestedGroupModel, PrimaryModel
|
||||
from netbox.models.features import ContactsMixin, ImageAttachmentsMixin
|
||||
from utilities.fields import NaturalOrderingField
|
||||
|
||||
__all__ = (
|
||||
'Location',
|
||||
@ -143,12 +142,8 @@ class Site(ContactsMixin, ImageAttachmentsMixin, PrimaryModel):
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True,
|
||||
help_text=_("Full name of the site")
|
||||
)
|
||||
_name = NaturalOrderingField(
|
||||
target_field='name',
|
||||
max_length=100,
|
||||
blank=True
|
||||
help_text=_("Full name of the site"),
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
@ -245,7 +240,7 @@ class Site(ContactsMixin, ImageAttachmentsMixin, PrimaryModel):
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ('_name',)
|
||||
ordering = ('name',)
|
||||
verbose_name = _('site')
|
||||
verbose_name_plural = _('sites')
|
||||
|
||||
|
@ -132,7 +132,6 @@ class PlatformTable(NetBoxTable):
|
||||
class DeviceTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
|
||||
name = tables.TemplateColumn(
|
||||
verbose_name=_('Name'),
|
||||
order_by=('_name',),
|
||||
template_code=DEVICE_LINK,
|
||||
linkify=True
|
||||
)
|
||||
@ -288,7 +287,6 @@ class DeviceComponentTable(NetBoxTable):
|
||||
name = tables.Column(
|
||||
verbose_name=_('Name'),
|
||||
linkify=True,
|
||||
order_by=('_name',)
|
||||
)
|
||||
device_status = columns.ChoiceFieldColumn(
|
||||
accessor=tables.A('device__status'),
|
||||
@ -391,7 +389,6 @@ class DeviceConsolePortTable(ConsolePortTable):
|
||||
name = tables.TemplateColumn(
|
||||
verbose_name=_('Name'),
|
||||
template_code='<i class="mdi mdi-console"></i> <a href="{{ record.get_absolute_url }}">{{ value }}</a>',
|
||||
order_by=Accessor('_name'),
|
||||
attrs={'td': {'class': 'text-nowrap'}}
|
||||
)
|
||||
actions = columns.ActionsColumn(
|
||||
@ -433,7 +430,6 @@ class DeviceConsoleServerPortTable(ConsoleServerPortTable):
|
||||
verbose_name=_('Name'),
|
||||
template_code='<i class="mdi mdi-console-network-outline"></i> '
|
||||
'<a href="{{ record.get_absolute_url }}">{{ value }}</a>',
|
||||
order_by=Accessor('_name'),
|
||||
attrs={'td': {'class': 'text-nowrap'}}
|
||||
)
|
||||
actions = columns.ActionsColumn(
|
||||
@ -482,7 +478,6 @@ class DevicePowerPortTable(PowerPortTable):
|
||||
verbose_name=_('Name'),
|
||||
template_code='<i class="mdi mdi-power-plug-outline"></i> <a href="{{ record.get_absolute_url }}">'
|
||||
'{{ value }}</a>',
|
||||
order_by=Accessor('_name'),
|
||||
attrs={'td': {'class': 'text-nowrap'}}
|
||||
)
|
||||
actions = columns.ActionsColumn(
|
||||
@ -531,7 +526,6 @@ class DevicePowerOutletTable(PowerOutletTable):
|
||||
name = tables.TemplateColumn(
|
||||
verbose_name=_('Name'),
|
||||
template_code='<i class="mdi mdi-power-socket"></i> <a href="{{ record.get_absolute_url }}">{{ value }}</a>',
|
||||
order_by=Accessor('_name'),
|
||||
attrs={'td': {'class': 'text-nowrap'}}
|
||||
)
|
||||
actions = columns.ActionsColumn(
|
||||
@ -550,6 +544,11 @@ class DevicePowerOutletTable(PowerOutletTable):
|
||||
|
||||
|
||||
class BaseInterfaceTable(NetBoxTable):
|
||||
name = tables.Column(
|
||||
verbose_name=_('Name'),
|
||||
linkify=True,
|
||||
order_by=('_name',)
|
||||
)
|
||||
enabled = columns.BooleanColumn(
|
||||
verbose_name=_('Enabled'),
|
||||
)
|
||||
@ -597,7 +596,7 @@ class BaseInterfaceTable(NetBoxTable):
|
||||
return ",".join([str(obj) for obj in value.all()])
|
||||
|
||||
|
||||
class InterfaceTable(ModularDeviceComponentTable, BaseInterfaceTable, PathEndpointTable):
|
||||
class InterfaceTable(BaseInterfaceTable, ModularDeviceComponentTable, PathEndpointTable):
|
||||
device = tables.Column(
|
||||
verbose_name=_('Device'),
|
||||
linkify={
|
||||
@ -736,7 +735,6 @@ class DeviceFrontPortTable(FrontPortTable):
|
||||
verbose_name=_('Name'),
|
||||
template_code='<i class="mdi mdi-square-rounded{% if not record.cable %}-outline{% endif %}"></i> '
|
||||
'<a href="{{ record.get_absolute_url }}">{{ value }}</a>',
|
||||
order_by=Accessor('_name'),
|
||||
attrs={'td': {'class': 'text-nowrap'}}
|
||||
)
|
||||
actions = columns.ActionsColumn(
|
||||
@ -783,7 +781,6 @@ class DeviceRearPortTable(RearPortTable):
|
||||
verbose_name=_('Name'),
|
||||
template_code='<i class="mdi mdi-square-rounded{% if not record.cable %}-outline{% endif %}"></i> '
|
||||
'<a href="{{ record.get_absolute_url }}">{{ value }}</a>',
|
||||
order_by=Accessor('_name'),
|
||||
attrs={'td': {'class': 'text-nowrap'}}
|
||||
)
|
||||
actions = columns.ActionsColumn(
|
||||
@ -846,7 +843,6 @@ class DeviceDeviceBayTable(DeviceBayTable):
|
||||
verbose_name=_('Name'),
|
||||
template_code='<i class="mdi mdi-circle{% if record.installed_device %}slice-8{% else %}outline{% endif %}'
|
||||
'"></i> <a href="{{ record.get_absolute_url }}">{{ value }}</a>',
|
||||
order_by=Accessor('_name'),
|
||||
attrs={'td': {'class': 'text-nowrap'}}
|
||||
)
|
||||
actions = columns.ActionsColumn(
|
||||
@ -915,7 +911,6 @@ class DeviceModuleBayTable(ModuleBayTable):
|
||||
name = columns.MPTTColumn(
|
||||
verbose_name=_('Name'),
|
||||
linkify=True,
|
||||
order_by=Accessor('_name')
|
||||
)
|
||||
actions = columns.ActionsColumn(
|
||||
extra_buttons=MODULEBAY_BUTTONS
|
||||
@ -982,7 +977,6 @@ class DeviceInventoryItemTable(InventoryItemTable):
|
||||
verbose_name=_('Name'),
|
||||
template_code='<a href="{{ record.get_absolute_url }}" style="padding-left: {{ record.level }}0px">'
|
||||
'{{ value }}</a>',
|
||||
order_by=Accessor('_name'),
|
||||
attrs={'td': {'class': 'text-nowrap'}}
|
||||
)
|
||||
|
||||
|
@ -163,9 +163,7 @@ class ComponentTemplateTable(NetBoxTable):
|
||||
id = tables.Column(
|
||||
verbose_name=_('ID')
|
||||
)
|
||||
name = tables.Column(
|
||||
order_by=('_name',)
|
||||
)
|
||||
name = tables.Column()
|
||||
|
||||
class Meta(NetBoxTable.Meta):
|
||||
exclude = ('id', )
|
||||
@ -220,6 +218,10 @@ class PowerOutletTemplateTable(ComponentTemplateTable):
|
||||
|
||||
|
||||
class InterfaceTemplateTable(ComponentTemplateTable):
|
||||
name = tables.Column(
|
||||
verbose_name=_('Name'),
|
||||
order_by=('_name',)
|
||||
)
|
||||
enabled = columns.BooleanColumn(
|
||||
verbose_name=_('Enabled'),
|
||||
)
|
||||
|
@ -111,7 +111,6 @@ class RackTypeTable(NetBoxTable):
|
||||
class RackTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
|
||||
name = tables.Column(
|
||||
verbose_name=_('Name'),
|
||||
order_by=('_name',),
|
||||
linkify=True
|
||||
)
|
||||
location = tables.Column(
|
||||
|
@ -688,8 +688,7 @@ class RackElevationListView(generic.ObjectListView):
|
||||
sort = request.GET.get('sort', 'name')
|
||||
if sort not in ORDERING_CHOICES:
|
||||
sort = 'name'
|
||||
sort_field = sort.replace("name", "_name") # Use natural ordering
|
||||
racks = racks.order_by(sort_field)
|
||||
racks = racks.order_by(sort)
|
||||
|
||||
# Pagination
|
||||
per_page = get_paginate_count(request)
|
||||
@ -731,8 +730,8 @@ class RackView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
peer_racks = peer_racks.filter(location=instance.location)
|
||||
else:
|
||||
peer_racks = peer_racks.filter(location__isnull=True)
|
||||
next_rack = peer_racks.filter(_name__gt=instance._name).first()
|
||||
prev_rack = peer_racks.filter(_name__lt=instance._name).reverse().first()
|
||||
next_rack = peer_racks.filter(name__gt=instance.name).first()
|
||||
prev_rack = peer_racks.filter(name__lt=instance.name).reverse().first()
|
||||
|
||||
# Determine any additional parameters to pass when embedding the rack elevations
|
||||
svg_extra = '&'.join([
|
||||
|
32
netbox/ipam/migrations/0076_natural_ordering.py
Normal file
32
netbox/ipam/migrations/0076_natural_ordering.py
Normal file
@ -0,0 +1,32 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ipam', '0075_vlan_qinq'),
|
||||
('dcim', '0197_natural_sort_collation'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='asnrange',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='routetarget',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=21, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vlangroup',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vrf',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100),
|
||||
),
|
||||
]
|
@ -16,7 +16,8 @@ class ASNRange(OrganizationalModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
|
@ -35,7 +35,8 @@ class VLANGroup(OrganizationalModel):
|
||||
"""
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100
|
||||
max_length=100,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
|
@ -18,7 +18,8 @@ class VRF(PrimaryModel):
|
||||
"""
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100
|
||||
max_length=100,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
rd = models.CharField(
|
||||
max_length=VRF_RD_MAX_LENGTH,
|
||||
@ -74,7 +75,8 @@ class RouteTarget(PrimaryModel):
|
||||
verbose_name=_('name'),
|
||||
max_length=VRF_RD_MAX_LENGTH, # Same format options as VRF RD (RFC 4360 section 4)
|
||||
unique=True,
|
||||
help_text=_('Route target value (formatted in accordance with RFC 4360)')
|
||||
help_text=_('Route target value (formatted in accordance with RFC 4360)'),
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
tenant = models.ForeignKey(
|
||||
to='tenancy.Tenant',
|
||||
|
27
netbox/tenancy/migrations/0017_natural_ordering.py
Normal file
27
netbox/tenancy/migrations/0017_natural_ordering.py
Normal file
@ -0,0 +1,27 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tenancy', '0016_charfield_null_choices'),
|
||||
('dcim', '0197_natural_sort_collation'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='contact',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='tenant',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='tenantgroup',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
]
|
@ -56,7 +56,8 @@ class Contact(PrimaryModel):
|
||||
)
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100
|
||||
max_length=100,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
title = models.CharField(
|
||||
verbose_name=_('title'),
|
||||
|
@ -18,7 +18,8 @@ class TenantGroup(NestedGroupModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
@ -39,7 +40,8 @@ class Tenant(ContactsMixin, PrimaryModel):
|
||||
"""
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100
|
||||
max_length=100,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
|
@ -5,7 +5,6 @@ from django.db import models
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from utilities.ordering import naturalize
|
||||
from .forms.widgets import ColorSelect
|
||||
from .validators import ColorValidator
|
||||
|
||||
@ -40,7 +39,7 @@ class NaturalOrderingField(models.CharField):
|
||||
"""
|
||||
description = "Stores a representation of its target field suitable for natural ordering"
|
||||
|
||||
def __init__(self, target_field, naturalize_function=naturalize, *args, **kwargs):
|
||||
def __init__(self, target_field, naturalize_function, *args, **kwargs):
|
||||
self.target_field = target_field
|
||||
self.naturalize_function = naturalize_function
|
||||
super().__init__(*args, **kwargs)
|
||||
|
@ -25,7 +25,6 @@ class ComponentType(NetBoxObjectType):
|
||||
"""
|
||||
Base type for device/VM components
|
||||
"""
|
||||
_name: str
|
||||
virtual_machine: Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]
|
||||
|
||||
|
||||
@ -77,7 +76,6 @@ class ClusterTypeType(OrganizationalObjectType):
|
||||
filters=VirtualMachineFilter
|
||||
)
|
||||
class VirtualMachineType(ConfigContextMixin, ContactsMixin, NetBoxObjectType):
|
||||
_name: str
|
||||
interface_count: BigInt
|
||||
virtual_disk_count: BigInt
|
||||
interface_count: BigInt
|
||||
@ -102,6 +100,7 @@ class VirtualMachineType(ConfigContextMixin, ContactsMixin, NetBoxObjectType):
|
||||
filters=VMInterfaceFilter
|
||||
)
|
||||
class VMInterfaceType(IPAddressesMixin, ComponentType):
|
||||
_name: str
|
||||
mac_address: str | None
|
||||
parent: Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')] | None
|
||||
bridge: Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')] | None
|
||||
|
43
netbox/virtualization/migrations/0046_natural_ordering.py
Normal file
43
netbox/virtualization/migrations/0046_natural_ordering.py
Normal file
@ -0,0 +1,43 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('virtualization', '0045_clusters_cached_relations'),
|
||||
('dcim', '0197_natural_sort_collation'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='virtualmachine',
|
||||
options={'ordering': ('name', 'pk')},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='virtualdisk',
|
||||
options={'ordering': ('virtual_machine', 'name')},
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='virtualmachine',
|
||||
name='_name',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='virtualdisk',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='virtualmachine',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='cluster',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100),
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='virtualdisk',
|
||||
name='_name',
|
||||
),
|
||||
]
|
@ -50,7 +50,8 @@ class Cluster(ContactsMixin, CachedScopeMixin, PrimaryModel):
|
||||
"""
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100
|
||||
max_length=100,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
type = models.ForeignKey(
|
||||
verbose_name=_('type'),
|
||||
|
@ -69,12 +69,8 @@ class VirtualMachine(ContactsMixin, ImageAttachmentsMixin, RenderConfigMixin, Co
|
||||
)
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=64
|
||||
)
|
||||
_name = NaturalOrderingField(
|
||||
target_field='name',
|
||||
max_length=100,
|
||||
blank=True
|
||||
max_length=64,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
status = models.CharField(
|
||||
max_length=50,
|
||||
@ -152,7 +148,7 @@ class VirtualMachine(ContactsMixin, ImageAttachmentsMixin, RenderConfigMixin, Co
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ('_name', 'pk') # Name may be non-unique
|
||||
ordering = ('name', 'pk') # Name may be non-unique
|
||||
constraints = (
|
||||
models.UniqueConstraint(
|
||||
Lower('name'), 'cluster', 'tenant',
|
||||
@ -273,13 +269,8 @@ class ComponentModel(NetBoxModel):
|
||||
)
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=64
|
||||
)
|
||||
_name = NaturalOrderingField(
|
||||
target_field='name',
|
||||
naturalize_function=naturalize_interface,
|
||||
max_length=100,
|
||||
blank=True
|
||||
max_length=64,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
description = models.CharField(
|
||||
verbose_name=_('description'),
|
||||
@ -289,7 +280,6 @@ class ComponentModel(NetBoxModel):
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
ordering = ('virtual_machine', CollateAsChar('_name'))
|
||||
constraints = (
|
||||
models.UniqueConstraint(
|
||||
fields=('virtual_machine', 'name'),
|
||||
@ -311,10 +301,9 @@ class ComponentModel(NetBoxModel):
|
||||
|
||||
|
||||
class VMInterface(ComponentModel, BaseInterface, TrackingModelMixin):
|
||||
virtual_machine = models.ForeignKey(
|
||||
to='virtualization.VirtualMachine',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='interfaces' # Override ComponentModel
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=64,
|
||||
)
|
||||
_name = NaturalOrderingField(
|
||||
target_field='name',
|
||||
@ -322,6 +311,11 @@ class VMInterface(ComponentModel, BaseInterface, TrackingModelMixin):
|
||||
max_length=100,
|
||||
blank=True
|
||||
)
|
||||
virtual_machine = models.ForeignKey(
|
||||
to='virtualization.VirtualMachine',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='interfaces' # Override ComponentModel
|
||||
)
|
||||
ip_addresses = GenericRelation(
|
||||
to='ipam.IPAddress',
|
||||
content_type_field='assigned_object_type',
|
||||
@ -358,6 +352,7 @@ class VMInterface(ComponentModel, BaseInterface, TrackingModelMixin):
|
||||
class Meta(ComponentModel.Meta):
|
||||
verbose_name = _('interface')
|
||||
verbose_name_plural = _('interfaces')
|
||||
ordering = ('virtual_machine', CollateAsChar('_name'))
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
@ -416,3 +411,4 @@ class VirtualDisk(ComponentModel, TrackingModelMixin):
|
||||
class Meta(ComponentModel.Meta):
|
||||
verbose_name = _('virtual disk')
|
||||
verbose_name_plural = _('virtual disks')
|
||||
ordering = ('virtual_machine', 'name')
|
||||
|
@ -53,7 +53,6 @@ VMINTERFACE_BUTTONS = """
|
||||
class VirtualMachineTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
|
||||
name = tables.Column(
|
||||
verbose_name=_('Name'),
|
||||
order_by=('_name',),
|
||||
linkify=True
|
||||
)
|
||||
status = columns.ChoiceFieldColumn(
|
||||
|
47
netbox/vpn/migrations/0007_natural_ordering.py
Normal file
47
netbox/vpn/migrations/0007_natural_ordering.py
Normal file
@ -0,0 +1,47 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('vpn', '0006_charfield_null_choices'),
|
||||
('dcim', '0197_natural_sort_collation'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='ikepolicy',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ikeproposal',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ipsecpolicy',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ipsecprofile',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ipsecproposal',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='l2vpn',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='tunnel',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
]
|
@ -22,7 +22,8 @@ class IKEProposal(PrimaryModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
authentication_method = models.CharField(
|
||||
verbose_name=('authentication method'),
|
||||
@ -67,7 +68,8 @@ class IKEPolicy(PrimaryModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
version = models.PositiveSmallIntegerField(
|
||||
verbose_name=_('version'),
|
||||
@ -125,7 +127,8 @@ class IPSecProposal(PrimaryModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
encryption_algorithm = models.CharField(
|
||||
verbose_name=_('encryption'),
|
||||
@ -176,7 +179,8 @@ class IPSecPolicy(PrimaryModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
proposals = models.ManyToManyField(
|
||||
to='vpn.IPSecProposal',
|
||||
@ -211,7 +215,8 @@ class IPSecProfile(PrimaryModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
mode = models.CharField(
|
||||
verbose_name=_('mode'),
|
||||
|
@ -20,7 +20,8 @@ class L2VPN(ContactsMixin, PrimaryModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
|
@ -31,7 +31,8 @@ class Tunnel(PrimaryModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
status = models.CharField(
|
||||
verbose_name=_('status'),
|
||||
|
17
netbox/wireless/migrations/0012_natural_ordering.py
Normal file
17
netbox/wireless/migrations/0012_natural_ordering.py
Normal file
@ -0,0 +1,17 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wireless', '0011_wirelesslan__location_wirelesslan__region_and_more'),
|
||||
('dcim', '0197_natural_sort_collation'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='wirelesslangroup',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
]
|
@ -52,7 +52,8 @@ class WirelessLANGroup(NestedGroupModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
|
Loading…
Reference in New Issue
Block a user