Navigation in Django


104

Ich habe gerade meine erste kleine Webapp in Django gemacht und ich liebe es. Ich bin dabei, eine alte PHP-Produktionssite in Django zu konvertieren, und als Teil ihrer Vorlage gibt es eine Navigationsleiste.

In PHP überprüfe ich die URL jeder Navigationsoption mit der aktuellen URL im Vorlagencode und wende eine CSS-Klasse an, wenn sie ausgerichtet sind. Es ist schrecklich chaotisch.

Gibt es etwas Besseres für Django oder eine gute Möglichkeit, mit dem Code in der Vorlage umzugehen?

Wie würde ich zunächst die aktuelle URL abrufen?


Ich habe dafür github.com/orokusaki/django-active-menu erstellt - es unterstützt verschachtelte URL-Strukturen und basiert auf der Konfiguration über Konventionen (so böse das klingt), sodass Sie die Hierarchie Ihrer Site definieren können, wie Sie möchten. Sie verwenden nur <a href="{% url "view:name" %}" {% active_class "view:name" %}>. Sie können es optional verwenden, um nur den " active"Wert zu generieren (indem Sie ihn Falseals zweites Argument an das Tag übergeben), um ihn an ein vorhandenes Klassenattribut anzuhängen. Für die meisten Navigationslinks verwende ich dieses Beispiel.
Orokusaki

Diese Frage scheint im Zusammenhang mit diesem einen stackoverflow.com/a/9801473/5739875
Evgeny Bobkin

Vielleicht hilft dieses Raster: djangopackages.org/grids/g/navigation
guettli

Antworten:


74

Ich verwende die Vorlagenvererbung, um die Navigation anzupassen. Beispielsweise:

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 %}

Ich mag diese Idee sehr, besonders aus Gründen der Flexibilität, aber sie ist mit einem weniger trockenen Kompromiss verbunden. Ich habe dies jedoch auf einer Website verwendet.
anonymer Feigling

22
Ich bin von diesem Ansatz nicht begeistert, da es nicht ungewöhnlich ist, dass mehrere Site-Abschnitte von derselben Untervorlage behandelt werden. Am Ende fügen Sie benutzerdefinierte Variablen in Ansichten und Bedingungen in Vorlagen ein oder ordnen Untervorlagen neu an, sodass sie alle einzigartig sind ... nur um den aktuellen Site-Abschnitt zu erkennen. Der Template-Tag-Ansatz ist am Ende sauberer.
Shacker

Ich habe mir ein paar andere Lösungen angesehen, und es scheint, als wären sie alle ein bisschen hackig. Zumindest dieser ist ziemlich einfach und einfach zu implementieren / zu verschrotten.
mlissner

Ich habe das <ul id="nav">....</ul>in eine andere Datei umgestaltet , sagen wir tabs.html. Jetzt ist base.html enthalten {%block nav%}{%include "tabs.html"%}{%endblock%}und das Hervorheben der aktiven Registerkarte funktioniert nicht mehr (in about.html oben). Vermisse ich etwas
Keine-da

@Maddy Du hast genug Indirektion, dass ich nicht absolut sicher bin, ob ich es gerade im Kopf habe, aber ich denke, die Antwort hat damit zu tun, wie das includeTag funktioniert. Schauen Sie sich die Notiz in der Dokumentation enthalten: docs.djangoproject.com/en/dev/ref/templates/builtins/#include In Ihrem Fall durch die Zeit , Sie versuchen , die Basisvorlage außer Kraft zu setzen in about.html, ich glaube , Sie haben Ich habe bereits einen gerenderten HTML-Block anstelle eines Django-Vorlagenblocks, der darauf wartet, verarbeitet zu werden.
jpwatts

117

Sie brauchen kein Wenn, um das zu tun, schauen Sie sich den folgenden Code an:

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>

