petrounias/django-cte-trees

Queryset for ancestors - Remove Extraneous Queries

Opened this issue · 0 comments

This issue can be summarized as having some way of getting a queryset of a Node's ancestors without having to perform a query.

Using these Models:

class Category2(CTENode):
    name = models.CharField(max_length = 128, null = False)

class Device(models.Model):
    category = models.ForeignKey(Category2)

and running Device.objects.filter(category__in=Category2.objects.get(name="bottom").ancestors()), I get two queries rather than one:

WITH RECURSIVE cte ("depth", "path", "ordering", "id") AS (

    SELECT 1 AS depth,
           array[T."id"] AS path,
           array[T."id"] AS ordering,
           T."id"
      FROM app_category2 T
     WHERE T."parent_id" IS NULL

     UNION ALL

    SELECT cte.depth + 1 AS depth,
           cte.path || T."id",
           cte.ordering || array[T."id"],
           T."id"
      FROM app_category2 T
      JOIN cte ON T."parent_id" = cte."id")

    SELECT (cte.ordering) AS "ordering", (cte.path) AS "path", (cte.depth) AS "depth", "app_category2"."id", "app_category2"."parent_id", "app_category2"."name" FROM "app_category2" , "cte" WHERE ((cte."id" = app_category2."id") AND "app_category2"."name" = 'bottom' ); 
SELECT "app_device"."id", "app_device"."category_id" FROM "app_device" WHERE "app_device"."category_id" IN (WITH RECURSIVE cte ("depth", "path", "ordering", "id") AS (

    SELECT 1 AS depth,
           array[T."id"] AS path,
           array[T."id"] AS ordering,
           T."id"
      FROM app_category2 T
     WHERE T."parent_id" IS NULL

     UNION ALL

    SELECT cte.depth + 1 AS depth,
           cte.path || T."id",
           cte.ordering || array[T."id"],
           T."id"
      FROM app_category2 T
      JOIN cte ON T."parent_id" = cte."id")

    SELECT "app_category2"."id" FROM "app_category2" , "cte" WHERE ((cte."id" = app_category2."id") AND "app_category2"."id" IN (2, 3))) LIMIT 21;

I think this is due in part to #3 because Category2.objects.get(name="bottom").ancestors() runs a SQL query rather than just returning a queryset. Is there another way to just get a queryset for the ancestors?

One idea is to use something like this which is a queryset:

Category2.objects.get(name="bottom").values_list('path', flat=True)

but that does not work quite right since I have a queryset of lists.