Nawigacja w django

104

Właśnie zrobiłem moją pierwszą małą aplikację internetową w django i bardzo mi się podoba. Zaraz zacznę od konwersji starej produkcyjnej strony PHP do django i jako część jej szablonu, jest tam pasek nawigacji.

W PHP sprawdzam adres URL każdej opcji nawigacyjnej z bieżącym adresem URL, w kodzie szablonu i stosuję klasę CSS, jeśli się zgadzają. Jest przerażająco brudny.

Czy jest coś lepszego dla django lub dobry sposób obsługi kodu w szablonie?

Na początek, jak mam się zabrać do uzyskania aktualnego adresu URL?

Oli
źródło
W tym celu stworzyłem github.com/orokusaki/django-active-menu - obsługuje zagnieżdżone struktury adresów URL i opiera się na konfiguracji zamiast konwencji (tak źle, jak to brzmi), więc możesz zdefiniować hierarchię witryny, jak chcesz. Po prostu użyj <a href="{% url "view:name" %}" {% active_class "view:name" %}>. Opcjonalnie można go używać do generowania tylko na " active"wartość (przekazując Falsejako drugi argument do znacznika) do dołączania do istniejącego atrybutu klasy, ale dla większości linków nav że przykładem jest to, co używam.
orokusaki,
Wydaje się, że to pytanie jest powiązane z tym stackoverflow.com/a/9801473/5739875
Evgeny Bobkin
Może ta siatka pomoże: djangopackages.org/grids/g/navigation
guettli

Odpowiedzi:

74

Korzystam z dziedziczenia szablonów, aby dostosować nawigację. Na przykład:

base.html

<html>
    <head>...</head>
    <body>
        ...
        {% block nav %}
        <ul id="nav">
            <li>{% block nav-home %}<a href="{% url home %}">Home</a>{% endblock %}</li>
            <li>{% block nav-about %}<a href="{% url about %}">About</a>{% endblock %}</li>
            <li>{% block nav-contact %}<a href="{% url contact %}">Contact</a>{% endblock %}</li>
        </ul>
        {% endblock %}
        ...
    </body>
</html>

about.html

{% extends "base.html" %}

{% block nav-about %}<strong class="nav-active">About</strong>{% endblock %}
jpwatts
źródło
Bardzo podoba mi się ten pomysł, szczególnie ze względu na elastyczność, ale wiąże się z mniej SUCHYM kompromisem. Zacząłem jednak używać tego w witrynie.
anonimowy tchórz
23
Nie podoba mi się to podejście, ponieważ nierzadko zdarza się, że wiele sekcji witryny jest obsługiwanych przez ten sam pod-szablon. W rezultacie umieszczasz niestandardowe zmienne w widokach i warunkowych w szablonach lub zmieniasz układ pod-szablonów, aby były unikalne ... wszystko po to, aby wykryć bieżącą sekcję witryny. Podejście oparte na tagach szablonów jest ostatecznie bardziej przejrzyste.
shacker
Przyjrzałem się kilku innym rozwiązaniom i wygląda na to, że wszystkie są trochę hackem. Przynajmniej ten jest dość prosty i łatwy do wdrożenia / złomowania.
mlissner
I refactored <ul id="nav">....</ul>do innego pliku, powiedzmy tabs.html. Więc teraz base.html zawierał, {%block nav%}{%include "tabs.html"%}{%endblock%}a następnie podświetlanie aktywnej karty przestało działać (w about.html powyżej). Czy coś mi brakuje?
None-da
@Maddy Masz wystarczająco dużo informacji, że nie jestem pewien, czy trzymam to prosto w głowie, ale myślę, że odpowiedź ma związek z tym, jak includedziała tag. Zapoznaj się z notatką zawartą w dokumentach: docs.djangoproject.com/en/dev/ref/templates/builtins/#include W twoim przypadku, zanim spróbujesz zastąpić szablon podstawowy w about.html, myślę, że ma już wyrenderowany blok HTML, a nie blok szablonu Django czekający na przetworzenie.
jpwatts
117

Nie potrzebujesz, aby to zrobić, spójrz na poniższy kod:

tags.py

@register.simple_tag
def active(request, pattern):
    import re
    if re.search(pattern, request.path):
        return 'active'
    return ''

urls.py

urlpatterns += patterns('',
    (r'/$', view_home_method, 'home_url_name'),
    (r'/services/$', view_services_method, 'services_url_name'),
    (r'/contact/$', view_contact_method, 'contact_url_name'),
)

base.html

{% load tags %}

{% url 'home_url_name' as home %}
{% url 'services_url_name' as services %}
{% url 'contact_url_name' as contact %}

<div id="navigation">
    <a class="{% active request home %}" href="{{ home }}">Home</a>
    <a class="{% active request services %}" href="{{ services }}">Services</a>
    <a class="{% active request contact %}" href="{{ contact }}">Contact</a>
