{"id":7181,"library":"django-tree-queries","title":"Django Tree Queries","description":"django-tree-queries is a Django library that enables efficient querying of hierarchical data structures (trees) using adjacency lists and recursive Common Table Expressions (CTEs). It provides a lightweight solution for managing tree-like models within the Django ORM, focusing on explicit opt-in for tree-specific features rather than extensive configurability. The library is actively maintained, with version 0.24.0 currently available, supporting modern Django and Python versions.","status":"active","version":"0.24.0","language":"en","source_language":"en","source_url":"https://github.com/matthiask/django-tree-queries/","tags":["django","ORM","tree","hierarchical data","CTE","adjacency list"],"install":[{"cmd":"pip install django-tree-queries","lang":"bash","label":"Install stable version"}],"dependencies":[{"reason":"Core framework dependency; supports Django 3.2 or better.","package":"Django","optional":false},{"reason":"Recommended for PostgreSQL for optimal performance and 'tree_path' array support.","package":"psycopg2-binary","optional":true}],"imports":[{"note":"To enable tree query capabilities, models should inherit from tree_queries.models.TreeNode or use tree_queries.query.TreeQuerySet with a custom manager.","wrong":"from django.db import models; class MyModel(models.Model): ...","symbol":"TreeNode","correct":"from tree_queries.models import TreeNode"},{"note":"Use TreeQuerySet to build custom managers for tree-aware querysets if not inheriting from TreeNode.","wrong":"from django.db.models import QuerySet","symbol":"TreeQuerySet","correct":"from tree_queries.query import TreeQuerySet"},{"note":"Provides a model field that augments the default foreign key field with tree structure visualization in forms.","symbol":"TreeNodeForeignKey","correct":"from tree_queries.fields import TreeNodeForeignKey"}],"quickstart":{"code":"import os\nfrom django.db import models\n\n# Assuming 'myapp' is in INSTALLED_APPS\nos.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')\nimport django\ndjango.setup()\n\nfrom tree_queries.models import TreeNode\n\nclass Category(TreeNode):\n    name = models.CharField(max_length=255)\n\n    class Meta:\n        app_label = 'myapp'\n\n    def __str__(self):\n        return self.name\n\n# Example Usage (after makemigrations and migrate)\n# from myapp.models import Category\n# root = Category.objects.create(name='Electronics')\n# child1 = Category.objects.create(name='Smartphones', parent=root)\n# child2 = Category.objects.create(name='Laptops', parent=root)\n# grandchild = Category.objects.create(name='Gaming Laptops', parent=child2)\n\n# Retrieve a tree with additional fields\n# for node in Category.objects.with_tree_fields().order_siblings_by('name'):\n#     print(f\"{'--' * node.tree_depth} {node.name} (depth: {node.tree_depth})\")","lang":"python","description":"Define a model inheriting from `tree_queries.models.TreeNode`. This automatically adds a `parent` ForeignKey and provides tree-aware query methods. To access `tree_depth`, `tree_path`, and `tree_ordering` fields, you must explicitly call `.with_tree_fields()` on your queryset. Siblings can be ordered using `.order_siblings_by()` method."},"warnings":[{"fix":"Replace `.order_by(...)` with `.order_siblings_by('field_name')` for sibling ordering. General ordering of the full tree is implicit depth-first.","message":"Django's standard `order_by()` method is not supported for tree ordering; nodes are returned according to a depth-first search. Use `order_siblings_by(\"field_name\")` instead.","severity":"breaking","affected_versions":"All versions"},{"fix":"Always append `.with_tree_fields()` to your queryset when you intend to access these tree-specific attributes. Example: `Category.objects.with_tree_fields().get(pk=1)`.","message":"The `tree_depth`, `tree_path`, and `tree_ordering` fields are only available on querysets where `with_tree_fields()` has been explicitly called. They are not stored in the database or available on newly created/saved instances.","severity":"gotcha","affected_versions":"All versions"},{"fix":"For very large datasets, consider using `tree_filter()` and `tree_exclude()` for better performance as they filter the base table before building the tree. For complex bottom-up aggregations, consider performing calculations in Python for small trees or exploring other tree libraries (e.g., django-treebeard, django-closuretree) if `django-tree-queries` becomes a bottleneck for specific use cases.","message":"Performance can degrade significantly on very large tables or deep trees, especially when queries involve filtering or aggregating parts of the tree, as the recursive CTE might calculate all trees in the table before filtering.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Ensure your model's self-referencing ForeignKey is defined as `parent = models.ForeignKey('self', ...)`","message":"The `parent` foreign key field in models must explicitly be named `parent` for `TreeNode` to function correctly.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Use the provided integer `tree_depth` and queryset methods for tree traversal and logic, avoiding direct parsing or reliance on the exact string format of `tree_path` or `tree_ordering` if your application needs to be future-proof or database-agnostic.","message":"The internal representation of `tree_path` and `tree_ordering` can change in future versions and should not be relied upon for application logic, especially for non-PostgreSQL databases where `tree_path` is a string representation.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Design your tree structure to stay within the 50-level limit when using MySQL or MariaDB. Consider PostgreSQL for deeper trees.","message":"MySQL and MariaDB databases have a maximum tree depth limit of 50 levels due to their lack of native array support, which `django-tree-queries` uses for `tree_path` and `tree_ordering` internally.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Add `.with_tree_fields()` to your queryset before accessing `tree_depth`, `tree_path`, or `tree_ordering`. Example: `MyModel.objects.with_tree_fields().first().tree_depth`.","cause":"Attempting to access `tree_depth` or other tree-specific fields without calling `.with_tree_fields()` on the queryset. These fields are dynamic annotations, not stored model fields.","error":"FieldError: Cannot resolve keyword 'tree_depth' into field."},{"fix":"Ensure your model inherits from `tree_queries.models.TreeNode` and retrieve instances using `MyModel.objects` (which is a `TreeQuerySet`) or explicitly use `TreeQuerySet.as_manager()` for custom managers. Example: `node = MyModel.objects.get(pk=1); ancestors = node.ancestors()`.","cause":"The model instance was retrieved without using the `TreeQuerySet` methods, or the model does not inherit from `TreeNode`. Standard Django model instances do not inherently have these tree methods.","error":"AttributeError: 'MyModel' object has no attribute 'ancestors' (or 'descendants', etc.)"},{"fix":"For complex aggregations, particularly bottom-up, consider loading smaller trees into Python memory and performing calculations there, or restructuring your query to align with the top-down nature of the CTE.","cause":"This error can occur when using complex subqueries or aggregations with tree querysets, especially when trying to aggregate values bottom-up (from descendants to ancestors), which is difficult or impossible with the library's top-down CTE approach.","error":"django.db.utils.ProgrammingError: subquery must return only one column"}]}