gault-millau/apps/utils/querysets.py

80 lines
3.0 KiB
Python

"""Utils QuerySet Mixins"""
from functools import reduce
from operator import add
from django.db import models
from django.db.models import Q, F
from utils.methods import get_contenttype
from utils.models import TJSONField
class ContentTypeQuerySetMixin(models.QuerySet):
"""QuerySet for ContentType"""
def by_object_id(self, object_id: int):
"""Filter by object_id"""
return self.filter(object_id=object_id)
def by_content_type(self, app_label: str = 'favorites', model: str = 'favorites'):
"""Filter QuerySet by ContentType."""
return self.filter(content_type=get_contenttype(app_label=app_label,
model=model))
class RelatedObjectsCountMixin(models.QuerySet):
"""QuerySet for ManyToMany related count filter"""
def _get_related_objects_names(self):
"""Get all related objects (with reversed)"""
related_objects_names = []
for related_object in self.model._meta.related_objects:
if isinstance(related_object, models.ManyToManyRel):
related_objects_names.append(related_object.name)
return related_objects_names
def _annotate_related_objects_count(self):
"""Annotate all related objects to queryset"""
annotations = {}
for related_object in self._get_related_objects_names():
annotations[f"{related_object}_count"] = models.Count(f"{related_object}")
return self.annotate(**annotations)
def filter_related_gt(self, count):
"""QuerySet filter by related objects count"""
q_objects = Q()
for related_object in self._get_related_objects_names():
q_objects.add(Q(**{f"{related_object}_count__gt": count}), Q.OR)
return self._annotate_related_objects_count().filter(q_objects)
def filter_all_related_gt(self, count):
"""Queryset filter by all related objects count"""
exp = reduce(add, [F(f"{related_object}_count") for related_object in self._get_related_objects_names()])
return self._annotate_related_objects_count()\
.annotate(all_related_count=exp)\
.filter(all_related_count__gt=count)
class TranslationQuerysetMixin(models.QuerySet):
def _get_translatable_fields(self):
"""Get translatable fields from model."""
translatable_fields = []
model = self.model
for field in model._meta.fields:
field_name = field.name
if isinstance(field, TJSONField):
translatable_fields.append(field_name)
return translatable_fields
def _get_translatable_field_filters(self, locale: str):
"""Generate filters for filtering TJSON objects that has necessary locale."""
return {f'{field}__has_key': locale for field in self._get_translatable_fields()}
def has_translation(self, locale: str):
"""Filter objects by locale from request."""
return self.filter(**self._get_translatable_field_filters(locale))