</div>

Otóż ​​to. szczegóły implementacji znajdziesz na:
gnuvince.wordpress.com
110j.wordpress.com

pradyunsg
źródło
2
We właściwościach href brakuje nawiasów szablonów django {{,}}. Na przykład <a class="{% active request home %}" href="home"> Home </a> powinno mieć wartość <a class = "{% active request home%}" href = "{{home} } "> Strona główna </a> plik tags.py będzie również wymagał kilku dołączeń. W przeciwnym razie świetne rozwiązanie!
bsk
2
+1 Jest to bardziej luźno powiązane z aplikacjami. Jako początkujący doszedłem do wniosku, że tagi potrzebują własnej aplikacji, nie możesz tego po prostu wrzucić do globalnego pliku tags.py. Stworzyłem nową aplikację o nazwie Tagi i wszystko poszło gładko. docs.djangoproject.com/en/dev/howto/custom-template-tags
Keyo
3
@Keyo, utwórz katalog templatetags w swoim projekcie i dodaj swój projekt do installedapps. To też wystarczy. Alternatywnie, tak jak powiedziałeś, utwórz swoją główną witrynę jako aplikację w swoim projekcie.
Josh Smeaton,
5
Nie zapomnij dodać django.core.context_processors.requestdo swojego TEMPLATE_CONTEXT_PROCESSORSinsettings.py
amigcamel
1
Jest to niepoprawne dla stanów, które mogą być zagnieżdżone, np. mysite.com(Jako dom) oraz mysite.com/blog, ponieważ ścieżka będzie wyświetlana jako /i /blog/(odpowiednio), za każdym razem dając dopasowanie do poprzedniego. Jeśli nie używasz /jako lądowania, może to być w porządku, w przeciwnym razie po prostu używam return 'active' if pattern == request.path else ''(nie widziałem jeszcze problemów z tym, ale właśnie skonfigurowałem, używając tego).
nerdwaller
33

Podobała mi się czystość 110j powyżej, więc wziąłem jej większość i refaktoryzowałem, aby rozwiązać 3 problemy, które miałem z nią:

  1. wyrażenie regularne pasowało do adresu URL „strony głównej” ze wszystkimi innymi
  2. Potrzebowałem wielu adresów URL mapowanych na jedną kartę nawigacyjną , więc potrzebowałem bardziej złożonego tagu, który przyjmuje zmienną ilość parametrów
  3. naprawiono niektóre problemy z adresami URL

Oto ona:

tags.py:

from django import template

register = template.Library()

@register.tag
def active(parser, token):
    args = token.split_contents()
    template_tag = args[0]
    if len(args) < 2:
        raise template.TemplateSyntaxError, "%r tag requires at least one argument" % template_tag
    return NavSelectedNode(args[1:])

class NavSelectedNode(template.Node):
    def __init__(self, patterns):
        self.patterns = patterns
    def render(self, context):
        path = context['request'].path
        for p in self.patterns:
            pValue = template.Variable(p).resolve(context)
            if path == pValue:
                return "active" # change this if needed for other bootstrap version (compatible with 3.2)
        return ""

urls.py:

urlpatterns += patterns('',
    url(r'/$', view_home_method, {}, name='home_url_name'),
    url(r'/services/$', view_services_method, {}, name='services_url_name'),
    url(r'/contact/$', view_contact_method, {}, name='contact_url_name'),
    url(r'/contact/$', view_contact2_method, {}, name='contact2_url_name'),
)

base.html:

{% load tags %}

{% url home_url_name as home %}
{% url services_url_name as services %}
{% url contact_url_name as contact %}
{% url contact2_url_name as contact2 %}

<div id="navigation">
    <a class="{% active request home %}" href="home">Home</a>
    <a class="{% active request services %}" href="services">Services</a>
    <a class="{% active request contact contact2 %}" href="contact">Contact</a>
</div>
nivhab
źródło
To chyba najlepsza odpowiedź z Marcusem, ale jak to działa z „domem”? jest zawsze aktywny? Jak uaktywnić go tylko przy wywołaniu głównego adresu URL (www.toto.com/ i www.toto.com/index)? Obie odpowiedzi nie powodują tego problemu ...
DestyNova
20

Jestem autorem linii django, którą napisałem specjalnie, aby rozwiązać to pytanie: D

Denerwowało mnie stosowanie (całkowicie akceptowalnej) metody jpwatts we własnych projektach i zaczerpnąłem inspirację z odpowiedzi 110j. Rodowód wygląda następująco:

{% load lineage %}
<div id="navigation">
    <a class="{% ancestor '/home/' %}" href="/home/">Home</a>
    <a class="{% ancestor '/services/' %}" href="/services/">Services</a>
    <a class="{% ancestor '/contact/' %}" href="/contact/">Contact</a>