das ist es. Einzelheiten zur Implementierung finden Sie unter:
gnuvince.wordpress.com
110j.wordpress.com


2
In den Eigenschaften des href fehlen die Django-Vorlagenklammern {{,}}. Beispielsweise sollte <a class="{% active request home %}" href="home"> Home </a> <a class = "{% active request home%}" href = "{{home} sein. } "> Home </a> Die Datei tags.py benötigt auch einige Includes. Ansonsten tolle Lösung!
bsk

2
+1 Dies ist lockerer von Anwendungen gekoppelt. Als Anfänger habe ich herausgefunden, dass Tags eine eigene App benötigen. Sie können diese nicht einfach in eine globale tags.py-Datei kopieren. Ich habe eine neue App namens Tags erstellt und alles verlief reibungslos. docs.djangoproject.com/de/dev/howto/custom-template-tags
Keyo

3
@Keyo, erstellen Sie ein Templatetags-Verzeichnis in Ihrem Projekt und fügen Sie Ihr Projekt zu installierten Apps hinzu. Das reicht auch. Alternativ können Sie, wie Sie sagten, Ihre Hauptwebsite als App in Ihrem Projekt erstellen.
Josh Smeaton

5
Vergessen Sie nicht, zu django.core.context_processors.requestIhrem TEMPLATE_CONTEXT_PROCESSORSinsettings.py
amigcamel

1
Dies ist ungültig für Zustände, die verschachtelt sein können, z. B. mysite.com(als Heimat) und mysite.com/blog, wie der Pfad zeigt, /bzw. /blog/(bzw.) die jeweils eine Übereinstimmung für den ersteren ergeben. Wenn Sie nicht /als Landung verwenden, ist dies möglicherweise in Ordnung, andernfalls verwende ich nur return 'active' if pattern == request.path else ''(ich habe noch keine Probleme damit gesehen, aber ich habe mich nur damit eingerichtet).
Nerdwaller

33

Ich mochte die Sauberkeit von 110j oben, also nahm ich das meiste davon und überarbeitete, um die 3 Probleme zu lösen, die ich damit hatte:

  1. Der reguläre Ausdruck stimmte mit der URL "Zuhause" mit allen anderen überein
  2. Ich brauchte mehrere URLs, die einer Navigationsregisterkarte zugeordnet waren , daher brauchte ich ein komplexeres Tag, das eine variable Anzahl von Parametern akzeptiert
  3. Einige URL-Probleme wurden behoben

Hier ist es:

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>

Vielleicht antworten wir am besten mit Marcus, aber wie funktioniert es mit dem "Zuhause"? es ist immer aktiv? Wie kann ich es nur bei einem Root-URL-Aufruf (www.toto.com/ und www.toto.com/index) aktivieren? Beide Antworten führen nicht zu diesem Problem ...
DestyNova

20

Ich bin der Autor der Django-Linie, die ich speziell geschrieben habe, um diese Frage zu lösen: D.

Ich ärgerte mich über die (vollkommen akzeptable) jpwatts-Methode in meinen eigenen Projekten und ließ mich von 110js Antwort inspirieren. Die Abstammung sieht folgendermaßen aus:

