web-dev-qa-db-fra.com

Navigation dans le Django

Je viens de faire ma première petite application Web à Django et j'adore ça. Je suis sur le point de commencer à convertir un ancien site de production PHP en Django et, dans le cadre de son modèle, une barre de navigation.

En PHP, je compare l'URL de chaque option de navigation à l'URL actuelle dans le code du modèle et applique une classe CSS si elles s'alignent. C'est horriblement malpropre.

Y a-t-il quelque chose de mieux pour Django ou un bon moyen de gérer le code dans le modèle?

Pour commencer, comment pourrais-je obtenir l’URL actuelle?

96
Oli

J'utilise l'héritage de modèles pour personnaliser la navigation. Par exemple:

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

Vous n'avez pas besoin d'un if pour le faire, regardez le code suivant:

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>

ça y est . pour les détails de la mise en œuvre, regardez: 
gnuvince.wordpress.com
110j.wordpress.com

113
110j

J'ai bien aimé la propreté de 110j ci-dessus, alors j'ai pris l'essentiel et refactorisé pour résoudre les 3 problèmes que j'avais avec: 

  1. l'expression régulière était correspondant à l'URL 'home' contre tous autres
  2. J'avais besoin de plusieurs URL Mappées sur un seul onglet de navigation , donc, j'avais besoin d'une balise plus complexe prenant Quantité variable de paramètres.
  3. correction de quelques problèmes d'URL

C'est ici:

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

Je suis l'auteur de Django-lineage que j'ai écrit spécifiquement pour résoudre cette question: D

Je me suis énervé en utilisant la méthode jpwatts (parfaitement acceptable) dans mes propres projets et je me suis inspiré de la réponse de 110j. La lignée ressemble à ceci:

