Skip to content

Commit

Permalink
Add support for descending sibling ordering, multi-field sibling orde…
Browse files Browse the repository at this point in the history
…ring, and related field sibling ordering (#62)

* Update compiler.py to support ordering options

Update the compiler to support descending, and multi-field ordering through the use of the ROW_NUMBER() SQL window expression.

* Update query.py to support multi-field ordering

Update the order_siblings_by method to take a list of fields

* Update compiler.py

Fix sibling_order_as_sql

* Update compiler.py

Remove the quoting of the order_by param because its now handled by django when creating the ROW_NUMBER() SQL

* Update compiler.py

added tuple handling to sibling_order_as_sql

* Update query.py

Fix description of order_siblings_by to reflect support of multi-field ordering

* Update compiler.py

Modified the compiler to accept multi-direction and multi-field ordering

* Update compiler.py

Updated compiler to use a basic django query to help write the __rank_table used for ordering

* Update models.py

Added models for testing ordering based on a related field

* Update test_queries.py

Added tests for ordering by descending order, ordering by a related field, and ordering by multiple fields

* Update test_queries.py

Make ordering during test_attributes deterministic
  • Loading branch information
rhomboss authored Mar 26, 2024
1 parent 4ac70b5 commit bc17cd4
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 128 deletions.
14 changes: 14 additions & 0 deletions tests/testapp/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,17 @@ class Meta:

class InheritConcreteGrandChildModel(InheritAbstractChildModel):
pass


class RelatedOrderModel(TreeNode):
name = models.CharField(max_length=100)


class OneToOneRelatedOrder(models.Model):
relatedmodel = models.OneToOneField(
RelatedOrderModel,
on_delete=models.CASCADE,
primary_key=True,
related_name="related",
)
order = models.PositiveIntegerField(default=0)
96 changes: 94 additions & 2 deletions tests/testapp/test_queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
TreeNodeIsOptional,
UnorderedModel,
UUIDModel,
RelatedOrderModel,
OneToOneRelatedOrder,
)
from tree_queries.compiler import SEPARATOR, TreeQuery
from tree_queries.query import pk
Expand Down Expand Up @@ -55,9 +57,12 @@ def test_no_attributes(self):

def test_attributes(self):
tree = self.create_tree()
child2_2 = Model.objects.with_tree_fields().get(pk=tree.child2_2.pk)
# Ordering should be deterministic
child2_2 = Model.objects.with_tree_fields().order_siblings_by("order", "pk").get(pk=tree.child2_2.pk)
self.assertEqual(child2_2.tree_depth, 2)
self.assertEqual(child2_2.tree_ordering, [0, 1, 1])
# Tree ordering is an array of the ranks assigned to a comment's
# ancestors when they are ordered without respect for tree relations.
self.assertEqual(child2_2.tree_ordering, [1, 5, 6])
self.assertEqual(
child2_2.tree_path, [tree.root.pk, tree.child2.pk, tree.child2_2.pk]
)
Expand Down Expand Up @@ -649,3 +654,90 @@ def test_polymorphic_queries(self):
("child2_2", [1, 3, 6]),
],
)

def test_descending_order(self):
tree = self.create_tree()

nodes = Model.objects.order_siblings_by("-order")
self.assertEqual(
list(nodes),
[
tree.root,
tree.child2,
tree.child2_2,
tree.child2_1,
tree.child1,
tree.child1_1,
],
)

def test_multi_field_order(self):
tree = type("Namespace", (), {})() # SimpleNamespace for PY2...

tree.root = MultiOrderedModel.objects.create(name="root")
tree.child1 = MultiOrderedModel.objects.create(
parent=tree.root, first_position=0, second_position=1, name="1"
)
tree.child2 = MultiOrderedModel.objects.create(
parent=tree.root, first_position=0, second_position=0, name="2"
)
tree.child1_1 = MultiOrderedModel.objects.create(
parent=tree.child1, first_position=1, second_position=1, name="1-1"
)
tree.child2_1 = MultiOrderedModel.objects.create(
parent=tree.child2, first_position=0, second_position=1, name="2-1"
)
tree.child2_2 = MultiOrderedModel.objects.create(
parent=tree.child2, first_position=1, second_position=0, name="2-2"
)

nodes = MultiOrderedModel.objects.order_siblings_by("first_position", "-second_position")
self.assertEqual(
list(nodes),
[
tree.root,
tree.child1,
tree.child1_1,
tree.child2,
tree.child2_1,
tree.child2_2,
]
)

def test_order_by_related(self):
tree = type("Namespace", (), {})() # SimpleNamespace for PY2...

tree.root = RelatedOrderModel.objects.create(name="root")
tree.child1 = RelatedOrderModel.objects.create(parent=tree.root, name="1")
tree.child1_related = OneToOneRelatedOrder.objects.create(
relatedmodel=tree.child1, order=0
)
tree.child2 = RelatedOrderModel.objects.create(parent=tree.root, name="2")
tree.child2_related = OneToOneRelatedOrder.objects.create(
relatedmodel=tree.child2, order=1
)
tree.child1_1 = RelatedOrderModel.objects.create(parent=tree.child1, name="1-1")
tree.child1_1_related = OneToOneRelatedOrder.objects.create(
relatedmodel=tree.child1_1, order=0
)
tree.child2_1 = RelatedOrderModel.objects.create(parent=tree.child2, name="2-1")
tree.child2_1_related = OneToOneRelatedOrder.objects.create(
relatedmodel=tree.child2_1, order=0
)
tree.child2_2 = RelatedOrderModel.objects.create(parent=tree.child2, name="2-2")
tree.child2_2_related = OneToOneRelatedOrder.objects.create(
relatedmodel=tree.child2_2, order=1
)

nodes = RelatedOrderModel.objects.order_siblings_by("related__order")
self.assertEqual(
list(nodes),
[
tree.root,
tree.child1,
tree.child1_1,
tree.child2,
tree.child2_1,
tree.child2_2,
]
)
Loading

0 comments on commit bc17cd4

Please sign in to comment.