{% 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 wird einfach durch "aktiv" ersetzt, wenn das Argument mit dem Anfang der aktuellen Seiten-URL übereinstimmt.

Variable Argumente und die vollständige {% url %}umgekehrte Auflösung werden ebenfalls unterstützt. Ich habe ein paar Konfigurationsoptionen eingestreut und es ein wenig ausgearbeitet und für alle verpackt.

Wenn jemand interessiert ist, lesen Sie ein bisschen mehr darüber unter:

>> github.com/marcuswhybrow/django-lineage


1
harte Codierungspfade in die Vorlage :(
CpILL

10

Seit Django 1.5 :

In allen generischen klassenbasierten Ansichten (oder allen von ContextMixin geerbten klassenbasierten Ansichten) enthält das Kontextwörterbuch eine Ansichtsvariable, die auf die View-Instanz verweist.

Wenn Sie solche Ansichten verwenden, können Sie etwas Ähnliches breadcrumbsals Feld auf Klassenebene hinzufügen und in Ihren Vorlagen verwenden.

Beispielansichtscode:

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

In Ihrer Vorlage können Sie sie folgendermaßen verwenden:

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

Wenn Sie übergeordnete Navigationselemente zusätzlich "hervorheben" möchten, müssen Sie die breadcrumbsListe erweitern:

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

... und in Ihrer Vorlage:

<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>

Dies ist eine einfache und saubere Lösung und funktioniert ziemlich gut mit verschachtelter Navigation.


Wären in diesem Beispiel nicht alle drei Navigationselemente .active?
Oli

Ja, aber dies ist normalerweise das, was Sie mit einer mehrstufigen Navigation erreichen möchten. Sie können natürlich einen Artikel einfügen, breadcrumbswenn Sie möchten. Aber Sie haben Recht - mein Beispiel ist nicht das beste.
Konrad Hałas

@Oli verbessertes Beispiel.
Konrad Hałas

9

Sie können eine Klasse oder ID auf das Hauptelement der Seite und nicht auf ein bestimmtes Navigationselement anwenden.

HTML:

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

CSS:

body.home #nav_home,
body.about #nav_about { */ Current nav styles */ }

8

So mach ich es:

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

und dann muss ich aus meiner Sicht nur noch {'active_tab': 'statistics'}zu meinem Kontextwörterbuch hinzufügen .

Wenn Sie verwenden RequestContext, können Sie den aktuellen Pfad in Ihrer Vorlage wie folgt abrufen:

{{ request.path }}

Und aus Ihrer Sicht:

from django.template import RequestContext

def my_view(request):
    # do something awesome here
    return template.render(RequestContext(request, context_dict))

Vielen Dank für das Teilen dieser Informationen. Ich habe diese Methode verwendet, hatte aber auch eine Flatpage in meiner Navigationsleiste. Um dies zu erkennen und korrekt hervorzuheben, habe ich {% ifequal flatpage.url '/ about /'%} verwendet. Ich mag die fest codierte Erkennung der URL nicht, aber sie funktioniert für einen einmaligen Hack.
Matt Garrison

Das Problem bei dieser Lösung ist, dass Sie fest codierte "Statistiken" im Code haben. Dies macht den Zweck der Verwendung des URL-Tags zum Abrufen der URL der Seite zunichte.
Justin

7

Ich nahm den Code von nivhab oben und entfernte etwas Seltsamkeit und machte ihn zu einem sauberen Templatetag, modifizierte ihn so, dass / account / edit / / account / tab weiterhin aktiv macht.

#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 %}

6

Dies ist nur eine Variante der oben von Toba vorgeschlagenen CSS-Lösung:

Nehmen Sie Folgendes in Ihre Basisvorlage auf:

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

Dann in Ihren Vorlagen, die die Basis erweitern, verwenden Sie:

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

Sie können dann CSS verwenden, um den aktuellen Bereich basierend auf dem Body-Tag hervorzuheben (z. B. wenn wir einen Link mit der ID nav-home haben):

#section-home a#nav-home{
 font-weight:bold;
}


3

Vielen Dank für Ihre bisherigen Antworten, Herren. Ich habe mich wieder für etwas anderes entschieden.

In meiner Vorlage:

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

Sobald ich herausgefunden habe, auf welcher Seite ich mich in der Logik befinde (normalerweise in urls.py), übergebe ich class="selected"als Teil des Kontexts unter dem richtigen Namen die Vorlage.

Wenn ich beispielsweise auf der Seite link1 bin, werde ich anhängen {'link1_active':' class="selected"'} den Kontext , damit die Vorlage aufgenommen und injiziert werden kann.

Es scheint zu funktionieren und ist ziemlich sauber.

Bearbeiten: Um HTML von meinem Controller / meiner Ansicht fernzuhalten, habe ich dies ein wenig geändert:

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

Es macht die Vorlage etwas weniger lesbar, aber ich stimme zu, es ist besser, nicht rohen HTML-Code aus der URL-Datei zu pushen.


2
Sie sollten es wirklich vermeiden, in Ihrer Ansicht mit rohem HTML umzugehen, was für diese Technik erforderlich ist. Haben Sie darüber nachgedacht, ein benutzerdefiniertes Vorlagen-Tag zu schreiben?
Justin Voss

Du hast recht. Ich habe bearbeitet, um den HTML-Code nicht mehr zu durchlaufen. Ich gehe jetzt einfach durch True. Ich habe noch keine Vorlagen-Tags geschrieben, aber ja, dies könnte ein guter Anfang sein.
Oli

2

Ich habe mehrere Menüs auf derselben Seite, die dynamisch über eine Schleife erstellt werden. Die obigen Beiträge zum Kontext gaben mir eine schnelle Lösung. Hoffe das hilft jemandem. (Ich verwende dies zusätzlich zum aktiven Vorlagen-Tag - mein Fix löst das dynamische Problem). Es scheint ein dummer Vergleich zu sein, aber es funktioniert. Ich habe mich entschieden, die Variablen active_something-unique und etwas-unique zu benennen. Auf diese Weise funktioniert es mit verschachtelten Menüs.

Hier ist ein Teil der Ansicht (genug, um zu verstehen, was ich tue):

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),

    }