{% 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 est simplement remplacé par "actif" si l'argument correspond au début de l'URL de la page en cours. 

Les arguments variables et la résolution inverse de type {% url %} complet sont également pris en charge. J'ai semé quelques options de configuration, je l'ai étoffé et emballé pour que tout le monde puisse l'utiliser.

Si quelqu'un est intéressé, lisez un peu plus à ce sujet sur:

>> github.com/marcuswhybrow/Django-lineage

18
Marcus Whybrow

Depuis Django 1.5 :

Dans toutes les vues génériques basées sur les classes (ou dans toute vue héritée de ___ contextMixin basée sur des classes), le dictionnaire de contexte contient une variable de vue. qui pointe vers l'instance View.

Ainsi, si vous utilisez de telles vues, vous pouvez ajouter quelque chose de semblable à breadcrumbs en tant que champ de niveau de classe et l’utiliser dans vos modèles.

Exemple de code de vue:

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

Dans votre modèle, vous pouvez l'utiliser de cette manière:

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

Si vous souhaitez également "surligner" les éléments de navigation parent, vous devez étendre la liste breadcrumbs

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

... et dans votre modèle:

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

C'est une solution simple et propre qui fonctionne plutôt bien avec la navigation imbriquée.

10
Konrad Hałas

Vous pouvez appliquer une classe ou un identifiant à l'élément body de la page, plutôt qu'à un élément de navigation spécifique.

HTML:

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

CSS:

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

Je le fais comme ça:

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

et tout ce que je dois faire est à mon avis d'ajouter {'active_tab': 'statistics'} à mon dictionnaire de contexte.

Si vous utilisez RequestContext vous pouvez obtenir le chemin actuel dans votre modèle en tant que:

{{ request.path }}

Et à votre avis:

from Django.template import RequestContext

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

J'ai pris le code de nivhab ci-dessus et enlevé un peu de wierdness et en ai fait un templatetag propre, je l'ai modifié pour que/account/edit/rende toujours/account/tab actif.

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

Ceci est juste une variante de la solution CSS proposée par Toba ci-dessus:

Incluez les éléments suivants dans votre modèle de base:

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

Ensuite, dans vos modèles qui étendent l'utilisation de base:

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

Vous pouvez ensuite utiliser css pour mettre en évidence la zone actuelle en fonction de la balise body (par exemple, si nous avons un lien avec un identifiant de nav-home):

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

Merci pour vos réponses jusqu'à présent, messieurs. Je suis encore parti pour quelque chose de légèrement différent ..

Dans mon modèle:

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

Une fois que j'ai déterminé la page sur laquelle je me trouve dans la logique (généralement dans urls.py), je passe class="selected" dans le cadre du contexte, sous le bon nom pour le modèle. 

Par exemple, si je suis sur la page de lien1, je vais ajouter {'link1_active':' class="selected"'} au contexte pour que le modèle soit récupéré et injecté.

Cela semble fonctionner et c'est assez propre.

Edit: pour garder le HTML en dehors de mon contrôleur/vue, j'ai un peu modifié ceci:

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

Cela rend le modèle un peu moins lisible, mais je suis d'accord, il est préférable de ne pas utiliser le code HTML brut à partir du fichier urls.

3
Oli

Vous pouvez utiliser la fonction reverse avec les paramètres appropriés pour obtenir l’URL actuelle.

3
Corey

J'ai plusieurs menus sur la même page qui sont créés dynamiquement à travers une boucle. Les messages ci-dessus relatifs au contexte m'ont fourni une solution rapide. J'espère que ça aide quelqu'un. (J'utilise cela en plus de la balise de modèle active - mon correctif résout le problème dynamique). Cela ressemble à une comparaison idiote, mais cela fonctionne. J'ai choisi de nommer les variables active_something-unique et quelque chose-unique, afin que cela fonctionne avec des menus imbriqués.

Voici une partie de la vue (assez pour comprendre ce que je fais):

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

    }

Et cela provient du modèle:

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

Une petite amélioration par rapport à @tback , sans les balises %if%:

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

Utilisez-le dans votre modèle comme ça:

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

Et incluez "Django.core.context_processors.request" dans votre réglage TEMPLATE_CONTEXT_PROCESSORS.

2
MadeOfAir

J'ai trouvé le meilleur est d'utiliser une balise d'inclusion:

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>

Ceci est juste mon élément de base de navigation bootstrap que je souhaite rendre.

Il obtient la valeur href et éventuellement la valeur link_name. is_active est calculé en fonction de la demande en cours.

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

Puis utilisez-le dans un nav: 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>
2
Tjorriemorrie

J'ai utilisé jQuery pour mettre en évidence mes barres de navigation. Cette solution ajoute simplement la classe css "active" à l'élément qui correspond au sélecteur 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>
2
matts1

Ma solution consistait à écrire un processeur de contexte simple pour définir une variable en fonction du chemin de requête:

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}

(N'oubliez pas d'ajouter votre processeur personnalisé à TEMPLATE_CONTEXT_PROCESSORS dans settings.py.)

Ensuite, dans le modèle de base, j'utilise une balise ifequal par lien pour déterminer s'il faut ajouter la classe "active". Certes, cette approche est strictement limitée à la flexibilité de votre structure de chemin, mais elle fonctionne pour mon déploiement relativement modeste.

2
stretch

Juste un autre progrès de la solution originale.

Ceci accepte plusieurs modèles et il vaut mieux également utiliser des modèles non nommés écrits sous forme d'URL relative, entourés de "" ", comme suit:

