Ostatnio trochę filozofowałem, czas wrócić do konkretów :).
Kto ma za sobą pierwsze kroki w Django, czyli zrobił już bloga i poznał flatpages, stanie nieuchronnie przed problemem jak zarządzać nawigacją w nieco bardziej rozbudowanym serwisie.
W jednym z najprostszych przypadków poradziłem sobie w ten sposób, że do każdej strony w modelu dodawałem tytuł jaki ma się wyświetlać w menu oraz liczbę, która miała pomóc takie menu ułożyć w pożądanej kolejności (wystarczyło posortować według tych liczb).
Oczywiście szybko się zorientowałem, że takie proste rozwiązanie jest nieeleganckie, chociażby dlatego, jeśli będziemy chcieli połączyć ten nasz prosty serwis z jakąś inną aplikacją, w jakiś większy projekt, to nie możemy tego uczynić, gdyż menu "siedzi" w modelu naszych starych stron i jest z nim mocno powiązane. Można je nieco ulepszyć poprzez utworzenie osobnego modelu menu powiązanego relacją 1 do 1 z innymi modelami.
Ale wtedy szybko zauważymy następne ograniczenie: menu, które możemy w ten sposób wygenerować jest wyłącznie menu "płaskim", czyli jednopoziomowym. Jak napisać model menu wielopoziomowego, o strukturze drzewa?
Niespodziewanie, rozwiązując prosty zdawałoby się problem, napotykamy na ciekawą i dość złożoną teorię informatyczną o modelowaniu struktury drzew.
Jeden z algorytmów "przechodzenia" przez takie drzewo nazywa się MPTT (Modified Preorder Tree Traversal). Django zawiera odpowiednią bibliotekę zbudowaną za zasadzie tego algorytmu. Jest to Django-mptt. Rzeczywiście, wszystkie bardziej rozbudowane CMS-y napisane w Django korzystają z tej biblioteki. Jest ona jednak dość skomplikowana i dla mojego prostego projektu szukałem rozwiązania mniej zamotanego.
Okazało się, że najlepszy przyjaciel developera jakim jest wyszukiwarka internetowa szybko znalazł rozwiązanie. Prosty generator menu został napisany przez Juliana Phalipa i dostępny jest na serwerach google.
Dokumentacja projektu jest dosyć jasna, więc napiszę tylko, że po wgraniu aplikacji do katalogu z naszym projektem należy oczywiście dopisać ją do INSTALLED_APPS w settings.py, wykonać manage.py syncdb i już mamy w panelu administratora nasze menu. Budujemy jego strukturę poprzez wpisanie dla każdej pozycji URLa i tytułu, no i oczywiście "rodzica". Aplikacja ma wygodny "klikany" sposób na przesuwanie pozycji w górę lub w dół.
Żeby zobaczyć menu na naszej stronie musimy w katalogu z naszymi szablonami utworzyć podkatalog "treemenus", a wnim dwa pliki szablonów: menu.html i menu_item.html. U mnie wyglądają one tak:
menu.html:
{% load tree_menu_tags %}
<ul>
{% for menu_item in menu.root_item.children %}
{% show_menu_item menu_item %}
{% endfor %}
</ul>menu_item.html:
{% load tree_menu_tags %}
{% if menu_item.has_children %}
<li><a class="daddy" href="{{ menu_item.url }}">{{ menu_item.caption }}</a>
<ul>
{% for child in menu_item.children %}
{% show_menu_item child %}
{% endfor %}
</ul>
</li>
{% else %}
<li><a href="{{ menu_item.url }}">{{ menu_item.caption }}</a></li>
{% endif %}Widać tu jak działa ten algorytm. Jest to klasyczna rekurencja i jest to
najprostsze (ale nie najbardziej wydajne) radzenie sobie z drzewami.
Jedyną rzeczą, która pozostała do zrobienia jest umieszczenie w szablonie strony, na jej początku, znacznika {% load tree_menu_tags %}, a w miejscu, gdzie ma być wyświetlane menu - znacznika {% show_menu "nazwa_menu" %}.
Odpowiedzi
Aktualna strona - current
Skoro tak ładnie można sobie poradzić z menu wielopoziomowym to warto jeszcze zaopatrzyć stronę w dodawanie klasy do znacznika li oznaczającej aktualnie wyświetlaną stronę. Tak aby patrząc na menu użytkownik od razu wiedział gdzie się w ramach drzewa znajduje.
Jak najbardziej. Można
Jak najbardziej. Można zresztą to osiągnąć przez zdefiniowanie dodatkowego znacznika w szablonie Django.