| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374 |
- from django.db.models import Count, OuterRef, Subquery, QuerySet
- from django.db.models.functions import Coalesce
- from utilities.mptt import TreeManager
- __all__ = (
- 'count_related',
- 'dict_to_filter_params',
- 'reapply_model_ordering',
- )
- 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 reapply_model_ordering(queryset: QuerySet) -> QuerySet:
- """
- Reapply model-level ordering in case it has been lost through .annotate().
- https://code.djangoproject.com/ticket/32811
- """
- # MPTT-based models are exempt from this; use caution when annotating querysets of these models
- if any(isinstance(manager, TreeManager) for manager in queryset.model._meta.local_managers):
- return queryset
- elif queryset.ordered:
- return queryset
- ordering = queryset.model._meta.ordering
- return queryset.order_by(*ordering)
|