Und das ist aus der Vorlage:

<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>

2

Meine Lösung bestand darin, einen einfachen Kontextprozessor zu schreiben, um eine Variable basierend auf dem Anforderungspfad festzulegen:

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}

(Vergessen Sie nicht, Ihren benutzerdefinierten Prozessor in settings.py zu TEMPLATE_CONTEXT_PROCESSORS hinzuzufügen.)

Dann verwende ich in der Basisvorlage ein ifequal-Tag pro Link, um zu bestimmen, ob die "aktive" Klasse angehängt werden soll. Zugegeben, dieser Ansatz ist streng auf die Flexibilität Ihrer Pfadstruktur beschränkt, funktioniert jedoch für meine relativ bescheidene Bereitstellung.


Ich denke, es ist wirklich sinnvoll, diese im globalen Kontext zu haben, so dass Sie den Site-Abschnitt auf verschiedene Arten referenzieren können (z. B. unter Verwendung verschiedener Vorlagen für verschiedene Site-Abschnitte. +1.
Shacker

2

Ich wollte nur meine kleine Verbesserung von Nivhabs Post teilen. In meiner Anwendung habe ich Subnavigationen und ich wollte sie nicht nur mit CSS ausblenden, daher brauchte ich eine Art "if" -Tag, um die Subnavigation für ein Element anzuzeigen oder nicht.

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 ""

Sie können dies grundsätzlich genauso verwenden wie das aktive Tag:

{% url product_url as product %}

{% ifnaviactive request product %}
    <ul class="subnavi">
        <li>Subnavi item for product 1</li>
        ...
    </ul>
{% endifnaviactive %}

2

Nur eine weitere Verbesserung der ursprünglichen Lösung.

Dies akzeptiert mehrere Muster und am besten auch unbenannte Muster, die als relative URL in '"' geschrieben sind, wie folgt:

{% 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 geht so:

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 ""

2

Ich habe jquery verwendet, um meine Navigationsleisten hervorzuheben. Diese Lösung fügt einfach die CSS-Klasse "aktiv" zu dem Element hinzu, das zum CSS-Selektor passt.

<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>

2

Eine kleine Verbesserung gegenüber der Antwort von @tback ohne %if%Tags:

# 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 ''

Verwenden Sie es in Ihrer Vorlage wie folgt:

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

Und "django.core.context_processors.request"in Ihre TEMPLATE_CONTEXT_PROCESSORSEinstellung aufnehmen.


2

Ich fand es am besten, ein Einschluss-Tag zu verwenden:

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>

Dies ist nur mein grundlegendes Bootstrap-Navigationselement, das ich rendern möchte.

Es erhält den href-Wert und optional den link_name-Wert. is_activewird basierend auf der aktuellen Anfrage berechnet.

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,
    }

Dann verwenden Sie es in einem Navi: 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>

Nur eine flüchtige Lektüre, aber beschränkt sich dies nicht auf genaue Übereinstimmungen in der URL? Ich verwende solche Navigationshinweise normalerweise auch für tiefe Seiten. ZB würde das Element Über Navi hervorgehoben, wenn Sie entweder am /about/company-history/oder /about/what-we-do/
Oli

1
Ja, is_activekann aber ersetzt und andere Schlüssel zum Wörterbuch zurückgegeben werden. Auch der Scheck kann sein context.request.resolver_match.url_name.startswith(x)oder irgendetwas anderes. Sie können auch Code vor der return-Anweisung haben, um die dict-Werte festzulegen. Sie können auch verschiedene Vorlagen verwenden, dh eine für top_level_nav.htmlmit unterschiedlicher Logik usw.
Tjorriemorrie

Saubere und einfache Lösung ... schön!
mmw

1

Wenn Sie die Antwort von Andreas leicht ändern, können Sie den Namen der Route von urls.py an das Vorlagen-Tag übergeben. In meinem Beispiel my_tasksund dann in der Vorlagen-Tag-Funktion verwenden Sie die Umkehrfunktion, um herauszufinden, wie die URL lauten soll. Dann können Sie diese mit der URL im Anforderungsobjekt abgleichen (verfügbar im Vorlagenkontext).

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>

Vielleicht ein einfacherer Ansatz: turnkeylinux.org/blog/django-navbar
jgsogo

1

Ich weiß, dass ich zu spät zur Party komme. Ich mochte jedoch keine der beliebten Lösungen:

Die Blockmethode scheint falsch zu sein: Ich denke, die Navigation sollte in sich geschlossen sein.

Die template_tag-Methode scheint falsch zu sein: Ich mag es nicht, dass ich zuerst die URL vom URL-Tag abrufen muss. Ich denke auch, dass die CSS-Klasse in der Vorlage definiert werden sollte, nicht im Tag.

Ich habe daher einen Filter geschrieben, der nicht die oben beschriebenen Nachteile aufweist. Es wird zurückgegeben, Truewenn eine URL aktiv ist, und kann daher verwendet werden mit {% if %}:

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

Der Code:

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

RequestContextStellen Sie einfach sicher, dass Sie auf Seiten mit Navigation verwenden oder die Anforderung context_processor in Ihrem aktivierensettings.py

TEMPLATE_CONTEXT_PROCESSORS = (
    ...
    'django.core.context_processors.request',
)

1

Ich habe jpwatts ', 110j ' s, nivhab 's und Marcus Whybrow gesehen , aber allen scheint etwas zu fehlen: Was ist mit dem Wurzelpfad? Warum ist es immer aktiv?

Also habe ich einen anderen Weg einfacher gemacht, der den "Controller" dazu bringt, selbst zu entscheiden, und ich denke, er löst die meisten großen Probleme.

Hier ist mein benutzerdefiniertes Tag:

## myapp_tags.py

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

Dann deklariert der "Controller" die benötigten CSS-Klassen (am wichtigsten ist, dass er der Vorlage seine Anwesenheit deklariert).

## views.py

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

Und schließlich rendere ich es in meiner Navigationsleiste:

<!-- 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>
...

Daher muss für jede Seite ein eigener nav_css_classWert festgelegt werden. Wenn diese festgelegt ist, wird die Vorlage aktiv: Keine Notwendigkeit requestim Vorlagenkontext, kein URL-Parcing und keine Probleme mehr mit Seiten mit mehreren URLs oder Stammseiten.


1

Inspiriert von dieser Lösung begann ich, diesen Ansatz zu verwenden:

**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 %}