</div>

ancestor jest po prostu zastępowany przez „aktywny”, jeśli argument jest zgodny z początkiem adresu URL bieżącej strony.

{% url %}Obsługiwane są również zmienne argumenty i odwrotna rozdzielczość pełnego typu. Wrzuciłem kilka opcji konfiguracyjnych, trochę go dopracowałem i zapakowałem, aby wszyscy mogli z niego korzystać.

Jeśli ktoś jest zainteresowany, przeczytaj więcej na ten temat pod adresem:

>> github.com/marcuswhybrow/django-lineage

Marcus Whybrow
źródło
1
twarde ścieżki kodowania do szablonu :(
CpILL
10

Od Django 1.5 :

We wszystkich ogólnych widokach opartych na klasach (lub dowolnym widoku opartym na klasach dziedziczącym z ContextMixin) słownik kontekstowy zawiera zmienną widoku, która wskazuje na wystąpienie View.

Więc jeśli korzystasz z takich widoków, możesz dodać coś podobnego breadcrumbsjako pole na poziomie klasy i użyć go w swoich szablonach.

Przykładowy kod widoku:

class YourDetailView(DetailView):
     breadcrumbs = ['detail']
     (...)

W swoim szablonie możesz to wykorzystać w ten sposób:

<a href="/detail/" {% if 'detail' in view.breadcrumbs %}class="active"{% endif %}>Detail</a>

Jeśli chcesz dodatkowo „podświetlić” nadrzędne elementy nawigacji, musisz rozszerzyć breadcrumbslistę:

class YourDetailView(DetailView):
     breadcrumbs = ['dashboard', 'list', 'detail']
     (...)

... aw Twoim szablonie:

<a href="/dashboard/" {% if 'dashboard' in view.breadcrumbs %}class="active"{% endif %}>Dashboard</a>
<a href="/list/" {% if 'list' in view.breadcrumbs %}class="active"{% endif %}>List</a>
<a href="/detail/" {% if 'detail' in view.breadcrumbs %}class="active"{% endif %}>Detail</a>

Jest to łatwe i przejrzyste rozwiązanie, które działa całkiem dobrze z nawigacją zagnieżdżoną.

Konrad Hałas
źródło
W tym przykładzie, czy wszystkie trzy elementy nawigacji nie byłyby .active?
Oli,
Tak, ale jest to zazwyczaj to, co chcesz osiągnąć dzięki nawigacji wielopoziomowej. breadcrumbsJeśli chcesz, możesz oczywiście włożyć jeden przedmiot . Ale masz rację - mój przykład nie jest najlepszy.
Konrad Hałas
@Oli ulepszony przykład.
Konrad Hałas
9

Możesz zastosować klasę lub identyfikator do elementu body strony zamiast do określonego elementu nawigacyjnego.

HTML:

<body class="{{ nav_class }}">

CSS:

body.home #nav_home,
body.about #nav_about { */ Current nav styles */ }
Michael Warkentin
źródło
8

Robię to tak:

<a class="tab {% ifequal active_tab "statistics" %}active{% endifequal %}" href="{% url Member.Statistics %}">Statistics</a>

a potem wszystko, co muszę zrobić, to dodać {'active_tab': 'statistics'}do mojego słownika kontekstowego.

Jeśli używasz RequestContext, możesz pobrać aktualną ścieżkę w swoim szablonie jako:

{{ request.path }}

Twoim zdaniem:

from django.template import RequestContext

def my_view(request):
    # do something awesome here
    return template.render(RequestContext(request, context_dict))
muhuk
źródło
Dzięki za udostępnienie tych informacji. Użyłem tej metody, ale miałem też płaską stronę na moim pasku nawigacyjnym, więc aby to wykryć i poprawnie ją podświetlić, użyłem {% ifequal flatpage.url '/ about /'%}. Nie podoba mi się zakodowane na stałe wykrywanie adresu URL, ale działa przy jednorazowym włamaniu.
Matt Garrison,
Problem z tym rozwiązaniem polega na tym, że masz na stałe zakodowane w kodzie „statystyki”. To mija się z celem używania tagu url do pobierania adresu URL strony.
justin
7

Wziąłem kod z nivhab powyżej i usunąłem trochę dziwności i przekształciłem go w czysty szablon szablonu, zmodyfikowałem go tak, aby / account / edit / nadal aktywował / account / tab.

#current_nav.py
from django import template

register = template.Library()

@register.tag
def current_nav(parser, token):
    import re
    args = token.split_contents()
    template_tag = args[0]
    if len(args) < 2:
        raise template.TemplateSyntaxError, "%r tag requires at least one argument" % template_tag
    return NavSelectedNode(args[1])

class NavSelectedNode(template.Node):
    def __init__(self, url):
        self.url = url

    def render(self, context):
        path = context['request'].path
        pValue = template.Variable(self.url).resolve(context)
        if (pValue == '/' or pValue == '') and not (path  == '/' or path == ''):
            return ""
        if path.startswith(pValue):
            return ' class="current"'
        return ""



#template.html
{% block nav %}
{% load current_nav %}
{% url home as home_url %}
{% url signup as signup_url %}
{% url auth_login as auth_login_url %}
<ul class="container">
    <li><a href="{{ home_url }}"{% current_nav home_url %} title="Home">Home</a></li>
    <li><a href="{{ auth_login_url }}"{% current_nav auth_login_url %} title="Login">Login</a></li>
    <li><a href="{{ signup_url }}"{% current_nav signup_url %} title="Signup">Signup</a></li>
</ul>
{% endblock %}
Andreas
źródło
6

To tylko wariant rozwiązania CSS zaproponowanego przez Toba powyżej:

Uwzględnij następujące elementy w swoim szablonie podstawowym:

<body id="section-{% block section %}home{% endblock %}">

Następnie w twoich szablonach, które rozszerzają podstawowe użycie:

{% block section %}show{% endblock %}

Następnie możesz użyć css, aby podświetlić bieżący obszar na podstawie tagu body (na przykład jeśli mamy link z identyfikatorem nav-home):

#section-home a#nav-home{
 font-weight:bold;
}
dtt101
źródło
3

