Move count_related() & dict_to_filter_params() to utilities.query

This commit is contained in:
Jeremy Stretch 2024-03-21 14:34:37 -04:00
parent 3b4898adea
commit ae8df77cc8
14 changed files with 71 additions and 63 deletions

View File

@ -6,7 +6,7 @@ from dcim.views import PathTraceView
from netbox.views import generic from netbox.views import generic
from tenancy.views import ObjectContactsView from tenancy.views import ObjectContactsView
from utilities.forms import ConfirmationForm from utilities.forms import ConfirmationForm
from utilities.utils import count_related from utilities.query import count_related
from utilities.views import register_model_view from utilities.views import register_model_view
from . import filtersets, forms, tables from . import filtersets, forms, tables
from .models import * from .models import *

View File

@ -25,7 +25,7 @@ from netbox.views import generic
from netbox.views.generic.base import BaseObjectView from netbox.views.generic.base import BaseObjectView
from netbox.views.generic.mixins import TableMixin from netbox.views.generic.mixins import TableMixin
from utilities.forms import ConfirmationForm from utilities.forms import ConfirmationForm
from utilities.utils import count_related from utilities.query import count_related
from utilities.views import ContentTypePermissionRequiredMixin, register_model_view from utilities.views import ContentTypePermissionRequiredMixin, register_model_view
from . import filtersets, forms, tables from . import filtersets, forms, tables
from .models import * from .models import *

View File

@ -25,8 +25,8 @@ from tenancy.views import ObjectContactsView
from utilities.forms import ConfirmationForm from utilities.forms import ConfirmationForm
from utilities.paginator import EnhancedPaginator, get_paginate_count from utilities.paginator import EnhancedPaginator, get_paginate_count
from utilities.permissions import get_permission_for_model from utilities.permissions import get_permission_for_model
from utilities.query import count_related
from utilities.query_functions import CollateAsChar from utilities.query_functions import CollateAsChar
from utilities.utils import count_related
from utilities.views import GetReturnURLMixin, ObjectPermissionRequiredMixin, ViewTab, register_model_view from utilities.views import GetReturnURLMixin, ObjectPermissionRequiredMixin, ViewTab, register_model_view
from virtualization.models import VirtualMachine from virtualization.models import VirtualMachine
from . import filtersets, forms, tables from . import filtersets, forms, tables

View File

@ -21,11 +21,11 @@ from netbox.views.generic.mixins import TableMixin
from utilities.data import shallow_compare_dict from utilities.data import shallow_compare_dict
from utilities.forms import ConfirmationForm, get_field_value from utilities.forms import ConfirmationForm, get_field_value
from utilities.paginator import EnhancedPaginator, get_paginate_count from utilities.paginator import EnhancedPaginator, get_paginate_count
from utilities.query import count_related
from utilities.querydict import normalize_querydict from utilities.querydict import normalize_querydict
from utilities.request import copy_safe_request from utilities.request import copy_safe_request
from utilities.rqworker import get_workers_for_queue from utilities.rqworker import get_workers_for_queue
from utilities.templatetags.builtins.filters import render_markdown from utilities.templatetags.builtins.filters import render_markdown
from utilities.utils import count_related
from utilities.views import ContentTypePermissionRequiredMixin, get_viewname, register_model_view from utilities.views import ContentTypePermissionRequiredMixin, get_viewname, register_model_view
from . import filtersets, forms, tables from . import filtersets, forms, tables
from .models import * from .models import *

View File

