mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-06 20:17:29 -06:00
Compare commits
4 Commits
35de0c59cd
...
12318-case
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f2f61c90d | ||
|
|
a34553325e | ||
|
|
06052f8eaa | ||
|
|
dac0a06f4f |
97
netbox/circuits/migrations/0053_ci_collations.py
Normal file
97
netbox/circuits/migrations/0053_ci_collations.py
Normal file
@@ -0,0 +1,97 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
PATTERN_OPS_INDEXES = [
|
||||
'circuits_circuitgroup_name_ec8ac1e5_like',
|
||||
'circuits_circuitgroup_slug_61ca866b_like',
|
||||
'circuits_circuittype_name_8256ea9a_like',
|
||||
'circuits_circuittype_slug_9b4b3cf9_like',
|
||||
'circuits_provider_name_8f2514f5_like',
|
||||
'circuits_provider_slug_c3c0aa10_like',
|
||||
'circuits_virtualcircuittype_name_5184db16_like',
|
||||
'circuits_virtualcircuittype_slug_75d5c661_like',
|
||||
]
|
||||
|
||||
|
||||
def remove_indexes(apps, schema_editor):
|
||||
for idx in PATTERN_OPS_INDEXES:
|
||||
schema_editor.execute(f'DROP INDEX IF EXISTS {idx}')
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('circuits', '0052_extend_circuit_abs_distance_upper_limit'),
|
||||
('dcim', '0217_ci_collations'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
code=remove_indexes,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='circuit',
|
||||
name='cid',
|
||||
field=models.CharField(db_collation='case_insensitive', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='circuitgroup',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='circuitgroup',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='circuittype',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='circuittype',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='provider',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='provider',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='provideraccount',
|
||||
name='account',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='provideraccount',
|
||||
name='name',
|
||||
field=models.CharField(blank=True, db_collation='ci_natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='providernetwork',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='virtualcircuit',
|
||||
name='cid',
|
||||
field=models.CharField(db_collation='case_insensitive', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='virtualcircuittype',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='virtualcircuittype',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
]
|
||||
@@ -41,9 +41,10 @@ class Circuit(ContactsMixin, ImageAttachmentsMixin, DistanceMixin, PrimaryModel)
|
||||
ProviderAccount. Circuit port speed and commit rate are measured in Kbps.
|
||||
"""
|
||||
cid = models.CharField(
|
||||
max_length=100,
|
||||
verbose_name=_('circuit ID'),
|
||||
help_text=_('Unique circuit ID')
|
||||
max_length=100,
|
||||
db_collation='case_insensitive',
|
||||
help_text=_('Unique circuit ID'),
|
||||
)
|
||||
provider = models.ForeignKey(
|
||||
to='circuits.Provider',
|
||||
|
||||
@@ -21,13 +21,14 @@ class Provider(ContactsMixin, PrimaryModel):
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True,
|
||||
db_collation='ci_natural_sort',
|
||||
help_text=_('Full name of the provider'),
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation='case_insensitive',
|
||||
)
|
||||
asns = models.ManyToManyField(
|
||||
to='ipam.ASN',
|
||||
@@ -56,13 +57,15 @@ class ProviderAccount(ContactsMixin, PrimaryModel):
|
||||
related_name='accounts'
|
||||
)
|
||||
account = models.CharField(
|
||||
verbose_name=_('account ID'),
|
||||
max_length=100,
|
||||
verbose_name=_('account ID')
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
blank=True
|
||||
db_collation='ci_natural_sort',
|
||||
blank=True,
|
||||
)
|
||||
|
||||
clone_fields = ('provider', )
|
||||
@@ -97,7 +100,7 @@ class ProviderNetwork(PrimaryModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
provider = models.ForeignKey(
|
||||
to='circuits.Provider',
|
||||
|
||||
@@ -34,9 +34,10 @@ class VirtualCircuit(PrimaryModel):
|
||||
A virtual connection between two or more endpoints, delivered across one or more physical circuits.
|
||||
"""
|
||||
cid = models.CharField(
|
||||
max_length=100,
|
||||
verbose_name=_('circuit ID'),
|
||||
help_text=_('Unique circuit ID')
|
||||
max_length=100,
|
||||
db_collation='case_insensitive',
|
||||
help_text=_('Unique circuit ID'),
|
||||
)
|
||||
provider_network = models.ForeignKey(
|
||||
to='circuits.ProviderNetwork',
|
||||
|
||||
30
netbox/core/migrations/0020_ci_collations.py
Normal file
30
netbox/core/migrations/0020_ci_collations.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
PATTERN_OPS_INDEXES = [
|
||||
'core_datasource_name_17788499_like',
|
||||
]
|
||||
|
||||
|
||||
def remove_indexes(apps, schema_editor):
|
||||
for idx in PATTERN_OPS_INDEXES:
|
||||
schema_editor.execute(f'DROP INDEX IF EXISTS {idx}')
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0019_configrevision_active'),
|
||||
('dcim', '0217_ci_collations'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
code=remove_indexes,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='datasource',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
]
|
||||
@@ -38,7 +38,8 @@ class DataSource(JobsMixin, PrimaryModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
type = models.CharField(
|
||||
verbose_name=_('type'),
|
||||
|
||||
26
netbox/dcim/migrations/0217_ci_collations.py
Normal file
26
netbox/dcim/migrations/0217_ci_collations.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from django.contrib.postgres.operations import CreateCollation
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0216_poweroutlettemplate_color'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
# Create a case-insensitive collation
|
||||
CreateCollation(
|
||||
'case_insensitive',
|
||||
provider='icu',
|
||||
locale='und-u-ks-level2',
|
||||
deterministic=False,
|
||||
),
|
||||
# Create a case-insensitive collation with natural sorting
|
||||
CreateCollation(
|
||||
'ci_natural_sort',
|
||||
provider='icu',
|
||||
locale='und-u-kn-true-ks-level2',
|
||||
deterministic=False,
|
||||
),
|
||||
]
|
||||
311
netbox/dcim/migrations/0218_ci_collations.py
Normal file
311
netbox/dcim/migrations/0218_ci_collations.py
Normal file
@@ -0,0 +1,311 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
PATTERN_OPS_INDEXES = [
|
||||
'dcim_devicerole_slug_7952643b_like',
|
||||
'dcim_devicetype_slug_448745bd_like',
|
||||
'dcim_inventoryitemrole_name_4c8cfe6d_like',
|
||||
'dcim_inventoryitemrole_slug_3556c227_like',
|
||||
'dcim_location_slug_352c5472_like',
|
||||
'dcim_manufacturer_name_841fcd92_like',
|
||||
'dcim_manufacturer_slug_00430749_like',
|
||||
'dcim_moduletypeprofile_name_1709c36e_like',
|
||||
'dcim_platform_slug_b0908ae4_like',
|
||||
'dcim_rackrole_name_9077cfcc_like',
|
||||
'dcim_rackrole_slug_40bbcd3a_like',
|
||||
'dcim_racktype_slug_6bbb384a_like',
|
||||
'dcim_region_slug_ff078a66_like',
|
||||
'dcim_site_name_8fe66c76_like',
|
||||
'dcim_site_slug_4412c762_like',
|
||||
'dcim_sitegroup_slug_a11d2b04_like',
|
||||
]
|
||||
|
||||
|
||||
def remove_indexes(apps, schema_editor):
|
||||
for idx in PATTERN_OPS_INDEXES:
|
||||
schema_editor.execute(f'DROP INDEX IF EXISTS {idx}')
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('dcim', '0217_ci_collations'),
|
||||
('extras', '0134_ci_collations'),
|
||||
('ipam', '0083_ci_collations'),
|
||||
('tenancy', '0021_ci_collations'),
|
||||
('virtualization', '0048_populate_mac_addresses'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
code=remove_indexes,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
),
|
||||
migrations.RemoveConstraint(
|
||||
model_name='device',
|
||||
name='dcim_device_unique_name_site_tenant',
|
||||
),
|
||||
migrations.RemoveConstraint(
|
||||
model_name='device',
|
||||
name='dcim_device_unique_name_site',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='consoleport',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='consoleporttemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='consoleserverport',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='consoleserverporttemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='name',
|
||||
field=models.CharField(blank=True, db_collation='ci_natural_sort', max_length=64, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicebay',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicebaytemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicerole',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicerole',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicetype',
|
||||
name='model',
|
||||
field=models.CharField(db_collation='case_insensitive', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicetype',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='frontport',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='frontporttemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interface',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interfacetemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='inventoryitem',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='inventoryitemrole',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='inventoryitemrole',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='inventoryitemtemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='location',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='location',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='manufacturer',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='manufacturer',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='modulebay',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='modulebaytemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='moduletype',
|
||||
name='model',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='moduletypeprofile',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='platform',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='platform',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='powerfeed',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='poweroutlet',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='poweroutlettemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='powerpanel',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='powerport',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='powerporttemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rack',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rackrole',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rackrole',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='racktype',
|
||||
name='model',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='racktype',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rearport',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rearporttemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='region',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='region',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='site',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='site',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='sitegroup',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='sitegroup',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='virtualdevicecontext',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='device',
|
||||
constraint=models.UniqueConstraint(
|
||||
models.F('name'), models.F('site'), models.F('tenant'), name='dcim_device_unique_name_site_tenant'
|
||||
),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='device',
|
||||
constraint=models.UniqueConstraint(
|
||||
models.F('name'),
|
||||
models.F('site'),
|
||||
condition=models.Q(('tenant__isnull', True)),
|
||||
name='dcim_device_unique_name_site',
|
||||
violation_error_message='Device name must be unique per site.',
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -43,10 +43,10 @@ class ComponentTemplateModel(ChangeLoggedModel, TrackingModelMixin):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=64,
|
||||
db_collation='ci_natural_sort',
|
||||
help_text=_(
|
||||
"{module} is accepted as a substitution for the module bay position when attached to a module type."
|
||||
),
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
label = models.CharField(
|
||||
verbose_name=_('label'),
|
||||
|
||||
@@ -52,7 +52,7 @@ class ComponentModel(NetBoxModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=64,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
label = models.CharField(
|
||||
verbose_name=_('label'),
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import decimal
|
||||
import yaml
|
||||
|
||||
from functools import cached_property
|
||||
|
||||
import yaml
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import ValidationError
|
||||
@@ -10,7 +9,6 @@ from django.core.files.storage import default_storage
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
from django.db import models
|
||||
from django.db.models import F, ProtectedError, prefetch_related_objects
|
||||
from django.db.models.functions import Lower
|
||||
from django.db.models.signals import post_save
|
||||
from django.urls import reverse
|
||||
from django.utils.safestring import mark_safe
|
||||
@@ -25,8 +23,8 @@ from extras.querysets import ConfigContextModelQuerySet
|
||||
from netbox.choices import ColorChoices
|
||||
from netbox.config import ConfigItem
|
||||
from netbox.models import NestedGroupModel, OrganizationalModel, PrimaryModel
|
||||
from netbox.models.mixins import WeightMixin
|
||||
from netbox.models.features import ContactsMixin, ImageAttachmentsMixin
|
||||
from netbox.models.mixins import WeightMixin
|
||||
from utilities.fields import ColorField, CounterCacheField
|
||||
from utilities.prefetch import get_prefetchable_fields
|
||||
from utilities.tracking import TrackingModelMixin
|
||||
@@ -34,7 +32,6 @@ from .device_components import *
|
||||
from .mixins import RenderConfigMixin
|
||||
from .modules import Module
|
||||
|
||||
|
||||
__all__ = (
|
||||
'Device',
|
||||
'DeviceRole',
|
||||
@@ -83,11 +80,13 @@ class DeviceType(ImageAttachmentsMixin, PrimaryModel, WeightMixin):
|
||||
)
|
||||
model = models.CharField(
|
||||
verbose_name=_('model'),
|
||||
max_length=100
|
||||
max_length=100,
|
||||
db_collation='case_insensitive',
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
max_length=100
|
||||
max_length=100,
|
||||
db_collation='case_insensitive',
|
||||
)
|
||||
default_platform = models.ForeignKey(
|
||||
to='dcim.Platform',
|
||||
@@ -525,7 +524,7 @@ class Device(
|
||||
max_length=64,
|
||||
blank=True,
|
||||
null=True,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
serial = models.CharField(
|
||||
max_length=50,
|
||||
@@ -721,11 +720,11 @@ class Device(
|
||||
ordering = ('name', 'pk') # Name may be null
|
||||
constraints = (
|
||||
models.UniqueConstraint(
|
||||
Lower('name'), 'site', 'tenant',
|
||||
'name', 'site', 'tenant',
|
||||
name='%(app_label)s_%(class)s_unique_name_site_tenant'
|
||||
),
|
||||
models.UniqueConstraint(
|
||||
Lower('name'), 'site',
|
||||
'name', 'site',
|
||||
name='%(app_label)s_%(class)s_unique_name_site',
|
||||
condition=Q(tenant__isnull=True),
|
||||
violation_error_message=_("Device name must be unique per site.")
|
||||
@@ -1119,7 +1118,7 @@ class VirtualChassis(PrimaryModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=64,
|
||||
db_collation="natural_sort"
|
||||
db_collation='natural_sort',
|
||||
)
|
||||
domain = models.CharField(
|
||||
verbose_name=_('domain'),
|
||||
@@ -1182,7 +1181,7 @@ class VirtualDeviceContext(PrimaryModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=64,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
status = models.CharField(
|
||||
verbose_name=_('status'),
|
||||
|
||||
@@ -31,7 +31,8 @@ class ModuleTypeProfile(PrimaryModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
schema = models.JSONField(
|
||||
blank=True,
|
||||
@@ -72,7 +73,8 @@ class ModuleType(ImageAttachmentsMixin, PrimaryModel, WeightMixin):
|
||||
)
|
||||
model = models.CharField(
|
||||
verbose_name=_('model'),
|
||||
max_length=100
|
||||
max_length=100,
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
part_number = models.CharField(
|
||||
verbose_name=_('part number'),
|
||||
|
||||
@@ -37,7 +37,7 @@ class PowerPanel(ContactsMixin, ImageAttachmentsMixin, PrimaryModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
|
||||
prerequisite_models = (
|
||||
@@ -88,7 +88,7 @@ class PowerFeed(PrimaryModel, PathEndpoint, CabledObjectModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
status = models.CharField(
|
||||
verbose_name=_('status'),
|
||||
|
||||
@@ -137,12 +137,14 @@ class RackType(RackBase):
|
||||
)
|
||||
model = models.CharField(
|
||||
verbose_name=_('model'),
|
||||
max_length=100
|
||||
max_length=100,
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation='case_insensitive',
|
||||
)
|
||||
|
||||
clone_fields = (
|
||||
@@ -262,7 +264,7 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, RackBase):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
facility_id = models.CharField(
|
||||
max_length=50,
|
||||
|
||||
@@ -142,13 +142,14 @@ class Site(ContactsMixin, ImageAttachmentsMixin, PrimaryModel):
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True,
|
||||
help_text=_("Full name of the site"),
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
help_text=_("Full name of the site")
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation='case_insensitive',
|
||||
)
|
||||
status = models.CharField(
|
||||
verbose_name=_('status'),
|
||||
|
||||
114
netbox/extras/migrations/0134_ci_collations.py
Normal file
114
netbox/extras/migrations/0134_ci_collations.py
Normal file
@@ -0,0 +1,114 @@
|
||||
import django.core.validators
|
||||
import re
|
||||
from django.db import migrations, models
|
||||
|
||||
PATTERN_OPS_INDEXES = [
|
||||
'extras_configcontext_name_4bbfe25d_like',
|
||||
'extras_configcontextprofile_name_070de83b_like',
|
||||
'extras_customfield_name_2fe72707_like',
|
||||
'extras_customfieldchoiceset_name_963e63ea_like',
|
||||
'extras_customlink_name_daed2d18_like',
|
||||
'extras_eventrule_name_899453c6_like',
|
||||
'extras_notificationgroup_name_70b0a3f9_like',
|
||||
'extras_savedfilter_name_8a4bbd09_like',
|
||||
'extras_savedfilter_slug_4f93a959_like',
|
||||
'extras_tag_name_9550b3d9_like',
|
||||
'extras_tag_slug_aaa5b7e9_like',
|
||||
'extras_webhook_name_82cf60b5_like',
|
||||
]
|
||||
|
||||
|
||||
def remove_indexes(apps, schema_editor):
|
||||
for idx in PATTERN_OPS_INDEXES:
|
||||
schema_editor.execute(f'DROP INDEX IF EXISTS {idx}')
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('extras', '0133_make_cf_minmax_decimal'),
|
||||
('dcim', '0217_ci_collations'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
code=remove_indexes,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='configcontext',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='configcontextprofile',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='customfield',
|
||||
name='name',
|
||||
field=models.CharField(
|
||||
db_collation='ci_natural_sort',
|
||||
max_length=50,
|
||||
unique=True,
|
||||
validators=[
|
||||
django.core.validators.RegexValidator(
|
||||
flags=re.RegexFlag['IGNORECASE'],
|
||||
message='Only alphanumeric characters and underscores are allowed.',
|
||||
regex='^[a-z0-9_]+$',
|
||||
),
|
||||
django.core.validators.RegexValidator(
|
||||
flags=re.RegexFlag['IGNORECASE'],
|
||||
inverse_match=True,
|
||||
message='Double underscores are not permitted in custom field names.',
|
||||
regex='__',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='customfieldchoiceset',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='customlink',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='eventrule',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=150, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='notificationgroup',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='savedfilter',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='savedfilter',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='tag',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='tag',
|
||||
name='slug',
|
||||
field=models.SlugField(allow_unicode=True, db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='webhook',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=150, unique=True),
|
||||
),
|
||||
]
|
||||
@@ -35,7 +35,8 @@ class ConfigContextProfile(SyncedDataMixin, PrimaryModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
description = models.CharField(
|
||||
verbose_name=_('description'),
|
||||
@@ -77,7 +78,8 @@ class ConfigContext(SyncedDataMixin, CloningMixin, CustomLinksMixin, ChangeLogge
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
profile = models.ForeignKey(
|
||||
to='extras.ConfigContextProfile',
|
||||
|
||||
@@ -94,6 +94,7 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
|
||||
verbose_name=_('name'),
|
||||
max_length=50,
|
||||
unique=True,
|
||||
db_collation='ci_natural_sort',
|
||||
help_text=_('Internal field name'),
|
||||
validators=(
|
||||
RegexValidator(
|
||||
@@ -779,7 +780,8 @@ class CustomFieldChoiceSet(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel
|
||||
"""
|
||||
name = models.CharField(
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
description = models.CharField(
|
||||
max_length=200,
|
||||
|
||||
@@ -59,7 +59,8 @@ class EventRule(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLogged
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=150,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
description = models.CharField(
|
||||
verbose_name=_('description'),
|
||||
@@ -164,7 +165,8 @@ class Webhook(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedMo
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=150,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
description = models.CharField(
|
||||
verbose_name=_('description'),
|
||||
@@ -307,7 +309,8 @@ class CustomLink(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
enabled = models.BooleanField(
|
||||
verbose_name=_('enabled'),
|
||||
@@ -468,12 +471,14 @@ class SavedFilter(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation='case_insensitive',
|
||||
)
|
||||
description = models.CharField(
|
||||
verbose_name=_('description'),
|
||||
|
||||
@@ -125,7 +125,8 @@ class NotificationGroup(ChangeLoggedModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
description = models.CharField(
|
||||
verbose_name=_('description'),
|
||||
|
||||
@@ -2,7 +2,7 @@ from django.conf import settings
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.utils.text import slugify
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _, pgettext_lazy
|
||||
from taggit.models import TagBase, GenericTaggedItemBase
|
||||
|
||||
from netbox.choices import ColorChoices
|
||||
@@ -25,6 +25,21 @@ class Tag(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel, TagBase):
|
||||
id = models.BigAutoField(
|
||||
primary_key=True
|
||||
)
|
||||
# Override TagBase.name to set db_collation
|
||||
name = models.CharField(
|
||||
verbose_name=pgettext_lazy("A tag name", "name"),
|
||||
unique=True,
|
||||
max_length=100,
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
# Override TagBase.slug to set db_collation
|
||||
slug = models.SlugField(
|
||||
verbose_name=pgettext_lazy("A tag slug", "slug"),
|
||||
unique=True,
|
||||
max_length=100,
|
||||
allow_unicode=True,
|
||||
db_collation='case_insensitive',
|
||||
)
|
||||
color = ColorField(
|
||||
verbose_name=_('color'),
|
||||
default=ColorChoices.COLOR_GREY
|
||||
|
||||
100
netbox/ipam/migrations/0083_ci_collations.py
Normal file
100
netbox/ipam/migrations/0083_ci_collations.py
Normal file
@@ -0,0 +1,100 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
PATTERN_OPS_INDEXES = [
|
||||
'ipam_asnrange_name_c7585e73_like',
|
||||
'ipam_asnrange_slug_c8a7d8a1_like',
|
||||
'ipam_rir_name_64a71982_like',
|
||||
'ipam_rir_slug_ff1a369a_like',
|
||||
'ipam_role_name_13784849_like',
|
||||
'ipam_role_slug_309ca14c_like',
|
||||
'ipam_routetarget_name_212be79f_like',
|
||||
'ipam_servicetemplate_name_1a2f3410_like',
|
||||
'ipam_vlangroup_slug_40abcf6b_like',
|
||||
'ipam_vlantranslationpolicy_name_17e0a007_like',
|
||||
'ipam_vrf_rd_0ac1bde1_like',
|
||||
]
|
||||
|
||||
|
||||
def remove_indexes(apps, schema_editor):
|
||||
for idx in PATTERN_OPS_INDEXES:
|
||||
schema_editor.execute(f'DROP INDEX IF EXISTS {idx}')
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ipam', '0082_add_prefix_network_containment_indexes'),
|
||||
('dcim', '0217_ci_collations'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
code=remove_indexes,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='asnrange',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='asnrange',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rir',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rir',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='role',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='role',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='routetarget',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=21, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='servicetemplate',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vlan',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vlangroup',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vlangroup',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vlantranslationpolicy',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vrf',
|
||||
name='rd',
|
||||
field=models.CharField(blank=True, db_collation='case_insensitive', max_length=21, null=True, unique=True),
|
||||
),
|
||||
]
|
||||
@@ -18,12 +18,7 @@ class ASNRange(OrganizationalModel):
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True,
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
rir = models.ForeignKey(
|
||||
to='ipam.RIR',
|
||||
|
||||
@@ -50,7 +50,8 @@ class ServiceTemplate(ServiceBase, PrimaryModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
||||
@@ -37,11 +37,12 @@ class VLANGroup(OrganizationalModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
max_length=100
|
||||
max_length=100,
|
||||
db_collation='case_insensitive',
|
||||
)
|
||||
scope_type = models.ForeignKey(
|
||||
to='contenttypes.ContentType',
|
||||
@@ -214,7 +215,8 @@ class VLAN(PrimaryModel):
|
||||
)
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=64
|
||||
max_length=64,
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
tenant = models.ForeignKey(
|
||||
to='tenancy.Tenant',
|
||||
@@ -362,6 +364,7 @@ class VLANTranslationPolicy(PrimaryModel):
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True,
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
||||
@@ -19,11 +19,12 @@ class VRF(PrimaryModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
db_collation="natural_sort"
|
||||
db_collation='natural_sort',
|
||||
)
|
||||
rd = models.CharField(
|
||||
max_length=VRF_RD_MAX_LENGTH,
|
||||
unique=True,
|
||||
db_collation='case_insensitive',
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name=_('route distinguisher'),
|
||||
@@ -75,8 +76,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,
|
||||
db_collation='ci_natural_sort',
|
||||
help_text=_('Route target value (formatted in accordance with RFC 4360)'),
|
||||
db_collation="natural_sort"
|
||||
)
|
||||
tenant = models.ForeignKey(
|
||||
to='tenancy.Tenant',
|
||||
|
||||
@@ -153,11 +153,13 @@ class NestedGroupModel(NetBoxFeatureSet, MPTTModel):
|
||||
)
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100
|
||||
max_length=100,
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
max_length=100
|
||||
max_length=100,
|
||||
db_collation='case_insensitive',
|
||||
)
|
||||
description = models.CharField(
|
||||
verbose_name=_('description'),
|
||||
@@ -202,12 +204,14 @@ class OrganizationalModel(NetBoxModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation='case_insensitive',
|
||||
)
|
||||
description = models.CharField(
|
||||
verbose_name=_('description'),
|
||||
|
||||
70
netbox/tenancy/migrations/0021_ci_collations.py
Normal file
70
netbox/tenancy/migrations/0021_ci_collations.py
Normal file
@@ -0,0 +1,70 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
PATTERN_OPS_INDEXES = [
|
||||
'tenancy_contactgroup_slug_5b0f3e75_like',
|
||||
'tenancy_contactrole_name_44b01a1f_like',
|
||||
'tenancy_contactrole_slug_c5837d7d_like',
|
||||
'tenancy_tenant_slug_0716575e_like',
|
||||
'tenancy_tenantgroup_name_53363199_like',
|
||||
'tenancy_tenantgroup_slug_e2af1cb6_like',
|
||||
]
|
||||
|
||||
|
||||
def remove_indexes(apps, schema_editor):
|
||||
for idx in PATTERN_OPS_INDEXES:
|
||||
schema_editor.execute(f'DROP INDEX IF EXISTS {idx}')
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tenancy', '0020_remove_contactgroupmembership'),
|
||||
('dcim', '0217_ci_collations'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
code=remove_indexes,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='contactgroup',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='contactgroup',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='contactrole',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='contactrole',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='tenant',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='tenant',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='tenantgroup',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='tenantgroup',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
]
|
||||
@@ -55,7 +55,7 @@ class Contact(PrimaryModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
db_collation="natural_sort"
|
||||
db_collation='natural_sort',
|
||||
)
|
||||
title = models.CharField(
|
||||
verbose_name=_('title'),
|
||||
|
||||
@@ -19,12 +19,13 @@ class TenantGroup(NestedGroupModel):
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort'
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation='case_insensitive'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
@@ -41,11 +42,12 @@ class Tenant(ContactsMixin, PrimaryModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
max_length=100
|
||||
max_length=100,
|
||||
db_collation='case_insensitive',
|
||||
)
|
||||
group = models.ForeignKey(
|
||||
to='tenancy.TenantGroup',
|
||||
|
||||
92
netbox/virtualization/migrations/0049_ci_collations.py
Normal file
92
netbox/virtualization/migrations/0049_ci_collations.py
Normal file
@@ -0,0 +1,92 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
PATTERN_OPS_INDEXES = [
|
||||
'virtualization_clustergroup_name_4fcd26b4_like',
|
||||
'virtualization_clustergroup_slug_57ca1d23_like',
|
||||
'virtualization_clustertype_name_ea854d3d_like',
|
||||
'virtualization_clustertype_slug_8ee4d0e0_like',
|
||||
]
|
||||
|
||||
|
||||
def remove_indexes(apps, schema_editor):
|
||||
for idx in PATTERN_OPS_INDEXES:
|
||||
schema_editor.execute(f'DROP INDEX IF EXISTS {idx}')
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('dcim', '0217_ci_collations'),
|
||||
('extras', '0134_ci_collations'),
|
||||
('ipam', '0083_ci_collations'),
|
||||
('tenancy', '0021_ci_collations'),
|
||||
('virtualization', '0048_populate_mac_addresses'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
code=remove_indexes,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
),
|
||||
migrations.RemoveConstraint(
|
||||
model_name='virtualmachine',
|
||||
name='virtualization_virtualmachine_unique_name_cluster_tenant',
|
||||
),
|
||||
migrations.RemoveConstraint(
|
||||
model_name='virtualmachine',
|
||||
name='virtualization_virtualmachine_unique_name_cluster',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='cluster',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='clustergroup',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='clustergroup',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='clustertype',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='clustertype',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='virtualdisk',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='virtualmachine',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=64),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='virtualmachine',
|
||||
constraint=models.UniqueConstraint(
|
||||
models.F('name'),
|
||||
models.F('cluster'),
|
||||
models.F('tenant'),
|
||||
name='virtualization_virtualmachine_unique_name_cluster_tenant',
|
||||
),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='virtualmachine',
|
||||
constraint=models.UniqueConstraint(
|
||||
models.F('name'),
|
||||
models.F('cluster'),
|
||||
condition=models.Q(('tenant__isnull', True)),
|
||||
name='virtualization_virtualmachine_unique_name_cluster',
|
||||
violation_error_message='Virtual machine name must be unique per cluster.',
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -51,7 +51,7 @@ class Cluster(ContactsMixin, CachedScopeMixin, PrimaryModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
type = models.ForeignKey(
|
||||
verbose_name=_('type'),
|
||||
|
||||
@@ -5,7 +5,6 @@ from django.core.exceptions import ValidationError
|
||||
from django.core.validators import MinValueValidator
|
||||
from django.db import models
|
||||
from django.db.models import Q, Sum
|
||||
from django.db.models.functions import Lower
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from dcim.models import BaseInterface
|
||||
@@ -70,7 +69,7 @@ class VirtualMachine(ContactsMixin, ImageAttachmentsMixin, RenderConfigMixin, Co
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=64,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
status = models.CharField(
|
||||
max_length=50,
|
||||
@@ -156,11 +155,11 @@ class VirtualMachine(ContactsMixin, ImageAttachmentsMixin, RenderConfigMixin, Co
|
||||
ordering = ('name', 'pk') # Name may be non-unique
|
||||
constraints = (
|
||||
models.UniqueConstraint(
|
||||
Lower('name'), 'cluster', 'tenant',
|
||||
'name', 'cluster', 'tenant',
|
||||
name='%(app_label)s_%(class)s_unique_name_cluster_tenant'
|
||||
),
|
||||
models.UniqueConstraint(
|
||||
Lower('name'), 'cluster',
|
||||
'name', 'cluster',
|
||||
name='%(app_label)s_%(class)s_unique_name_cluster',
|
||||
condition=Q(tenant__isnull=True),
|
||||
violation_error_message=_("Virtual machine name must be unique per cluster.")
|
||||
@@ -275,7 +274,7 @@ class ComponentModel(NetBoxModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=64,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
description = models.CharField(
|
||||
verbose_name=_('description'),
|
||||
|
||||
84
netbox/vpn/migrations/0010_ci_collations.py
Normal file
84
netbox/vpn/migrations/0010_ci_collations.py
Normal file
@@ -0,0 +1,84 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
PATTERN_OPS_INDEXES = [
|
||||
'vpn_ikepolicy_name_5124aa3b_like',
|
||||
'vpn_ikeproposal_name_254623b7_like',
|
||||
'vpn_ipsecpolicy_name_cf28a1aa_like',
|
||||
'vpn_ipsecprofile_name_3ac63c72_like',
|
||||
'vpn_ipsecproposal_name_2fb98e2b_like',
|
||||
'vpn_l2vpn_name_8824eda5_like',
|
||||
'vpn_l2vpn_slug_76b5a174_like',
|
||||
'vpn_tunnel_name_f060beab_like',
|
||||
'vpn_tunnelgroup_name_9f6ebf92_like',
|
||||
'vpn_tunnelgroup_slug_9e614d62_like',
|
||||
]
|
||||
|
||||
|
||||
def remove_indexes(apps, schema_editor):
|
||||
for idx in PATTERN_OPS_INDEXES:
|
||||
schema_editor.execute(f'DROP INDEX IF EXISTS {idx}')
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0217_ci_collations'),
|
||||
('vpn', '0009_remove_redundant_indexes'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
code=remove_indexes,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ikepolicy',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ikeproposal',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ipsecpolicy',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ipsecprofile',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ipsecproposal',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='l2vpn',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='l2vpn',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='tunnel',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='tunnelgroup',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='tunnelgroup',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
]
|
||||
@@ -23,7 +23,7 @@ class IKEProposal(PrimaryModel):
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
authentication_method = models.CharField(
|
||||
verbose_name=('authentication method'),
|
||||
@@ -69,7 +69,7 @@ class IKEPolicy(PrimaryModel):
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
version = models.PositiveSmallIntegerField(
|
||||
verbose_name=_('version'),
|
||||
@@ -128,7 +128,7 @@ class IPSecProposal(PrimaryModel):
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
encryption_algorithm = models.CharField(
|
||||
verbose_name=_('encryption'),
|
||||
@@ -180,7 +180,7 @@ class IPSecPolicy(PrimaryModel):
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
proposals = models.ManyToManyField(
|
||||
to='vpn.IPSecProposal',
|
||||
@@ -216,7 +216,7 @@ class IPSecProfile(PrimaryModel):
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
mode = models.CharField(
|
||||
verbose_name=_('mode'),
|
||||
|
||||
@@ -20,12 +20,13 @@ class L2VPN(ContactsMixin, PrimaryModel):
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation='case_insensitive',
|
||||
)
|
||||
type = models.CharField(
|
||||
verbose_name=_('type'),
|
||||
|
||||
@@ -32,7 +32,7 @@ class Tunnel(ContactsMixin, PrimaryModel):
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
status = models.CharField(
|
||||
verbose_name=_('status'),
|
||||
|
||||
36
netbox/wireless/migrations/0016_ci_collations.py
Normal file
36
netbox/wireless/migrations/0016_ci_collations.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
PATTERN_OPS_INDEXES = [
|
||||
'wireless_wirelesslangroup_name_2ffd60c8_like',
|
||||
'wireless_wirelesslangroup_slug_f5d59831_like',
|
||||
]
|
||||
|
||||
|
||||
def remove_indexes(apps, schema_editor):
|
||||
for idx in PATTERN_OPS_INDEXES:
|
||||
schema_editor.execute(f'DROP INDEX IF EXISTS {idx}')
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0217_ci_collations'),
|
||||
('wireless', '0015_extend_wireless_link_abs_distance_upper_limit'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
code=remove_indexes,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='wirelesslangroup',
|
||||
name='name',
|
||||
field=models.CharField(db_collation='ci_natural_sort', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='wirelesslangroup',
|
||||
name='slug',
|
||||
field=models.SlugField(db_collation='case_insensitive', max_length=100, unique=True),
|
||||
),
|
||||
]
|
||||
@@ -53,12 +53,13 @@ class WirelessLANGroup(NestedGroupModel):
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True,
|
||||
db_collation="natural_sort"
|
||||
db_collation='ci_natural_sort',
|
||||
)
|
||||
slug = models.SlugField(
|
||||
verbose_name=_('slug'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
unique=True,
|
||||
db_collation='case_insensitive',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
||||
Reference in New Issue
Block a user