{% 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 va comme ça:

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
xaralis

Je voulais juste partager mon amélioration mineure au poste de nivhab. Dans mon application, j'ai des sous-navigations et je ne voulais pas les cacher en utilisant uniquement du CSS, il me fallait donc une sorte de balise "if" pour afficher la sous-navigation pour un élément ou non.

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

Vous pouvez utiliser cela de la même manière que la balise active:

{% url product_url as product %}

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

En modifiant légèrement la réponse d'Andreas, il semble que vous puissiez transmettre le nom de la route d'urls.py à la balise de modèle. Dans mon exemple my_tasks, puis dans la fonction de balise de modèle, utilisez la fonction inverse pour déterminer l'URL à utiliser. Vous pouvez ensuite faire correspondre cette URL à l'URL de l'objet de requête (disponible dans le contexte du modèle)

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

Je sais que je suis en retard à la fête. Cependant, je n’ai aimé aucune des solutions populaires:

La méthode block semble fausse: je pense que la navigation devrait être autonome.

La méthode template_tag semble erronée: je n'aime pas cela, je dois d'abord obtenir l'URL du tag url. De plus, je pense que la classe css devrait être définie dans le modèle, pas dans la balise. 

J'ai donc écrit un filtre qui ne présente pas les inconvénients décrits ci-dessus. Il retourne True si une URL est active et peut donc être utilisé avec {% if %}:

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

Le code: 

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

Veillez simplement à utiliser RequestContext sur les pages avec navigation ou à activer la requête context_processor dans votre settings.py

TEMPLATE_CONTEXT_PROCESSORS = (
    ...
    'Django.core.context_processors.request',
)
1
tback

J'ai vu les réponses de jpwatts ', 110j , nivhab & Marcus Whybrow , mais elles semblent toutes manquer de quelque chose: que dire de la racine chemin ? Pourquoi est-il toujours actif?

Donc, j'ai fait un autre chemin, plus facile, qui permet au "contrôleur" de décider par lui-même et je pense que cela résout la plupart des gros problèmes.

Voici mon tag personnalisé:

## myapp_tags.py

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

Ensuite, le "contrôleur" déclare les classes CSS nécessaires (en fait, le plus important est de déclarer sa présence au modèle)

## views.py

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

Et enfin, je le rends dans ma barre de navigation:

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

Ainsi, chaque page a sa propre valeur nav_css_class à définir et, si elle est définie, le modèle est rendu actif: aucun besoin de request dans le contexte du modèle, aucune mise en page d'URL et plus de problèmes liés aux pages multi-URL ou aux pages racine.

1
DestyNova

de cette SO Question

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

Répétez autant que nécessaire pour chaque lien.

0
simple_human

voici une solution assez simple, https://github.com/hellysmile/Django-activeurl

0

Créez un modèle d'inclusion "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>

Et l'inclure dans l'élément 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>

Et vous devez ajouter ceci aux paramètres:

from Django.conf import global_settings
TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
    'Django.core.context_processors.request',
)
0
Per

Le mien est un peu similaire à une autre approche JS déjà soumise .. juste sans jQuery ...

Disons que nous avons dans base.html ce qui suit:

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

Je viens de faire ma hiérarchie pour suivre un certain modèle d'URL ... après l'adresse de l'hôte ... j'ai ma catégorie principale, par exemple, la maison, les nouvelles, l'analyse, etc. et la regex tire juste le premier mot de l'emplacement

0
Logik Sounds

Voici mon coup d'oeil. J'ai fini par implémenter une classe dans mes vues contenant la structure de navigation (à plat avec des métadonnées). Je l'injecte ensuite dans le modèle et le rende.

Ma solution concerne i18n. Cela devrait probablement être un peu plus abstrait, mais je ne m'en suis pas vraiment préoccupé.

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)

J'ai défini la logique du modèle en utilisant include comme ceci. Modèle de base:

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

Inclusion réelle (inclut/navigation.html):

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

Espérons que quelqu'un trouvera cela utile! Je suppose qu'il serait assez facile d'étendre cette idée pour prendre en charge les hiérarchies imbriquées, etc.

0
Juho Vepsäläinen

Inspiré par cette solution , j'ai commencé à utiliser cette approche:

**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
Evgeny Bobkin

J'ai également utilisé jQuery pour le mettre en évidence et le trouver plus élégant que d'encombrer le modèle avec des balises de modèle Django non sémantiques.

Le code ci-dessous fonctionne avec les listes déroulantes imbriquées dans bootstrap 3 (met en surbrillance à la fois l'élément parent et l'élément enfant <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');
    });
});

Il est également très facile d'ajouter un événement click à return false (ou de modifier l'attribut href en #) pour la page en cours, sans modifier le balisage 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;
0
user193130

J'utilise une combinaison de ce mixin pour les vues basées sur les classes:

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

avec ceci dans le modèle:

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