Możesz użyć funkcji reverse z odpowiednimi parametrami, aby uzyskać aktualny adres URL.

Corey
źródło
3

Dzięki za dotychczasowe odpowiedzi panowie. Znowu poszedłem na coś nieco innego ...

W moim szablonie:

<li{{ link1_active }}>...link...</li>
<li{{ link2_active }}>...link...</li>
<li{{ link3_active }}>...link...</li>
<li{{ link4_active }}>...link...</li>

Po ustaleniu, na której stronie jestem w logice (zwykle w urls.py), przekazuję class="selected"jako część kontekstu pod odpowiednią nazwą do szablonu.

Np. Jeśli jestem na stronie link1, dołączę {'link1_active':' class="selected"'}do kontekstu, aby szablon zgarnął i wstrzyknął.

Wygląda na to, że działa i jest dość czysty.

Edycja: aby zachować HTML poza moim kontrolerem / widokiem, zmodyfikowałem to trochę:

<li{% if link1_active %} class="selected"{% endif %}>...link...</li>
<li{% if link2_active %} class="selected"{% endif %}>...link...</li>
...

To sprawia, że ​​szablon jest trochę mniej czytelny, ale zgadzam się, lepiej nie przepychać surowego kodu HTML z pliku URL.

Oli
źródło
2
Naprawdę powinieneś unikać obsługi surowego HTML w swoim widoku, czego wymaga ta technika. Czy myślałeś o napisaniu niestandardowego tagu szablonu?
Justin Voss,
Masz rację. Edytowałem, aby przestać przechodzić przez HTML. Właśnie przechodzę teraz przez True. Nie napisałem jeszcze żadnych tagów szablonów, ale tak, to może być dobre miejsce na rozpoczęcie.
Oli
2

Mam wiele menu na tej samej stronie, które są tworzone dynamicznie za pomocą pętli. Powyższe posty odnoszące się do kontekstu dały mi szybką poprawkę. Mam nadzieję, że to komuś pomoże. (Używam tego oprócz aktywnego tagu szablonu - moja poprawka rozwiązuje problem dynamiczny). Wydaje się, że to głupie porównanie, ale działa. Zdecydowałem się nazwać zmienne active_something-unique i something-unique, w ten sposób działa to z zagnieżdżonymi menu.

Oto fragment widoku (wystarczający, aby zrozumieć, co robię):

def project_list(request, catslug):
    "render the category detail page"
    category = get_object_or_404(Category, slug=catslug, site__id__exact=settings.SITE_ID)
    context = {
        'active_category': 
            category,
        'category': 
            category,
        'category_list': 
            Category.objects.filter(site__id__exact=settings.SITE_ID),

    }

A to z szablonu:

<ul>
  {% for category in category_list %}
    <li class="tab{% ifequal active_category category %}-active{% endifequal %}">
      <a href="{{ category.get_absolute_url }}">{{ category.cat }}</a>
    </li>
  {% endfor %}
</ul>
Thomas Schreiber
źródło
2

Moim rozwiązaniem było napisanie prostego procesora kontekstu do ustawiania zmiennej na podstawie ścieżki żądania:

def navigation(request):
"""
Custom context processor to set the navigation menu pointer.
"""
nav_pointer = ''
if request.path == '/':
    nav_pointer = 'main'
