Практика работы с иерархиями: реальные примеры и выводы ¶
Введение ¶
Эта статья содержит практические примеры работы с иерархией: как реально работают методы get_ancestors, get_descendants, get_parents, get_children на тестовой структуре. Все примеры и выводы получены в результате запуска тестов на актуальном коде.
Пример теста: функциональность иерархии для корректировок ¶
Используемый yaml-файл со структурой иерархии:
src/planiqum/core/hierarchy/tests/fixtures/corrections_hierarchy.yaml
Структура иерархии (реальный вывод из теста) ¶
graph TD
business["business"] --> brand["brand"]
brand["brand"] --> product_plan_level["product_plan_level"]
is_discontinued["is_discontinued"] --> dpu["dpu"]
category["category"] --> subcategory["subcategory"]
subcategory["subcategory"] --> product_plan_level["product_plan_level"]
segment["segment"] --> business["business"]
sales_point_type["sales_point_type"] --> sales_point["sales_point"]
pack_type["pack_type"] --> subcategory["subcategory"]
product_plan_level["product_plan_level"] --> dpu["dpu"]
sales_point["sales_point"] --> dpu["dpu"]
activity["activity"]
Диаграмма построена по реальному выводу метода get_mermaid_notation() из теста для типа planning.
Файл теста: src/planiqum/core/hierarchy/tests/test_hierarchy_basics.py
import os
import pytest
from django.core.management import call_command
from planiqum.core.hierarchy.models import Level
@pytest.mark.django_db
def test_hierarchy_basics():
fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures/corrections_hierarchy.yaml')
print(f"[DEBUG] Загружаем иерархию из: {fixture_path}")
call_command('loadhierarchy', fixture_path, verbosity=2)
keys = [
"activity", "is_discontinued", "dpu", "product_plan_level", "brand", "business", "segment",
"subcategory", "category", "pack_type", "sales_point", "sales_point_type"
]
levels = {k: Level.objects.get(key=k) for k in keys}
for k, level in levels.items():
print(f"\n[DEBUG] Уровень: {k}")
print(" get_ancestors:", list(level.get_ancestors().values_list('key', flat=True)))
print(" get_descendants:", list(level.get_descendants().values_list('key', flat=True)))
print(" get_parents:", list(level.parents.values_list('key', flat=True)))
print(" get_children:", list(level.get_children().values_list('key', flat=True)))
Реальный вывод теста ¶
[DEBUG] Уровень: activity
get_ancestors: []
get_descendants: []
get_parents: []
get_children: []
[DEBUG] Уровень: is_discontinued
get_ancestors: ['dpu']
get_descendants: []
get_parents: []
get_children: []
[DEBUG] Уровень: dpu
get_ancestors: []
get_descendants: ['business', 'brand', 'is_discontinued', 'category', 'subcategory', 'segment', 'sales_point_type', 'pack_type', 'product_plan_level', 'sales_point']
get_parents: ['is_discontinued', 'product_plan_level', 'sales_point']
get_children: ['is_discontinued', 'product_plan_level', 'sales_point']
[DEBUG] Уровень: product_plan_level
get_ancestors: ['dpu']
get_descendants: ['business', 'brand', 'category', 'subcategory', 'segment', 'pack_type']
get_parents: ['brand', 'subcategory']
get_children: ['brand', 'subcategory']
[DEBUG] Уровень: brand
get_ancestors: ['dpu', 'product_plan_level']
get_descendants: ['business', 'segment']
get_parents: ['business']
get_children: ['business']
[DEBUG] Уровень: business
get_ancestors: ['dpu', 'product_plan_level', 'brand']
get_descendants: ['segment']
get_parents: ['segment']
get_children: ['segment']
[DEBUG] Уровень: segment
get_ancestors: ['dpu', 'product_plan_level', 'brand', 'business']
get_descendants: []
get_parents: []
get_children: []
[DEBUG] Уровень: subcategory
get_ancestors: ['dpu', 'product_plan_level']
get_descendants: ['category', 'pack_type']
get_parents: ['category', 'pack_type']
get_children: ['category', 'pack_type']
[DEBUG] Уровень: category
get_ancestors: ['dpu', 'product_plan_level', 'subcategory']
get_descendants: []
get_parents: []
get_children: []
[DEBUG] Уровень: pack_type
get_ancestors: ['dpu', 'product_plan_level', 'subcategory']
get_descendants: []
get_parents: []
get_children: []
[DEBUG] Уровень: sales_point
get_ancestors: ['dpu']
get_descendants: ['sales_point_type']
get_parents: ['sales_point_type']
get_children: ['sales_point_type']
[DEBUG] Уровень: sales_point_type
get_ancestors: ['dpu', 'sales_point']
get_descendants: []
get_parents: []
get_children: []
Пояснения к результатам ¶
- get_ancestors — возвращает цепочку предков уровня (по MPTT-структуре).
- get_descendants — возвращает всех потомков уровня (по MPTT-структуре).
- get_parents и get_children — отражают M2M-связи, которые могут отличаться от MPTT.
- В структуре могут присутствовать дополнительные уровни (например, is_discontinued, category и др.), которые также попадают в descendants/parents/children.