idlesign / django-sitetree

Reusable application for Django introducing site tree, menu and breadcrumbs navigation elements.
http://github.com/idlesign/django-sitetree
BSD 3-Clause "New" or "Revised" License
346 stars 132 forks source link

Incorporate one dynamic tree into another #280

Open xlevus opened 4 years ago

xlevus commented 4 years ago

Files:

# myproj/settings.py

INSTALLED_APPS = [
  'sitetree',
  'base',
  'keywords',
]
# myproj/urls.py

...

from sitetree.sitetreeapp import register_dynamic_trees, compose_dynamic_tree, SiteTree
from sitetree.utils import tree, item

base = compose_dynamic_tree(
  "base")

keywords = compose_dynamic_tree(
    "keywords", target_tree_alias="sidenav", parent_tree_item_alias="sidenav_1"
)

register_dynamic_trees(
    base, keywords, reset_cache=True,
)
# keywords/sitetrees.py

from sitetree.utils import tree, item

sitetrees = [
    tree(
        "keywords",
        items=[
            item(
                "Keywords",
                "index",
                children=[item("Manage Keywords", "keywords-list",)],
            )
        ],
    ),
]
# base/sitetrees.py
sitetrees = [
   tree("sidenav", items=[item("Sidenav 1", "index", alias="sidenav_1")],),
]
# templates/example.html

{% load sitetree %}
{% sitetree_tree from "sidenav" %}

I would expect the following to be rendered by sitetree_tree:


  -  Sidenav 1
     +-   Keywords
        +-    Manage Keywords

Except, I get:

  -  Sidenav 1

If I run python manage.py sitetree_resync_apps and then disable SITETREE_DYNAMIC_ONLY it works fine.

xlevus commented 4 years ago

I suspect the issue lies somewhere around here in sitetree/sitetreeapp.py:

535             if not sitetree:
536                 if DYNAMIC_ONLY:
537                     sitetree = []
538     
539                 else:
540                     sitetree = (
541                         MODEL_TREE_ITEM_CLASS.objects.
542                         select_related('parent', 'tree').
543                         prefetch_related('access_permissions__content_type').
544                         filter(tree__alias__exact=alias).
545                         order_by('parent__sort_order', 'sort_order'))
546     
547  ->             sitetree = self.attach_dynamic_tree_items(alias, sitetree)
548                 set_cache_entry('sitetrees', alias, sitetree)
549                 caching_required = True
453             items = []
454             if not src_tree_items:  # THIS IS AN EMPTY ARRAY WHEN `DYNAMIC_ONLY` IS TRUE.
455                 if _IDX_ORPHAN_TREES in trees and tree_alias in trees[_IDX_ORPHAN_TREES]:
456                     for tree in trees[_IDX_ORPHAN_TREES][tree_alias]:
458             else:  # THIS ATTACHMENT BRANCH NEVER GETS RUN
459     
460                 # TODO Seems to be underoptimized %)
461     
462                 # Tree item attachment by alias.
463                 for static_item in list(src_tree_items):
464                     items.append(static_item)
465                     if not static_item.alias:
466                         continue
467     
468                     idx = _IDX_TPL % (tree_alias, static_item.alias)
469                     if idx not in trees:
idlesign commented 4 years ago

The purpose of SITETREE_DYNAMIC_ONLY as documented here is to prevent reading static trees from DB entirely.

sitetree_resync_apps is to make your trees static.

Attaching tree items was initialy implemented to allow attachment of dynamic nodes to static. In your case, if I understand correctly, you want to attach items from one dynamic tree into another. I'm afraid this case is not covered now. Maybe we could implement that, but it'll probably require registering trees using some definite sequence (first parents, then children).

If you have any ideas of how could this be solved in a backward-compatible, robust and convenient way, you're welcome.