elif request.path.startswith('/services/'):
    nav_pointer = 'services'
elif request.path.startswith('/other_stuff/'):
    nav_pointer = 'other_stuff'
return {'nav_pointer': nav_pointer}

(Nie zapomnij dodać własnego procesora do TEMPLATE_CONTEXT_PROCESSORS w settings.py).

Następnie w szablonie podstawowym używam tagu ifequal dla każdego linku, aby określić, czy dołączyć klasę „aktywną”. To prawda, że ​​to podejście jest ściśle ograniczone do elastyczności struktury ścieżki, ale działa w przypadku mojego stosunkowo skromnego wdrożenia.


źródło
Myślę, że naprawdę sensowne jest posiadanie ich w kontekście globalnym, więc możesz odwoływać się do sekcji witryny na różne sposoby (używając różnych szablonów dla różnych sekcji witryny, na przykład. +1.
shacker
2

Chciałem tylko udostępnić moje drobne ulepszenia w poście nivhab. W mojej aplikacji mam subnavigacje i nie chciałem ich ukrywać używając tylko CSS, więc potrzebowałem jakiegoś tagu "if", aby wyświetlić subnavigację dla elementu, czy nie.

from django import template
register = template.Library()

@register.tag
def ifnaviactive(parser, token):
    nodelist = parser.parse(('endifnaviactive',))
    parser.delete_first_token()

    import re
    args = token.split_contents()
    template_tag = args[0]
    if len(args) < 2:
        raise template.TemplateSyntaxError, "%r tag requires at least one argument" % template_tag
    return NavSelectedNode(args[1:], nodelist)

class NavSelectedNode(template.Node):
    def __init__(self, patterns, nodelist):
        self.patterns = patterns
        self.nodelist = nodelist

    def render(self, context):
        path = context['request'].path
        for p in self.patterns:
            pValue = template.Variable(p).resolve(context)
            if path == pValue:
                return self.nodelist.render(context)
        return ""

Możesz tego używać w zasadzie w taki sam sposób, jak aktywnego tagu:

{% url product_url as product %}

{% ifnaviactive request product %}
    <ul class="subnavi">
        <li>Subnavi item for product 1</li>
        ...
    </ul>
{% endifnaviactive %}
Nino
źródło
2

Kolejne ulepszenie oryginalnego rozwiązania.

Akceptuje wiele wzorców i najlepiej sprawdza się również wzorce nienazwane zapisane jako względny adres URL zawinięty w „” ”, na przykład:

{% url admin:clients_client_changelist as clients %}
{% url admin:clients_town_changelist as towns %}
{% url admin:clients_district_changelist as districts %}

<li class="{% active "/" %}"><a href="/">Home</a></li>
<li class="{% active clients %}"><a href="{{ clients }}">Clients</a></li>
{% if request.user.is_superuser %}
<li class="{% active towns districts %}">
    <a href="#">Settings</a>
    <ul>
        <li><a href="{{ towns }}">Towns</a></li>
        <li><a href="{{ districts }}">Districts</a></li>
    </ul>
</li>
{% endif %}

Tag wygląda tak:

from django import template

register = template.Library()

@register.tag
def active(parser, token):
    args = token.split_contents()
    template_tag = args[0]
    if len(args) < 2:
        raise template.TemplateSyntaxError, "%r tag requires at least one argument" % template_tag
    return NavSelectedNode(args[1:])

class NavSelectedNode(template.Node):
    def __init__(self, urls):
        self.urls = urls

    def render(self, context):
        path = context['request'].path

        for url in self.urls:
            if '"' not in url:
                cpath = template.Variable(url).resolve(context)
            else:
                cpath = url.strip('"')

            if (cpath == '/' or cpath == '') and not (path == '/' or path == ''):
                return ""
            if path.startswith(cpath):
                return 'active'
        return ""
xaralis
źródło
2

Użyłem jquery do wyróżnienia moich navbarów. To rozwiązanie po prostu dodaje klasę css „active” do pozycji, która pasuje do selektora css.

<script type="text/javascript" src="/static/js/jquery.js"></script>
<script>
    $(document).ready(function(){
        var path = location.pathname;
        $('ul.navbar a.nav[href$="' + path + '"]').addClass("active");
    });
</script>
matts1
źródło
2

Trochę ulepszenia w stosunku do odpowiedzi @tback , bez żadnych %if%tagów:

# navigation.py
from django import template
from django.core.urlresolvers import resolve

register = template.Library()

@register.filter(name="activate_if_active", is_safe=True)
def activate_if_active(request, urlname):
  if resolve(request.get_full_path()).url_name == urlname:
    return "active"
  return ''

Użyj go w swoim szablonie w ten sposób:

{% load navigation %}
<li class="{{ request|activate_if_active:'url_name' }}">
  <a href="{% url 'url_name' %}">My View</a>
</li>

I uwzględnij "django.core.context_processors.request"w swoim TEMPLATE_CONTEXT_PROCESSORSotoczeniu.

MadeOfAir
źródło
2

Uważam, że najlepiej jest użyć tagu włączenia:

templates/fnf/nav_item.html

<li class="nav-item">
    <a class="nav-link {% if is_active %}active{% endif %}" href="{% url url_name %}">{{ link_name }}</a>
</li>

To jest tylko mój podstawowy element nawigacyjny bootstrap, który chcę renderować.

Pobiera wartość href i opcjonalnie wartość link_name. is_activejest obliczany na podstawie bieżącego żądania.

templatetags/nav.py

from django import template

register = template.Library()


@register.inclusion_tag('fnf/nav_item.html', takes_context=True)
def nav_item(context, url_name, link_name=None):
    return {
        'url_name': url_name,
        'link_name': link_name or url_name.title(),
        'is_active': context.request.resolver_match.url_name == url_name,
    }

Następnie użyj go w nawigacji: templates/fnf/nav.html

{% load nav %}
<nav class="navbar navbar-expand-lg navbar-light bg-light">
        <ul class="navbar-nav mr-auto">
                {% nav_item 'dashboard' %}
            </ul>
Tjorriemorrie
źródło
Tylko pobieżna lektura, ale czy to nie ogranicza rzeczy do dokładnych dopasowań w adresie URL? Często używam wskazówek nawigacyjnych, takich jak ta dla głębokich stron. Np. Pozycja About nav byłaby podświetlona, ​​gdybyś był na jednym /about/company-history/lub /about/what-we-do/
Oli
1
Tak, ale is_activemożna je wymienić, a inne klucze dodać do słownika. Również czek może być context.request.resolver_match.url_name.startswith(x)lub cokolwiek innego. Możesz również mieć kod przed instrukcją return, aby ustalić wartości dict. Możesz także użyć różnych szablonów, np. Jednego top_level_nav.htmlz inną logiką itp.
Tjorriemorrie
Czyste i proste rozwiązanie ... fajnie!
mmw
1

Nieznacznie modyfikując odpowiedź Andreasa, wygląda na to, że możesz przekazać nazwę trasy z urls.py do tagu szablonu. W moim przykładzie my_tasks, a następnie w funkcji znacznika szablonu użyj funkcji odwrotnej, aby ustalić, jaki powinien być adres URL, wtedy możesz dopasować go do adresu URL w obiekcie żądania (dostępne w kontekście szablonu)

from django import template
from django.core.urlresolvers import reverse

register = template.Library()

@register.tag
def active(parser, token):
    args = token.split_contents()
    template_tag = args[0]
    if len(args) < 2:
        raise template.TemplateSyntaxError, "%r tag requires at least one argument" % template_tag
    return NavSelectedNode(args[1:])

class NavSelectedNode(template.Node):
    def __init__(self, name):
        self.name = name

    def render(self, context):

        if context['request'].path == reverse(self.name[1]):
            return 'active'
        else:
            return ''

urls.py

url(r'^tasks/my', my_tasks, name = 'my_tasks' ),

template.html

<li class="{% active request all_tasks %}"><a href="{% url all_tasks %}">Everyone</a></li>
errkk
źródło
Może bardziej proste podejście: turnkeylinux.org/blog/django-navbar
jgsogo.
1

Wiem, że spóźniłem się na przyjęcie. Nie podobało mi się jednak żadne z popularnych rozwiązań:

Metoda blokowa wydaje się błędna: myślę, że nawigacja powinna być samodzielna.

Metoda template_tag wydaje się nieprawidłowa: nie podoba mi się to, że muszę najpierw pobrać adres URL z tagu url. Myślę też, że klasę css należy zdefiniować w szablonie, a nie w tagu.

Dlatego napisałem filtr, który nie ma wad, które opisałem powyżej. Zwraca, Truejeśli adres URL jest aktywny i dlatego może być używany z {% if %}:

{% load navigation %}
<li{% if request|active:"home" %} class="active"{% endif %}><a href="{% url "home" %}">Home</a></li>

Kod:

@register.filter(name="active")
def active(request, url_name):
    return resolve(request.path_info).url_name == url_name

Po prostu upewnij się, że używasz RequestContextna stronach z nawigacją lub włączasz żądanie context_processor w swoimsettings.py

TEMPLATE_CONTEXT_PROCESSORS = (
    ...
    'django.core.context_processors.request',
)
tback
źródło
1

Widziałem jpwatts , 110j , nivhab i Marcus Whybrow , ale wydaje się, że wszystkim im czegoś brakuje: a co ze ścieżką ? Dlaczego jest zawsze aktywny?

Zrobiłem więc inny sposób, łatwiejszy, który sprawia, że ​​„kontroler” sam decyduje i myślę, że rozwiązuje większość dużych problemów.

Oto mój niestandardowy tag:

## myapp_tags.py

@register.simple_tag
def nav_css_class(page_class):
    if not page_class:
        return ""
    else:
        return page_class

Następnie "kontroler" deklaruje potrzebne klasy CSS (tak naprawdę najważniejsze to deklaruje swoją obecność do szablonu)

## views.py

def ping(request):
    context={}
    context["nav_ping"] = "active"
    return render(request, 'myapp/ping.html',context)

Na koniec renderuję go na pasku nawigacyjnym:

<!-- sidebar.html -->

{% load myapp_tags %}
...

<a class="{% nav_css_class nav_home %}" href="{% url 'index' %}">
    Accueil
</a>
<a class="{% nav_css_class nav_candidats %}" href="{% url 'candidats' %}">
    Candidats
</a>
<a class="{% nav_css_class nav_ping %}" href="{% url 'ping' %}">
    Ping
</a>
<a class="{% nav_css_class nav_stat %}" href="{% url 'statistiques' %}">
    Statistiques
</a>
...

Tak więc każda strona ma własną nav_css_classwartość do ustawienia, a jeśli jest ustawiona, szablon renderuje się jako aktywny: bez potrzeby requestw kontekście szablonu, bez parowania adresów URL i żadnych problemów ze stronami z wieloma adresami URL lub stroną główną.

DestyNova
źródło
1

Zainspirowany tym rozwiązaniem zacząłem stosować takie podejście:

**Placed in templates as base.html**

{% block tab_menu %}
<ul class="tab-menu">
  <li class="{% if active_tab == 'tab1' %} active{% endif %}"><a href="#">Tab 1</a></li>
  <li class="{% if active_tab == 'tab2' %} active{% endif %}"><a href="#">Tab 2</a></li>
  <li class="{% if active_tab == 'tab3' %} active{% endif %}"><a href="#">Tab 3</a></li>
</ul>
{% endblock tab_menu %}

**Placed in your page template**

{% extends "base.html" %}

{% block tab_menu %}
  {% with active_tab="tab1" %} {{ block.super }} {% endwith %}
{% endblock tab_menu %}
Evgeny Bobkin
źródło
0

Oto moja propozycja. Skończyło się na zaimplementowaniu klasy w moich widokach, która zawiera moją strukturę nawigacji (płaska z niektórymi metadanymi). Następnie wstrzykuję to do szablonu i renderuję.

Moje rozwiązanie dotyczy i18n. Prawdopodobnie powinien być nieco bardziej abstrakcyjny, ale tak naprawdę nie przejmowałem się tym.

views.py:

from django.utils.translation import get_language, ugettext as _


class Navi(list):
    items = (_('Events'), _('Users'), )

    def __init__(self, cur_path):
        lang = get_language()
        first_part = '/' + cur_path.lstrip('/').split('/')[0]

        def set_status(n):
            if n['url'] == first_part:
                n['status'] == 'active'

        for i in self.items:
            o = {'name': i, 'url': '/' + slugify(i)}
            set_status(o)
            self.append(o)

# remember to attach Navi() to your template context!
# ie. 'navi': Navi(request.path)

Zdefiniowałem logikę szablonu przy użyciu takich elementów. Szablon podstawowy:

{% include "includes/navigation.html" with items=navi %}

Rzeczywiste uwzględnienie (obejmuje / navigation.html):

 <ul class="nav">
     {% for item in items %}
         <li class="{{ item.status }}">
             <a href="{{ item.url }}">{{ item.name }}</a>
         </li>
     {% endfor %}
 </ul>

Miejmy nadzieję, że ktoś uzna to za przydatne! Myślę, że byłoby całkiem łatwo rozszerzyć ten pomysł na obsługę zagnieżdżonych hierarchii itp.

Juho Vepsäläinen
źródło
0

Utwórz szablon dołączania „intranet / nav_item.html”:

{% load url from future %}

{% url view as view_url %}
<li class="nav-item{% ifequal view_url request.path %} current{% endifequal %}">
    <a href="{{ view_url }}">{{ title }}</a>
</li>

I umieść to w elemencie nav:

<ul>
    {% include "intranet/nav_item.html" with view='intranet.views.home' title='Home' %}
    {% include "intranet/nav_item.html" with view='crm.views.clients' title='Clients' %}
</ul>

I musisz to dodać do ustawień:

from django.conf import global_settings
TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
    'django.core.context_processors.request',
)
Za
źródło
0

