feincms / django-tree-queries

Adjacency-list trees for Django using recursive common table expressions. Supports PostgreSQL, sqlite, MySQL and MariaDB.
https://django-tree-queries.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
433 stars 27 forks source link

Advance queries not supported #14

Closed SafaAlfulaij closed 3 years ago

SafaAlfulaij commented 3 years ago

Doing something like: test_field__in=node_field.ancetors() raises an error:

no such column: ....

Check this diff please (contains a "fix"):

diff --git a/tests/testapp/models.py b/tests/testapp/models.py
index 1e5bbb0..f342729 100644
--- a/tests/testapp/models.py
+++ b/tests/testapp/models.py
@@ -30,6 +30,15 @@ class StringOrderedModel(TreeNode):
         return self.name

+class ReferenceModel(models.Model):
+    my_field = models.ForeignKey(
+        Model,
+        on_delete=models.CASCADE,
+        blank=True,
+        null=True,
+    )
+
+
 class AlwaysTreeQueryModelCategory(models.Model):
     pass

diff --git a/tests/testapp/test_queries.py b/tests/testapp/test_queries.py
index 8d14d41..419247d 100644
--- a/tests/testapp/test_queries.py
+++ b/tests/testapp/test_queries.py
@@ -2,7 +2,7 @@ from __future__ import unicode_literals

 from django import forms
 from django.core.exceptions import ValidationError
-from django.db.models import Count, Sum
+from django.db.models import Count, Q, Sum
 from django.test import TestCase

 from tree_queries.compiler import TreeQuery
@@ -11,6 +11,7 @@ from .models import (
     AlwaysTreeQueryModel,
     AlwaysTreeQueryModelCategory,
     Model,
+    ReferenceModel,
     StringOrderedModel,
     UnorderedModel,
 )
@@ -272,3 +273,20 @@ class Test(TestCase):
         m4 = c.instances.get()
         self.assertEqual(m1, m4)
         self.assertEqual(m4.tree_depth, 0)
+
+    def test_reference(self):
+        tree = self.create_tree()
+
+        ReferenceModel.objects.create(my_field=tree.child2_2)
+        obj = ReferenceModel.objects.get()
+
+        self.assertEqual(
+            list(
+                ReferenceModel.objects.filter(
+                    Q(my_field__in=tree.root.ancestors(include_self=True))
+                    | Q(my_field__isnull=False)
+                    | Q(my_field__in=tree.root.descendants(include_self=True))
+                )
+            ),
+            [obj],
+        )

diff --git a/tree_queries/compiler.py b/tree_queries/compiler.py
index db5f785..fdd0281 100644
--- a/tree_queries/compiler.py
+++ b/tree_queries/compiler.py
@@ -139,6 +139,10 @@ class TreeCompiler(SQLCompiler):
         }

         if "__tree" not in self.query.extra_tables:  # pragma: no branch - unlikely
+            custom_params = params.copy()
+            if "base_table" in self.query.__dict__:
+                custom_params["db_table"] = self.query.__dict__["base_table"] # use aliased table name "UX"
+
             self.query.add_extra(
                 # Do not add extra fields to the select statement when it is a
                 # summary query
@@ -150,7 +154,7 @@ class TreeCompiler(SQLCompiler):
                     "tree_ordering": "__tree.tree_ordering",
                 },
                 select_params=None,
-                where=["__tree.tree_pk = {db_table}.{pk}".format(**params)],
+                where=["__tree.tree_pk = {db_table}.{pk}".format(**custom_params)],
                 params=None,
                 tables=["__tree"],
                 order_by=(
matthiask commented 3 years ago

Looks good, do you want to submit a pull request? I'd split up the ReferenceModel.objects.filter() query and maybe add one self.assertEqual for each case (ancestors, isnull and descendants) and maybe also verify that exclude() works too.