@ -3,8 +3,8 @@ from django.db.models import Count, F, OuterRef, Q, Subquery, Value
from django.db.models.expressions import RawSQL from django.db.models.expressions import RawSQL
from django.db.models.functions import Round from django.db.models.functions import Round
from utilities.query import count_related
from utilities.querysets import RestrictedQuerySet from utilities.querysets import RestrictedQuerySet
from utilities.utils import count_related
__all__ = ( __all__ = (
'ASNRangeQuerySet', 'ASNRangeQuerySet',

View File

@ -9,8 +9,8 @@ from circuits.models import Provider
from dcim.filtersets import InterfaceFilterSet from dcim.filtersets import InterfaceFilterSet
from dcim.models import Interface, Site from dcim.models import Interface, Site
from netbox.views import generic from netbox.views import generic
from utilities.query import count_related
from utilities.tables import get_table_ordering from utilities.tables import get_table_ordering
from utilities.utils import count_related
from utilities.views import ViewTab, register_model_view from utilities.views import ViewTab, register_model_view
from virtualization.filtersets import VMInterfaceFilterSet from virtualization.filtersets import VMInterfaceFilterSet
from virtualization.models import VMInterface from virtualization.models import VMInterface

View File

@ -3,7 +3,8 @@ from django.shortcuts import get_object_or_404
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from netbox.views import generic from netbox.views import generic
from utilities.utils import count_related, get_related_models from utilities.query import count_related
from utilities.utils import get_related_models
from utilities.views import register_model_view, ViewTab from utilities.views import register_model_view, ViewTab
from . import filtersets, forms, tables from . import filtersets, forms, tables
from .models import * from .models import *

View File

@ -11,8 +11,9 @@ from rest_framework.views import get_view_name as drf_get_view_name
from extras.constants import HTTP_CONTENT_TYPE_JSON from extras.constants import HTTP_CONTENT_TYPE_JSON
from netbox.api.exceptions import GraphQLTypeNotFound, SerializerNotFound from netbox.api.exceptions import GraphQLTypeNotFound, SerializerNotFound
from netbox.api.fields import RelatedObjectCountField from netbox.api.fields import RelatedObjectCountField
from .query import count_related, dict_to_filter_params
from .string import title from .string import title
from .utils import count_related, dict_to_filter_params, dynamic_import from .utils import dynamic_import
__all__ = ( __all__ = (
'get_annotations_for_serializer', 'get_annotations_for_serializer',

56
netbox/utilities/query.py Normal file
View File

@ -0,0 +1,56 @@
from django.db.models import Count, OuterRef, Subquery
from django.db.models.functions import Coalesce
__all__ = (
'count_related',
'dict_to_filter_params',
)
def count_related(model, field):
"""
Return a Subquery suitable for annotating a child object count.
"""
subquery = Subquery(
model.objects.filter(
**{field: OuterRef('pk')}
).order_by().values(
field
).annotate(
c=Count('*')
).values('c')
)
return Coalesce(subquery, 0)
def dict_to_filter_params(d, prefix=''):
"""
Translate a dictionary of attributes to a nested set of parameters suitable for QuerySet filtering. For example:
{
"name": "Foo",
"rack": {
"facility_id": "R101"
}
}
Becomes:
{
"name": "Foo",
"rack__facility_id": "R101"
}
And can be employed as filter parameters:
Device.objects.filter(**dict_to_filter(attrs_dict))
"""
params = {}
for key, val in d.items():
k = prefix + key
if isinstance(val, dict):
params.update(dict_to_filter_params(val, k + '__'))
else:
params[k] = val
return params

View File

@ -2,8 +2,8 @@ from django.http import QueryDict
from django.test import TestCase from django.test import TestCase
from utilities.data import deepmerge from utilities.data import deepmerge
from utilities.query import dict_to_filter_params
from utilities.querydict import normalize_querydict from utilities.querydict import normalize_querydict
from utilities.utils import dict_to_filter_params
class DictToFilterParamsTest(TestCase): class DictToFilterParamsTest(TestCase):

View File

@ -1,5 +1,4 @@
from django.db.models import Count, ManyToOneRel, OuterRef, Subquery from django.db.models import ManyToOneRel
from django.db.models.functions import Coalesce
from django.utils import timezone from django.utils import timezone
from django.utils.timezone import localtime from django.utils.timezone import localtime
@ -17,55 +16,6 @@ def dynamic_import(name):
return mod return mod
def count_related(model, field):
"""
Return a Subquery suitable for annotating a child object count.
"""
subquery = Subquery(
model.objects.filter(
**{field: OuterRef('pk')}
).order_by().values(
field
).annotate(
c=Count('*')
).values('c')
)
return Coalesce(subquery, 0)
def dict_to_filter_params(d, prefix=''):
"""
Translate a dictionary of attributes to a nested set of parameters suitable for QuerySet filtering. For example:
{
"name": "Foo",
"rack": {
"facility_id": "R101"
}
}
Becomes:
{
"name": "Foo",
"rack__facility_id": "R101"
}
And can be employed as filter parameters:
Device.objects.filter(**dict_to_filter(attrs_dict))
"""
params = {}
for key, val in d.items():
k = prefix + key
if isinstance(val, dict):
params.update(dict_to_filter_params(val, k + '__'))
else:
params[k] = val
return params
def content_type_name(ct, include_app=True): def content_type_name(ct, include_app=True):
""" """
Return a human-friendly ContentType name (e.g. "DCIM > Site"). Return a human-friendly ContentType name (e.g. "DCIM > Site").

View File

@ -18,8 +18,8 @@ from ipam.tables import InterfaceVLANTable
from netbox.constants import DEFAULT_ACTION_PERMISSIONS from netbox.constants import DEFAULT_ACTION_PERMISSIONS
from netbox.views import generic from netbox.views import generic
from tenancy.views import ObjectContactsView from tenancy.views import ObjectContactsView
from utilities.query import count_related
from utilities.query_functions import CollateAsChar from utilities.query_functions import CollateAsChar
from utilities.utils import count_related
from utilities.views import ViewTab, register_model_view from utilities.views import ViewTab, register_model_view
from . import filtersets, forms, tables from . import filtersets, forms, tables
from .models import * from .models import *

View File

@ -1,7 +1,7 @@
from ipam.tables import RouteTargetTable from ipam.tables import RouteTargetTable
from netbox.views import generic from netbox.views import generic
from tenancy.views import ObjectContactsView from tenancy.views import ObjectContactsView
from utilities.utils import count_related from utilities.query import count_related
from utilities.views import register_model_view from utilities.views import register_model_view
from . import filtersets, forms, tables from . import filtersets, forms, tables
from .models import * from .models import *

View File

@ -1,6 +1,6 @@
from dcim.models import Interface from dcim.models import Interface
from netbox.views import generic from netbox.views import generic
from utilities.utils import count_related from utilities.query import count_related
from utilities.views import register_model_view from utilities.views import register_model_view
from . import filtersets, forms, tables from . import filtersets, forms, tables
from .models import * from .models import *