0

Hier ist mein Versuch. Am Ende habe ich eine Klasse in meinen Ansichten implementiert, die meine Navigationsstruktur enthält (flach mit einigen Metadaten). Ich füge dies dann in die Vorlage ein und rendere es aus.

Meine Lösung befasst sich mit i18n. Es sollte wahrscheinlich ein bisschen mehr abstrahiert werden, aber das hat mich nicht wirklich gestört.

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)

Ich habe die Vorlagenlogik mit solchen Includes definiert. Basisvorlage:

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

Tatsächliches Include (Includes / Navigation.html):

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

Hoffentlich findet das jemand nützlich! Ich denke, es wäre ziemlich einfach, diese Idee zu erweitern, um verschachtelte Hierarchien usw. zu unterstützen.


0

Erstellen Sie eine Include-Vorlage "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>

Und fügen Sie es in das nav-Element ein:

<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>

Und Sie müssen dies zu den Einstellungen hinzufügen:

from django.conf import global_settings
TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
    'django.core.context_processors.request',
)

0

Hier ist eine ziemlich einfache Lösung: https://github.com/hellysmile/django-activeurl


1
Bitte beachten Sie, dass Sie die nützlichen Punkte einer Antwort hier auf dieser Website veröffentlichen sollten. Andernfalls besteht die Gefahr, dass Ihr Beitrag als "Keine Antwort" gelöscht wird . Sie können den Link weiterhin einfügen, wenn Sie dies wünschen, jedoch nur als 'Referenz'. Die Antwort sollte für sich allein stehen, ohne den Link zu benötigen.
Andrew Barber

