mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-11 21:10:29 +01:00
Remove NaturalOrderingManager #3096
Closed
opened 2025-12-29 18:25:34 +01:00 by adam
·
12 comments
No Branch/Tag Specified
main
update-changelog-comments-docs
feature-removal-issue-type
20911-dropdown
20239-plugin-menu-classes-mutable-state
21097-graphql-id-lookups
feature
fix_module_substitution
20923-dcim-templates
20044-elevation-stuck-lightmode
feature-ip-prefix-link
v4.5-beta1-release
20068-import-moduletype-attrs
20766-fix-german-translation-code-literals
20378-del-script
7604-filter-modifiers-v3
circuit-swap
12318-case-insensitive-uniqueness
20637-improve-device-q-filter
20660-script-load
19724-graphql
20614-update-ruff
14884-script
02496-max-page
19720-macaddress-interface-generic-relation
19408-circuit-terminations-export-templates
20203-openapi-check
fix-19669-api-image-download
7604-filter-modifiers
19275-fixes-interface-bulk-edit
fix-17794-get_field_value_return_list
11507-show-aggregate-and-rir-on-api
9583-add_column_specific_search_field_to_tables
v4.5.0
v4.4.10
v4.4.9
v4.5.0-beta1
v4.4.8
v4.4.7
v4.4.6
v4.4.5
v4.4.4
v4.4.3
v4.4.2
v4.4.1
v4.4.0
v4.3.7
v4.4.0-beta1
v4.3.6
v4.3.5
v4.3.4
v4.3.3
v4.3.2
v4.3.1
v4.3.0
v4.2.9
v4.3.0-beta2
v4.2.8
v4.3.0-beta1
v4.2.7
v4.2.6
v4.2.5
v4.2.4
v4.2.3
v4.2.2
v4.2.1
v4.2.0
v4.1.11
v4.1.10
v4.1.9
v4.1.8
v4.2-beta1
v4.1.7
v4.1.6
v4.1.5
v4.1.4
v4.1.3
v4.1.2
v4.1.1
v4.1.0
v4.0.11
v4.0.10
v4.0.9
v4.1-beta1
v4.0.8
v4.0.7
v4.0.6
v4.0.5
v4.0.3
v4.0.2
v4.0.1
v4.0.0
v3.7.8
v3.7.7
v4.0-beta2
v3.7.6
v3.7.5
v4.0-beta1
v3.7.4
v3.7.3
v3.7.2
v3.7.1
v3.7.0
v3.6.9
v3.6.8
v3.6.7
v3.7-beta1
v3.6.6
v3.6.5
v3.6.4
v3.6.3
v3.6.2
v3.6.1
v3.6.0
v3.5.9
v3.6-beta2
v3.5.8
v3.6-beta1
v3.5.7
v3.5.6
v3.5.5
v3.5.4
v3.5.3
v3.5.2
v3.5.1
v3.5.0
v3.4.10
v3.4.9
v3.5-beta2
v3.4.8
v3.5-beta1
v3.4.7
v3.4.6
v3.4.5
v3.4.4
v3.4.3
v3.4.2
v3.4.1
v3.4.0
v3.3.10
v3.3.9
v3.4-beta1
v3.3.8
v3.3.7
v3.3.6
v3.3.5
v3.3.4
v3.3.3
v3.3.2
v3.3.1
v3.3.0
v3.2.9
v3.2.8
v3.3-beta2
v3.2.7
v3.3-beta1
v3.2.6
v3.2.5
v3.2.4
v3.2.3
v3.2.2
v3.2.1
v3.2.0
v3.1.11
v3.1.10
v3.2-beta2
v3.1.9
v3.2-beta1
v3.1.8
v3.1.7
v3.1.6
v3.1.5
v3.1.4
v3.1.3
v3.1.2
v3.1.1
v3.1.0
v3.0.12
v3.0.11
v3.0.10
v3.1-beta1
v3.0.9
v3.0.8
v3.0.7
v3.0.6
v3.0.5
v3.0.4
v3.0.3
v3.0.2
v3.0.1
v3.0.0
v2.11.12
v3.0-beta2
v2.11.11
v2.11.10
v3.0-beta1
v2.11.9
v2.11.8
v2.11.7
v2.11.6
v2.11.5
v2.11.4
v2.11.3
v2.11.2
v2.11.1
v2.11.0
v2.10.10
v2.10.9
v2.11-beta1
v2.10.8
v2.10.7
v2.10.6
v2.10.5
v2.10.4
v2.10.3
v2.10.2
v2.10.1
v2.10.0
v2.9.11
v2.10-beta2
v2.9.10
v2.10-beta1
v2.9.9
v2.9.8
v2.9.7
v2.9.6
v2.9.5
v2.9.4
v2.9.3
v2.9.2
v2.9.1
v2.9.0
v2.9-beta2
v2.8.9
v2.9-beta1
v2.8.8
v2.8.7
v2.8.6
v2.8.5
v2.8.4
v2.8.3
v2.8.2
v2.8.1
v2.8.0
v2.7.12
v2.7.11
v2.7.10
v2.7.9
v2.7.8
v2.7.7
v2.7.6
v2.7.5
v2.7.4
v2.7.3
v2.7.2
v2.7.1
v2.7.0
v2.6.12
v2.6.11
v2.6.10
v2.6.9
v2.7-beta1
Solcon-2020-01-06
v2.6.8
v2.6.7
v2.6.6
v2.6.5
v2.6.4
v2.6.3
v2.6.2
v2.6.1
v2.6.0
v2.5.13
v2.5.12
v2.6-beta1
v2.5.11
v2.5.10
v2.5.9
v2.5.8
v2.5.7
v2.5.6
v2.5.5
v2.5.4
v2.5.3
v2.5.2
v2.5.1
v2.5.0
v2.4.9
v2.5-beta2
v2.4.8
v2.5-beta1
v2.4.7
v2.4.6
v2.4.5
v2.4.4
v2.4.3
v2.4.2
v2.4.1
v2.4.0
v2.3.7
v2.4-beta1
v2.3.6
v2.3.5
v2.3.4
v2.3.3
v2.3.2
v2.3.1
v2.3.0
v2.2.10
v2.3-beta2
v2.2.9
v2.3-beta1
v2.2.8
v2.2.7
v2.2.6
v2.2.5
v2.2.4
v2.2.3
v2.2.2
v2.2.1
v2.2.0
v2.1.6
v2.2-beta2
v2.1.5
v2.2-beta1
v2.1.4
v2.1.3
v2.1.2
v2.1.1
v2.1.0
v2.0.10
v2.1-beta1
v2.0.9
v2.0.8
v2.0.7
v2.0.6
v2.0.5
v2.0.4
v2.0.3
v2.0.2
v2.0.1
v2.0.0
v2.0-beta3
v1.9.6
v1.9.5
v2.0-beta2
v1.9.4-r1
v1.9.3
v2.0-beta1
v1.9.2
v1.9.1
v1.9.0-r1
v1.8.4
v1.8.3
v1.8.2
v1.8.1
v1.8.0
v1.7.3
v1.7.2-r1
v1.7.1
v1.7.0
v1.6.3
v1.6.2-r1
v1.6.1-r1
1.6.1
v1.6.0
v1.5.2
v1.5.1
v1.5.0
v1.4.2
v1.4.1
v1.4.0
v1.3.2
v1.3.1
v1.3.0
v1.2.2
v1.2.1
v1.2.0
v1.1.0
v1.0.7-r1
v1.0.7
v1.0.6
v1.0.5
v1.0.4
v1.0.3-r1
v1.0.3
1.0.0
Labels
Clear labels
beta
breaking change
complexity: high
complexity: low
complexity: medium
needs milestone
netbox
pending closure
plugin candidate
pull-request
severity: high
severity: low
severity: medium
status: accepted
status: backlog
status: blocked
status: duplicate
status: needs owner
status: needs triage
status: revisions needed
status: under review
topic: GraphQL
topic: Internationalization
topic: OpenAPI
topic: UI/UX
topic: cabling
topic: event rules
topic: htmx navigation
topic: industrialization
topic: migrations
topic: plugins
topic: scripts
topic: templating
topic: testing
type: bug
type: deprecation
type: documentation
type: feature
type: housekeeping
type: translation
Mirrored from GitHub Pull Request
Milestone
No items
No Milestone
Projects
Clear projects
No project
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: starred/netbox#3096
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @jeremystretch on GitHub (Dec 27, 2019).
Originally assigned to: @jeremystretch on GitHub.
Proposed Changes
Replace
NaturalOrderingManagerwith a simpler and more efficient approach: ordering first by string length and then by string. This article provides a concise example.Justification
NaturalOrderingManagerwas introduced some time ago to effect the natural ordering of various models. (For example, ensure that Router10 appears in a list before Router2; the default alphabetic ordering does not do this). It works by splitting a field into its leading integers (if any), middle part, and trailing integers (if any), and ordering the three discrete values independently.Although this approach works, it incurs significant performance penalty, which is visible when inspecting SQL queries. Casual testing with the Device model saw a drop from ~66ms to ~7ms per query using the length-based approach. Further, ordering is removed entirely during
COUNT()queries as we are no longer modifying the default queryset returned by the manager.I believe the only drawback to this approach is that we'll lose natural ordering on integer prefixes, which seems reasonable given that it's not a very common requirement, and ordering on trailing integers is presumably a much more common use case.
@steffann commented on GitHub (Dec 27, 2019):
I like the idea, but it doesn't work in some common cases. For example:
Besides: Juniper EX and QFX change the interface name based on the type of transceiver, so there will be a mix of
xe-0/0/0',ge-0/0/1` etc. That will not be sorted properly.For Cisco gear with both
GigabitEthernet1/1andTenGigabitEthernet1/1the new ordering would be nice though :)@steffann commented on GitHub (Dec 27, 2019):
How about creating an extra field called
_sortable_namethat transforms the interface name to something the database can easily sort on?For example left-padding every sequence of integers to a length of 4 or 5 digits (anybody has interface numbers >99999?). Or an ArrayField like:
It can be automatically populated on
save()and after that sorting on it wouldn't cost any performance.@jeremystretch commented on GitHub (Dec 27, 2019):
NaturalOrderingManager isn't used for interfaces; the Interface model has its own jumble of regex that it uses (see #3097). This is for generic natural ordering.
@steffann commented on GitHub (Dec 27, 2019):
/me was confused. Please ignore 🙂
@jeremystretch commented on GitHub (Dec 27, 2019):
Come up with a solution for #3097 and all is forgiven! 😆
@jeremystretch commented on GitHub (Jan 3, 2020):
I have the code for this done in the
3799-natural-orderingbranch. However, I've run into a suspected bug in Django that causes an exception when ordering by a ForeignKey to a model which orders by a function (e.g.Length()). I've refrained from submitting a PR since the code as it currently stands breaks several views.This is on hold until I hear back regarding the bug report linked above. Worst case, it should be possible to work around this by cooking up a custom wrapper that provides a
__getitem__method to avoid the exception.@jeremystretch commented on GitHub (Jan 3, 2020):
After poking at this some more, it unfortunately doesn't seem that we can work around it with a wrapper. However, we can fall back to referencing each parent model's ordering explicitly. For instance:
However, great would be needed to ensure the child's ordering for the parent never deviates from the parent's native ordering.
@jeremystretch commented on GitHub (Jan 6, 2020):
After some experimentation, I haven't been able to come up with a suitable workaround. This is a confirmed bug that has been resolved in Django 3.0 but is not eligible for back-porting to Django 2.2.
Marking this as blocked by #3848.
@candlerb commented on GitHub (Jan 28, 2020):
Presumably this should say "after" not "before".
However, unless I've missed something, I don't think that sorting by length and then by text works at all. Consider the following example:
bar1foo1bar10foo10switch1firewall1That list is sorted by length first, then by text. However, I would expect "firewall1" to become before "switch1", and also before "foo". I would also expect "bar10" to come before "foo1".
To optimise sorting without explicitly splitting the device name into two parts then index expressions are probably the best approach. But I think you'd need an expression which separates the input into (text part, number part) as a composite type rather than just using the length.
@jeremystretch commented on GitHub (Jan 28, 2020):
I don't recall exactly what I was doing earlier, but it seemed to work at the time. It's likely I didn't have sufficiently diverse test data.
This should be a common enough problem that I'd expect to find plenty of existing solutions for reference. Might just need to dig a bit more.
@jeremystretch commented on GitHub (Feb 4, 2020):
After poking at this some more, I think our best chance at an efficient, maintainable solution will be to normalize the sort field at write time and store that value in a separate column. For example,
Router12would becomerouter00000012, which would get stored in a field separate fromname.While this is trivial to achieve inside a model's
save()method, I would like to preservecreate()andbulk_create()functionality to allow for better performance when populating test data. I haven't tried it yet, but we should be able to leveragepre_save()on the sort field to populate the normalized value on demand.I envision something like this:
This would require a one-time migration to calculate all normalized values in bulk, but that should not be a disruptive process (we've performed similar operations in prior releases).
@jeremystretch commented on GitHub (Feb 7, 2020):
Testing of the initial implementation is very promising:
I was also able to fully replicate the existing ordering logic (at least according to the tests we have in place).