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.