Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get all descendants of a node with Django treebeard?

Let's say I have these Models:

class Category(MP_Node):
    name = models.CharField(max_length=30)

class Item(models.Model):
    category = models.ForeignKey(Category)

and I would like to find all Items belonging to any descendant of a given Category.

Usually I would write category.item_set but this is just Items belonging to the given level of the hierarchy.

Using the example tree in the treebeard tutorial, if an Item belongs to "Laptop Memory", how would I find all Items belonging to descendants "Computer Hardware" where "Laptop Memory" is one of those descendants?

like image 661
Alex Rothberg Avatar asked Nov 23 '25 07:11

Alex Rothberg


2 Answers

I just had the same problem and figured out how to do it (consider it inside the function get_queryset of a ListView):

category = Category.objects.filter(slug=self.kwargs['category']).get()
descendants = list(category.get_descendants().all())
return self.model.objects.select_related('category').filter(category__in=descendants+[category, ])

Another option that I came up with was using a filter with 'OR':

from django.db.models import Q

category = Category.objects.filter(slug=self.kwargs['category']).get()
descendants = list(category.get_descendants().all())
return self.model.objects.select_related('category').filter(Q(category__in=category.get_descendants()) | Q(category=category))
like image 122
Massa Avatar answered Nov 25 '25 21:11

Massa


I looked at the treebeard code to see how it gets descendants of a node. We can apply the same filters as a related field lookup.

paramcat = Category.objects.get(id=1) # how you actually get the category will depend on your application
#all items associated with this category OR its descendants:
items = Item.objects.filter(category__tree_id=paramcat.tree_id, category__lft__range=(paramcat.lft,paramcat.rgt-1))

I think using intermediate calls like get_descendants will result in one query per descendant, plus load all descendants into memory. It defeats the purpose of using treebeard in the first place

I'd be interested in seeing a custom lookup based on this code, I'm not sure how to do it...

like image 24
Simone Avatar answered Nov 25 '25 21:11

Simone



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!