0

von dieser SO Frage

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

Wiederholen Sie diesen Vorgang nach Bedarf für jeden Link.


Dies funktioniert nur für direkte Übereinstimmungen. Die meisten Navigationssysteme markieren das Navigationselement als aktiv, wenn auch eine Nachkommenseite aktiv ist. Dh , wenn /blog/posts/2021/04/12die URL ist der / Blog / nav Artikel würde aktiv sein.
Oli

@Oli ja es wird manchmal nicht funktionieren. zum Beispiel in Stackoverflow - Navigation dh Questions, Tags, Users, Badges, Unanswered, Ask Question. es wird nicht funktionieren Questions, aber für alle anderen Navis wird es gut funktionieren.
Suhailvs

0

Ich habe jQuery auch verwendet, um es hervorzuheben und eleganter zu finden, als die Vorlage mit nicht-semantischen Django-Vorlagen-Tags zu überladen.

Der folgende Code funktioniert mit verschachtelten Dropdowns in Bootstrap 3 (hebt sowohl das übergeordnete als auch das untergeordnete <li>Element hervor.

// 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');
    });
});

Es ist auch ganz einfach, der aktuellen Seite ein clickEreignis hinzuzufügen return false(oder das hrefAttribut in zu ändern #), ohne das Vorlagen- / HTML-Markup zu ändern:

        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;

0

Ich verwende eine Kombination dieses Mixins für klassenbasierte Ansichten:

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

mit diesem in der Vorlage:

<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>

0

Meins ähnelt ein bisschen einem anderen JS-Ansatz, der zuvor eingereicht wurde. Nur ohne jQuery ...

Angenommen, wir haben in base.html Folgendes:

<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>

Ich habe gerade meine Hierarchie so gestaltet, dass sie einem bestimmten URL-Muster folgt ... nach der Hostadresse ... ich habe meine Hauptkategorie, z. B. Startseite, Nachrichten, Analyse usw., und der reguläre Ausdruck zieht nur das erste Wort aus dem Speicherort

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.