Перейти к содержанию

Практика работы с иерархиями: реальные примеры и выводы

Введение

Эта статья содержит практические примеры работы с иерархией: как реально работают методы 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.

Ссылки