Add add_related_count() method as replacement for django-mptt functionality
This PR implements add_related_count() method for TreeQuerySet as a direct replacement for django-mptt's method of the same name, enabling users to migrate from django-mptt while retaining the ability to count related objects across tree hierarchies.
Problem
Users migrating from django-mptt to django-tree-queries were missing the add_related_count() functionality, which annotates tree nodes with counts of related objects. The challenge was that django-mptt's approach relies on concrete database fields (like lft, rght), while django-tree-queries uses CTE-generated fields like tree_path that aren't accessible to Django's Subquery class.
Solution
The new implementation leverages django-tree-queries' existing CTE infrastructure and tree_path field to provide the same functionality:
# Non-cumulative counting (direct relationships only)
regions = Region.objects.add_related_count(
Region.objects.all(),
Site,
'region',
'site_count',
cumulative=False
)
# Cumulative counting (includes descendants)
regions_cumulative = Region.objects.add_related_count(
Region.objects.all(),
Site,
'region',
'total_sites',
cumulative=True
)
Key Features
-
Database Agnostic: Optimized for PostgreSQL using array operations (
ANY(__tree.tree_path)), with fallback string operations for SQLite, MySQL, and MariaDB - Two Counting Modes: Non-cumulative (direct relationships) and cumulative (includes all descendants)
- Automatic Relationship Detection: Intelligently finds reverse relationship names
-
CTE Integration: Uses existing
tree_pathfield from recursive Common Table Expressions - Performance Optimized: Uses subqueries designed for each database backend
Implementation Details
The method works by:
- For non-cumulative counts: Simple annotation using Django's
Count()with reverse relationships - For cumulative counts: Using subqueries that leverage
tree_pathto find all descendants of each node, then counting related objects across the entire subtree
Testing
Added comprehensive test coverage including:
- Test models (
RegionandSite) that mirror real-world usage scenarios - Non-cumulative counting validation
- Cumulative counting with complex hierarchies (country → state → city structure)
- Edge cases with empty trees
Documentation
Updated README.rst with:
- Detailed usage examples showing both counting modes
- Method signature documentation
- Integration examples with existing tree query methods
- Added to feature list
This provides a complete migration path for django-mptt users who rely on add_related_count() functionality.
Fixes #21.
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.