tutaj jest całkiem proste rozwiązanie, https://github.com/hellysmile/django-activeurl

Віктор Ковтун
źródło
1
Pamiętaj, że powinieneś zamieścić przydatne punkty odpowiedzi tutaj, na tej stronie, w przeciwnym razie Twój post może zostać usunięty jako „Brak odpowiedzi” . Jeśli chcesz, możesz nadal dołączyć link, ale tylko jako „odniesienie”. Odpowiedź powinna istnieć sama, bez potrzeby korzystania z łącza.
Andrew Barber
0

z tego pytania SO

{% url 'some_urlpattern_name' as url %}
<a href="{{url}}"{% if request.path == url %} class="active"{% endif %}>Link</a>

W razie potrzeby powtórz te czynności dla każdego łącza.

suhailvs
źródło
Działa to tylko w przypadku bezpośrednich meczów. Większość systemów nawigacji zaznacza element nawigacji jako aktywny, jeśli aktywna jest również strona podrzędna. To znaczy, jeśli /blog/posts/2021/04/12byłby adresem URL, element / blog / nav byłby aktywny.
Oli
@Oli tak, to czasami nie zadziała. na przykład w stackoverflow nawigacji IE Questions, Tags, Users, Badges, Unanswered, Ask Question. to nie zadziała Questions, ale dla wszystkich innych nawigacji będzie działać dobrze.
suhailvs
0

