4👍
✅
queryset.annotate(
descendants_count=Floor((F('rght') - F('lft') - 1) / 2)
).values(
'descendants_count'
).aggregate(
total_count=Count('descendants_count')
)
Let me explain
First, current method of get_descendant_count
just operates existing data, so we can use it in Queryset.
def get_descendant_count(self):
"""
Returns the number of descendants this model instance has.
"""
if self._mpttfield('right') is None:
# node not saved yet
return 0
else:
return (self._mpttfield('right') - self._mpttfield('left') - 1) // 2
This is the current mptt models’ method. In queryset we are sure that all of instances is already saved so we’ll skip that.
Next step is to transform math operations into db expressions.
In Django 3.0 appeared Floor
expression. But we can use it even in 1.7 (as I do)
from django.db.models.lookups import Transform
class Floor(Transform):
function = 'FLOOR'
lookup_name = 'floor'
If you want you can refactor this to use self._mpttfield('right')
analog instead of hardcoded rght, lft
and make this as Manager
method
Let’s test. I have top element with descendants
In [1]: m = MenuItem.objects.get(id=settings.TOP_MENU_ID)
In [2]: m.get_descendant_count()
Out[2]: 226
In [3]: n = m.get_descendants()
In [4]: n.annotate(descendants_count=Floor((F('rght') - F('lft') - 1) / 2)).values('descendants_count').aggregate(total_count=Count('descendants_count'))
Out[4]: {'total_count': 226}
Source:stackexchange.com