query.py 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. from django.db.models import Count, OuterRef, Subquery, QuerySet
  2. from django.db.models.functions import Coalesce
  3. from utilities.mptt import TreeManager
  4. __all__ = (
  5. 'count_related',
  6. 'dict_to_filter_params',
  7. 'reapply_model_ordering',
  8. )
  9. def count_related(model, field):
  10. """
  11. Return a Subquery suitable for annotating a child object count.
  12. """
  13. subquery = Subquery(
  14. model.objects.filter(
  15. **{field: OuterRef('pk')}
  16. ).order_by().values(
  17. field
  18. ).annotate(
  19. c=Count('*')
  20. ).values('c')
  21. )
  22. return Coalesce(subquery, 0)
  23. def dict_to_filter_params(d, prefix=''):
  24. """
  25. Translate a dictionary of attributes to a nested set of parameters suitable for QuerySet filtering. For example:
  26. {
  27. "name": "Foo",
  28. "rack": {
  29. "facility_id": "R101"
  30. }
  31. }
  32. Becomes:
  33. {
  34. "name": "Foo",
  35. "rack__facility_id": "R101"
  36. }
  37. And can be employed as filter parameters:
  38. Device.objects.filter(**dict_to_filter(attrs_dict))
  39. """
  40. params = {}
  41. for key, val in d.items():
  42. k = prefix + key
  43. if isinstance(val, dict):
  44. params.update(dict_to_filter_params(val, k + '__'))
  45. else:
  46. params[k] = val
  47. return params
  48. def reapply_model_ordering(queryset: QuerySet) -> QuerySet:
  49. """
  50. Reapply model-level ordering in case it has been lost through .annotate().
  51. https://code.djangoproject.com/ticket/32811
  52. """
  53. # MPTT-based models are exempt from this; use caution when annotating querysets of these models
  54. if any(isinstance(manager, TreeManager) for manager in queryset.model._meta.local_managers):
  55. return queryset
  56. elif queryset.ordered:
  57. return queryset
  58. ordering = queryset.model._meta.ordering
  59. return queryset.order_by(*ordering)