Skip to content

Commit 73d1a2d

Browse files
Merge pull request #3929 from netbox-community/3830-api-pagination
Fixes #3830: Update model ordering parameters to ensure deterministic ordering
2 parents c202c13 + c5ec470 commit 73d1a2d

File tree

9 files changed

+129
-14
lines changed

9 files changed

+129
-14
lines changed

docs/release-notes/version-2.7.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,10 @@ PATCH) to maintain backward compatibility. This behavior will be discontinued be
237237
* [#3731](https://github.com/digitalocean/netbox/issues/3731) - Change Graph.type to a ContentType foreign key field
238238
* [#3801](https://github.com/digitalocean/netbox/issues/3801) - Use YAML for export of device types
239239

240+
## Bug Fixes
241+
242+
* [#3830](https://github.com/digitalocean/netbox/issues/3830) - Ensure deterministic ordering for all models
243+
240244
## Bug Fixes (From Beta)
241245

242246
* [#3868](https://github.com/digitalocean/netbox/issues/3868) - Fix creation of interfaces for virtual machines
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Generated by Django 2.2.8 on 2020-01-15 18:10
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('dcim', '0088_powerfeed_available_power'),
10+
]
11+
12+
operations = [
13+
migrations.AlterModelOptions(
14+
name='device',
15+
options={'ordering': ('name', 'pk'), 'permissions': (('napalm_read', 'Read-only access to devices via NAPALM'), ('napalm_write', 'Read/write access to devices via NAPALM'))},
16+
),
17+
migrations.AlterModelOptions(
18+
name='rack',
19+
options={'ordering': ('site', 'group', 'name', 'pk')},
20+
),
21+
]

netbox/dcim/models/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -594,8 +594,9 @@ class Rack(ChangeLoggedModel, CustomFieldModel, RackElevationHelperMixin):
594594
}
595595

596596
class Meta:
597-
ordering = ['site', 'group', 'name']
597+
ordering = ('site', 'group', 'name', 'pk') # (site, group, name) may be non-unique
598598
unique_together = [
599+
# Name and facility_id must be unique *only* within a RackGroup
599600
['group', 'name'],
600601
['group', 'facility_id'],
601602
]
@@ -1392,7 +1393,7 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
13921393
}
13931394

13941395
class Meta:
1395-
ordering = ['name']
1396+
ordering = ('name', 'pk') # Name may be NULL
13961397
unique_together = [
13971398
['site', 'tenant', 'name'], # See validate_unique below
13981399
['rack', 'position', 'face'],
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Generated by Django 2.2.8 on 2020-01-15 18:10
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('extras', '0034_configcontext_tags'),
10+
]
11+
12+
operations = [
13+
migrations.AlterModelOptions(
14+
name='customfieldvalue',
15+
options={'ordering': ('obj_type', 'obj_id', 'pk')},
16+
),
17+
migrations.AlterModelOptions(
18+
name='graph',
19+
options={'ordering': ('type', 'weight', 'name', 'pk')},
20+
),
21+
migrations.AlterModelOptions(
22+
name='imageattachment',
23+
options={'ordering': ('name', 'pk')},
24+
),
25+
migrations.AlterModelOptions(
26+
name='webhook',
27+
options={'ordering': ('name',)},
28+
),
29+
]

netbox/extras/models.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ class Webhook(models.Model):
120120
)
121121

122122
class Meta:
123+
ordering = ('name',)
123124
unique_together = ('payload_url', 'type_create', 'type_update', 'type_delete',)
124125

125126
def __str__(self):
@@ -308,8 +309,8 @@ class CustomFieldValue(models.Model):
308309
)
309310

310311
class Meta:
311-
ordering = ['obj_type', 'obj_id']
312-
unique_together = ['field', 'obj_type', 'obj_id']
312+
ordering = ('obj_type', 'obj_id', 'pk') # (obj_type, obj_id) may be non-unique
313+
unique_together = ('field', 'obj_type', 'obj_id')
313314

314315
def __str__(self):
315316
return '{} {}'.format(self.obj, self.field)
@@ -454,7 +455,7 @@ class Graph(models.Model):
454455
)
455456

456457
class Meta:
457-
ordering = ['type', 'weight', 'name']
458+
ordering = ('type', 'weight', 'name', 'pk') # (type, weight, name) may be non-unique
458459

459460
def __str__(self):
460461
return self.name
@@ -623,7 +624,7 @@ class ImageAttachment(models.Model):
623624
)
624625

625626
class Meta:
626-
ordering = ['name']
627+
ordering = ('name', 'pk') # name may be non-unique
627628