Użyłem również jQuery, aby go podświetlić i uważam, że jest bardziej elegancki niż zaśmiecanie szablonu niesemantycznymi tagami szablonu Django.

Poniższy kod działa z zagnieżdżonymi listami rozwijanymi w bootstrap 3 (podświetla zarówno element nadrzędny, jak i podrzędny <li>.

// DOM Ready
$(function() {
    // Highlight current page in nav bar
    $('.nav, .navbar-nav li').each(function() {
        // Count the number of links to the current page in the <li>
        var matched_links = $(this).find('a[href]').filter(function() {
            return $(this).attr('href') == window.location.pathname; 
        }).length;
        // If there's at least one, mark the <li> as active
        if (matched_links)
            $(this).addClass('active');
    });
});

Dość łatwo jest również dodać clickzdarzenie return false(lub zmienić hrefatrybut na #) dla bieżącej strony, bez zmiany znaczników template / html:

        var matched_links = $(this).find('a[href]').filter(function() {
            var matched = $(this).attr('href') == window.location.pathname;
            if (matched)
                $(this).click(function() { return false; });
            return matched;
        }).length;
user193130
źródło
0

Używam kombinacji tej mieszanki do widoków opartych na klasach:

class SetActiveViewMixin(object):
    def get_context_data(self, **kwargs):
        context = super(SetActiveViewMixin, self).get_context_data(**kwargs)
        context['active_nav_menu'] = {
            self.request.resolver_match.view_name: ' class="pure-menu-selected"'
        }
        return context

z tym w szablonie:

<ul>
    <li{{active_nav_menu.node_explorer }}><a href="{% url 'node_explorer' '' %}">Explore</a></li>
    <li{{active_nav_menu.node_create }}><a href="{% url 'node_create' path %}">Create</a></li>
    <li{{active_nav_menu.node_edit }}><a href="{% url 'node_edit' path %}">Edit</a></li>
    <li{{active_nav_menu.node_delete }}><a href="{% url 'node_delete' path %}">Delete</a></li>
</ul>
Brian Faherty
źródło
0

Moje jest trochę podobne do innego podejścia JS przesłanego wcześniej ... tylko bez jQuery ...

Powiedzmy, że w base.html mamy co następuje:

<div class="pure-u-1 pure-menu pure-menu-open pure-menu-horizontal header" >
    <ul class="">
        <li id="home"><a href="{% url 'article:index' %}">Home</a></li>
        <li id="news"><a href="{% url 'article:index' %}">News</a></li>
        <li id="analysis"><a href="{% url 'article:index' %}">Analysis</a></li>
        <li id="opinion"><a href="{% url 'article:index' %}">Opinion</a></li>
        <li id="data"><a href="{% url 'article:index' %}">Data</a></li>
        <li id="events"><a href="{% url 'article:index' %}">Events</a></li>
        <li id="forum"><a href="{% url 'article:index' %}">Forum</a></li>
        <li id="subscribe"><a href="{% url 'article:index' %}">Subscribe</a></li>
    </ul>
    <script type="text/javascript">
        (function(){
            loc=/\w+/.exec(window.location.pathname)[0];
            el=document.getElementById(loc).className='pure-menu-selected';         
        })();   
    </script>
</div>

Właśnie utworzyłem hierarchię tak, aby podążała za określonym wzorcem adresu URL ... po adresie hosta ... mam swoją główną kategorię, np. Dom, wiadomości, analiza itp., A wyrażenie regularne po prostu wyciąga pierwsze słowo z lokalizacji

Logik Sounds
źródło