From bdb3838d71606cb28d83b82dc5323641788ea4ad Mon Sep 17 00:00:00 2001 From: chambersh1129 Date: Sun, 29 Sep 2019 01:15:46 -0400 Subject: [PATCH] Replace all instances of .extra() in QuerySets with annotations, including references in docstrings --- netbox/ipam/querysets.py | 2 +- netbox/ipam/views.py | 9 +++++---- netbox/utilities/managers.py | 11 ++++++----- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/netbox/ipam/querysets.py b/netbox/ipam/querysets.py index 9fd9bb6c1..3a48be789 100644 --- a/netbox/ipam/querysets.py +++ b/netbox/ipam/querysets.py @@ -6,7 +6,7 @@ class PrefixQuerySet(QuerySet): def annotate_depth(self, limit=None): """ Iterate through a QuerySet of Prefixes and annotate the hierarchical level of each. While it would be preferable - to do this using .extra() on the QuerySet to count the unique parents of each prefix, that approach introduces + to do this using .annotate() on the QuerySet to count the unique parents of each prefix, that approach introduces performance issues at scale. Because we're adding a non-field attribute to the model, annotation must be made *after* any QuerySet diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 4a4d6ad34..d744fff04 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -2,6 +2,7 @@ import netaddr from django.conf import settings from django.contrib.auth.mixins import PermissionRequiredMixin from django.db.models import Count, Q +from django.db.models.expressions import RawSQL from django.shortcuts import get_object_or_404, redirect, render from django.views.generic import View from django_tables2 import RequestConfig @@ -291,10 +292,10 @@ class RIRBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class AggregateListView(PermissionRequiredMixin, ObjectListView): permission_required = 'ipam.view_aggregate' - # TODO: Replace raw SQL - queryset = Aggregate.objects.prefetch_related('rir').extra(select={ - 'child_count': 'SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', - }) + queryset = Aggregate.objects.prefetch_related('rir').annotate( + child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ()) + ) + filter = filters.AggregateFilter filter_form = forms.AggregateFilterForm table = tables.AggregateDetailTable diff --git a/netbox/utilities/managers.py b/netbox/utilities/managers.py index fc2497063..ad646a78e 100644 --- a/netbox/utilities/managers.py +++ b/netbox/utilities/managers.py @@ -1,4 +1,5 @@ from django.db.models import Manager +from django.db.models.expressions import RawSQL NAT1 = r"CAST(SUBSTRING({}.{} FROM '^(\d{{1,9}})') AS integer)" NAT2 = r"SUBSTRING({}.{} FROM '^\d*(.*?)\d*$')" @@ -21,11 +22,11 @@ class NaturalOrderingManager(Manager): db_field = self.natural_order_field # Append the three subfields derived from the designated natural ordering field - queryset = queryset.extra(select={ - '_nat1': NAT1.format(db_table, db_field), - '_nat2': NAT2.format(db_table, db_field), - '_nat3': NAT3.format(db_table, db_field), - }) + queryset = ( + queryset.annotate(_nat1=RawSQL(NAT1.format(db_table, db_field), ())) + .annotate(_nat2=RawSQL(NAT2.format(db_table, db_field), ())) + .annotate(_nat3=RawSQL(NAT3.format(db_table, db_field), ())) + ) # Replace any instance of the designated natural ordering field with its three subfields ordering = []