628629
def __str__(self):
629630
if self.name:
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Generated by Django 2.2.8 on 2020-01-15 18:10
2+
3+
from django.db import migrations
4+
import django.db.models.expressions
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('ipam', '0032_role_description'),
11+
]
12+
13+
operations = [
14+
migrations.AlterModelOptions(
15+
name='aggregate',
16+
options={'ordering': ('family', 'prefix', 'pk')},
17+
),
18+
migrations.AlterModelOptions(
19+
name='ipaddress',
20+
options={'ordering': ('family', 'address', 'pk'), 'verbose_name': 'IP address', 'verbose_name_plural': 'IP addresses'},
21+
),
22+
migrations.AlterModelOptions(
23+
name='prefix',
24+
options={'ordering': (django.db.models.expressions.OrderBy(django.db.models.expressions.F('vrf'), nulls_first=True), 'family', 'prefix', 'pk'), 'verbose_name_plural': 'prefixes'},
25+
),
26+
migrations.AlterModelOptions(
27+
name='service',
28+
options={'ordering': ('protocol', 'port', 'pk')},
29+
),
30+
migrations.AlterModelOptions(
31+
name='vlan',
32+
options={'ordering': ('site', 'group', 'vid', 'pk'), 'verbose_name': 'VLAN', 'verbose_name_plural': 'VLANs'},
33+
),
34+
migrations.AlterModelOptions(
35+
name='vlangroup',
36+
options={'ordering': ('site', 'name', 'pk'), 'verbose_name': 'VLAN group', 'verbose_name_plural': 'VLAN groups'},
37+
),
38+
migrations.AlterModelOptions(
39+
name='vrf',
40+
options={'ordering': ('name', 'rd', 'pk'), 'verbose_name': 'VRF', 'verbose_name_plural': 'VRFs'},
41+
),
42+
]

netbox/ipam/models.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ class VRF(ChangeLoggedModel, CustomFieldModel):
8787
]
8888

8989
class Meta:
90-
ordering = ['name', 'rd']
90+
ordering = ('name', 'rd', 'pk') # (name, rd) may be non-unique
9191
verbose_name = 'VRF'
9292
verbose_name_plural = 'VRFs'
9393

@@ -189,7 +189,7 @@ class Aggregate(ChangeLoggedModel, CustomFieldModel):
189189
]
190190

191191
class Meta:
192-
ordering = ['family', 'prefix']
192+
ordering = ('family', 'prefix', 'pk') # (family, prefix) may be non-unique
193193

194194
def __str__(self):
195195
return str(self.prefix)
@@ -383,7 +383,7 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
383383
}
384384

385385
class Meta:
386-
ordering = [F('vrf').asc(nulls_first=True), 'family', 'prefix']
386+
ordering = (F('vrf').asc(nulls_first=True), 'family', 'prefix', 'pk') # (vrf, family, prefix) may be non-unique
387387
verbose_name_plural = 'prefixes'
388388

389389
def __str__(self):
@@ -666,7 +666,7 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
666666
}
667667

668668
class Meta:
669-
ordering = ['family', 'address']
669+
ordering = ('family', 'address', 'pk') # (family, address) may be non-unique
670670
verbose_name = 'IP address'
671671
verbose_name_plural = 'IP addresses'
672672

@@ -829,7 +829,7 @@ class VLANGroup(ChangeLoggedModel):
829829
csv_headers = ['name', 'slug', 'site']
830830

831831
class Meta:
832-
ordering = ['site', 'name']
832+
ordering = ('site', 'name', 'pk') # (site, name) may be non-unique
833833
unique_together = [
834834
['site', 'name'],
835835
['site', 'slug'],
@@ -934,7 +934,7 @@ class VLAN(ChangeLoggedModel, CustomFieldModel):
934934
}
935935

936936
class Meta:
937-
ordering = ['site', 'group', 'vid']
937+
ordering = ('site', 'group', 'vid', 'pk') # (site, group, vid) may be non-unique
938938
unique_together = [
939939
['group', 'vid'],
940940
['group', 'name'],
@@ -1037,7 +1037,7 @@ class Service(ChangeLoggedModel, CustomFieldModel):
10371037
csv_headers = ['device', 'virtual_machine', 'name', 'protocol', 'description']
10381038

10391039
class Meta:
1040-
ordering = ['protocol', 'port']
1040+
ordering = ('protocol', 'port', 'pk') # (protocol, port) may be non-unique
10411041

10421042
def __str__(self):
10431043
return '{} ({}/{})'.format(self.name, self.port, self.get_protocol_display())
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Generated by Django 2.2.8 on 2020-01-15 18:10
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('virtualization', '0012_vm_name_nonunique'),
10+
]
11+
12+
operations = [
13+
migrations.AlterModelOptions(
14+
name='virtualmachine',
15+
options={'ordering': ('name', 'pk')},
16+
),
17+
]

netbox/virtualization/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ class VirtualMachine(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
273273
}
274274

275275
class Meta:
276-
ordering = ['name']
276+
ordering = ('name', 'pk') # Name may be non-unique
277277
unique_together = [
278278
['cluster', 'tenant', 'name']
279279
]

0 commit comments

Comments
 (0)