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 tenancy.views import ObjectContactsView
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 . import filtersets, forms, tables
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.mixins import TableMixin
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 . import filtersets, forms, tables
from .models import *

View File

@ -25,8 +25,8 @@ from tenancy.views import ObjectContactsView
from utilities.forms import ConfirmationForm
from utilities.paginator import EnhancedPaginator, get_paginate_count
from utilities.permissions import get_permission_for_model
from utilities.query import count_related
from utilities.query_functions import CollateAsChar
from utilities.utils import count_related
from utilities.views import GetReturnURLMixin, ObjectPermissionRequiredMixin, ViewTab, register_model_view
from virtualization.models import VirtualMachine
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.forms import ConfirmationForm, get_field_value
from utilities.paginator import EnhancedPaginator, get_paginate_count
from utilities.query import count_related
from utilities.querydict import normalize_querydict
from utilities.request import copy_safe_request
from utilities.rqworker import get_workers_for_queue
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 . import filtersets, forms, tables
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.functions import Round
from utilities.query import count_related
from utilities.querysets import RestrictedQuerySet
from utilities.utils import count_related
__all__ = (
'ASNRangeQuerySet',

View File

@ -9,8 +9,8 @@ from circuits.models import Provider
from dcim.filtersets import InterfaceFilterSet
from dcim.models import Interface, Site
from netbox.views import generic
from utilities.query import count_related
from utilities.tables import get_table_ordering
from utilities.utils import count_related
from utilities.views import ViewTab, register_model_view
from virtualization.filtersets import VMInterfaceFilterSet
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 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 . import filtersets, forms, tables
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 netbox.api.exceptions import GraphQLTypeNotFound, SerializerNotFound
from netbox.api.fields import RelatedObjectCountField
from .query import count_related, dict_to_filter_params
from .string import title
from .utils import count_related, dict_to_filter_params, dynamic_import
from .utils import dynamic_import
__all__ = (
'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 utilities.data import deepmerge
from utilities.query import dict_to_filter_params
from utilities.querydict import normalize_querydict
from utilities.utils import dict_to_filter_params
class DictToFilterParamsTest(TestCase):

View File

@ -1,5 +1,4 @@
from django.db.models import Count, ManyToOneRel, OuterRef, Subquery
from django.db.models.functions import Coalesce
from django.db.models import ManyToOneRel
from django.utils import timezone
from django.utils.timezone import localtime
@ -17,55 +16,6 @@ def dynamic_import(name):
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):
"""
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.views import generic
from tenancy.views import ObjectContactsView
from utilities.query import count_related
from utilities.query_functions import CollateAsChar
from utilities.utils import count_related
from utilities.views import ViewTab, register_model_view
from . import filtersets, forms, tables
from .models import *

View File

@ -1,7 +1,7 @@
from ipam.tables import RouteTargetTable
from netbox.views import generic
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 . import filtersets, forms, tables
from .models import *

View File

@ -1,6 +1,6 @@
from dcim.models import Interface
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 . import filtersets, forms, tables
from .models import *