Navigazione in django


104

Ho appena fatto la mia prima piccola webapp in Django e la adoro. Sto per iniziare a convertire un vecchio sito PHP di produzione in django e come parte del suo modello, c'è una barra di navigazione.

In PHP, controllo l'URL di ciascuna opzione di navigazione rispetto all'URL corrente, nel codice del modello e applico una classe CSS se si allineano. È orrendamente disordinato.

C'è qualcosa di meglio per django o un buon modo di gestire il codice nel template?

Per iniziare, come dovrei fare per ottenere l'URL corrente?


Ho creato github.com/orokusaki/django-active-menu per questo: supporta strutture URL nidificate e si basa sulla configurazione rispetto alle convenzioni (per quanto diabolico possa sembrare), quindi puoi definire la gerarchia del tuo sito come preferisci. Devi solo usare <a href="{% url "view:name" %}" {% active_class "view:name" %}>. Puoi opzionalmente usarlo per generare solo il " active"valore (passando Falsecome secondo argomento al tag) da aggiungere a un attributo di classe esistente, ma per la maggior parte dei collegamenti di navigazione questo esempio è quello che uso.
orokusaki

Questa domanda sembra essere correlato a questo stackoverflow.com/a/9801473/5739875
Evgeny Bobkin

Forse questa griglia aiuta: djangopackages.org/grids/g/navigation
guettli

Risposte:


74

Uso l'ereditarietà dei modelli per personalizzare la navigazione. Per esempio:

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

Mi piace molto questa idea, soprattutto per la flessibilità, ma ha un compromesso meno ASCIUTTO. Tuttavia, ho iniziato a usarlo in un sito.
codardo anonimo

23
Non sono entusiasta di questo approccio perché non è raro avere più sezioni del sito gestite dallo stesso sotto-modello. Quindi finisci per inserire variabili personalizzate nelle viste e condizionali nei modelli, o riorganizzare i sotto-modelli in modo che siano tutti unici ... tutto solo per rilevare la sezione del sito corrente. L'approccio del tag modello finisce per essere più pulito alla fine.
shacker

Ho esaminato alcune altre soluzioni e sembra che siano tutte un po 'un trucco. Questo, almeno, è piuttosto semplice e semplice da implementare / rottamare.
mlissner

Ho riformattato il <ul id="nav">....</ul>in un file diverso, diciamo tabs.html. Quindi ora base.html contenuto {%block nav%}{%include "tabs.html"%}{%endblock%}e quindi l'evidenziazione della scheda attiva ha smesso di funzionare (in about.html sopra). Mi manca qualcosa?
Nessuno da

@Maddy Hai abbastanza riferimenti indiretti che non sono assolutamente certo di tenerlo dritto nella mia testa, ma penso che la risposta abbia a che fare con il modo in cui funziona il includetag. Controlla la nota inclusa nei documenti: docs.djangoproject.com/en/dev/ref/templates/builtins/#include Nel tuo caso, nel momento in cui stai cercando di sovrascrivere il modello di base in about.html, penso che tu abbia ha già un blocco HTML renderizzato, piuttosto che un blocco modello Django in attesa di essere elaborato.
jpwatt

117

Non hai bisogno di un if per farlo, dai un'occhiata al seguente codice:

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>

questo è tutto. per i dettagli sull'implementazione dare un'occhiata a:
gnuvince.wordpress.com
110j.wordpress.com


2
Nelle proprietà dell'href mancano le parentesi del modello django {{,}}. Ad esempio, <a class="{% active request home %}" href="home"> Home </a> dovrebbe essere <a class = "{% active request home%}" href = "{{home} } "> Home </a> il file tags.py richiederà anche alcuni include. Altrimenti, ottima soluzione!
bsk

2
+1 Questo è più liberamente accoppiato dalle applicazioni. Da principiante ho capito che i tag hanno bisogno della sua app, non puoi semplicemente scaricarla in un file tags.py globale. Ho creato una nuova app chiamata tag e tutto è filato liscio. docs.djangoproject.com/en/dev/howto/custom-template-tags
Keyo

3
@Keyo, crea una directory templatetags nel tuo progetto e aggiungi il tuo progetto a installedapps. Anche questo farà il trucco. In alternativa, come hai detto, crea il tuo sito principale come un'app all'interno del tuo progetto.
Josh Smeaton

5
Non dimenticare di aggiungere django.core.context_processors.requestal tuo TEMPLATE_CONTEXT_PROCESSORSinsettings.py
amigcamel

1
Ciò non è valido per gli stati che possono essere annidati, ad esempio mysite.com(come home) e mysite.com/blog, poiché il percorso mostrerà /e /blog/(rispettivamente) restituendo ogni volta una corrispondenza per il primo. Se non lo usi /come atterraggio, potrebbe andare bene, altrimenti lo uso solo return 'active' if pattern == request.path else ''(non ho ancora riscontrato problemi con questo, ma ho appena impostato usando questo).
nerdwaller

33

Mi è piaciuta la pulizia di 110j sopra, quindi ho preso la maggior parte e ho rifatturato per risolvere i 3 problemi che ho avuto con esso:

  1. l'espressione regolare corrispondeva all'URL "home" rispetto a tutti gli altri
  2. Avevo bisogno di più URL mappati su una scheda di navigazione , quindi avevo bisogno di un tag più complesso che accetta una quantità variabile di parametri
  3. risolti alcuni problemi con l'URL

Ecco qui:

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>

Forse è meglio rispondere con Marcus, ma come funziona con la "casa"? è sempre attivo? Come renderlo attivo solo alla chiamata dell'URL di root (www.toto.com/ e www.toto.com/index)? Entrambe le risposte non provocano questo problema ...
DestyNova

20

Sono l'autore di django-lignaggio che ho scritto appositamente per risolvere questa domanda: D

Mi sono infastidito usando il metodo jpwatts (perfettamente accettabile) nei miei progetti e ho tratto ispirazione dalla risposta di 110j. Il lignaggio ha questo aspetto:

{% 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 viene semplicemente sostituito con "attivo" se l'argomento corrisponde all'inizio dell'URL della pagina corrente.

{% url %}Sono supportati anche argomenti variabili e risoluzione inversa di tipo completo . Ho aggiunto alcune opzioni di configurazione e l'ho arricchito un po 'e l'ho confezionato per essere utilizzato da tutti.

Se qualcuno è interessato, leggi qualcosa di più su:

>> github.com/marcuswhybrow/django-lineage


1
percorsi hard coding nel modello :(
CpILL

10

A partire da Django 1.5 :

In tutte le viste generiche basate su classi (o qualsiasi vista basata su classi che eredita da ContextMixin), il dizionario di contesto contiene una variabile di visualizzazione che punta all'istanza View.

Quindi, se stai usando tali viste, potresti aggiungere qualcosa di simile breadcrumbscome campo a livello di classe e usarlo nei tuoi modelli.

Codice di visualizzazione di esempio:

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

Nel tuo modello puoi usarlo in questo modo:

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

Se desideri "evidenziare" ulteriormente gli elementi di navigazione principale, devi estendere l' breadcrumbselenco:

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

... e nel tuo modello:

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

Questa è una soluzione semplice e pulita e funziona abbastanza bene con la navigazione annidata.


In quell'esempio, non lo sarebbero tutti e tre gli elementi di navigazione .active?
Oli

Sì, ma in genere questo è ciò che desideri ottenere con la navigazione a più livelli. Ovviamente potresti mettere un oggetto breadcrumbsse vuoi. Ma hai ragione: il mio esempio non è il migliore.
Konrad Hałas,

@Oli ha migliorato l'esempio.
Konrad Hałas,

9

È possibile applicare una classe o un ID all'elemento del corpo della pagina, anziché a un elemento di navigazione specifico.

HTML:

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

CSS:

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

8

Lo faccio così:

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

e poi tutto quello che devo fare è, a mio avviso, aggiungere {'active_tab': 'statistics'}al mio dizionario contestuale.

Se stai usando RequestContextpuoi ottenere il percorso corrente nel tuo modello come:

{{ request.path }}

E secondo te:

from django.template import RequestContext

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

Grazie per aver condiviso queste informazioni. Ho usato questo metodo, ma avevo anche una pagina piatta nella mia barra di navigazione, quindi per rilevarla ed evidenziarla correttamente, ho usato {% ifequal flatpage.url '/ about /'%}. Non mi piace il rilevamento hardcoded dell'URL, ma funziona per un hack una tantum.
Matt Garrison

Il problema con questa soluzione è che hai "statistiche" hardcoded nel codice. Ciò vanifica lo scopo di utilizzare il tag url per ottenere l'URL della pagina.
justin

7

Ho preso il codice da nivhab sopra e ho rimosso alcune stranezze e l'ho trasformato in un templatetag pulito, modificato in modo che / account / edit / renda ancora / account / tab attivo.

#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

Questa è solo una variante della soluzione css proposta da Toba sopra:

Includere quanto segue nel modello di base:

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

Quindi nei tuoi modelli che estendono l'uso di base:

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

È quindi possibile utilizzare css per evidenziare l'area corrente in base al tag body (ad esempio se abbiamo un collegamento con un id nav-home):

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


3

Grazie per le vostre risposte finora, signori. Ho scelto di nuovo qualcosa di leggermente diverso ..

Nel mio modello:

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

Una volta che ho capito in quale pagina mi trovo nella logica (di solito in urls.py), passo class="selected"come parte del contesto sotto il nome giusto al modello.

Ad esempio, se sono sulla pagina link1, aggiungerò {'link1_active':' class="selected"'}al contesto per il modello da raccogliere e iniettare.

Sembra funzionare ed è abbastanza pulito.

Modifica: per mantenere l'HTML fuori dal mio controller / vista, l'ho modificato un po ':

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

Rende il modello un po 'meno leggibile, ma sono d'accordo, è meglio non inserire HTML grezzo dal file urls.


2
Dovresti davvero evitare di gestire HTML grezzo nella tua vista, che è ciò che richiede questa tecnica. Hai pensato di scrivere un tag modello personalizzato?
Justin Voss

Hai ragione. Ho modificato per interrompere il passaggio attraverso l'HTML. Adesso sono passato per True. Non ho ancora scritto alcun tag di modello, ma sì, questo potrebbe essere un buon punto di partenza.
Oli

2

Ho più menu sulla stessa pagina che vengono creati dinamicamente attraverso un ciclo. I post sopra relativi al contesto mi hanno dato una soluzione rapida. Spero che questo aiuti qualcuno. (Lo uso in aggiunta al tag del modello attivo: la mia correzione risolve il problema dinamico). Sembra uno sciocco confronto, ma funziona. Ho scelto di denominare le variabili active_something-unique e something-unique, in questo modo funziona con i menu annidati.

Ecco una parte della visualizzazione (sufficiente per capire cosa sto facendo):

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

    }

E questo è dal modello:

<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

La mia soluzione era scrivere un semplice processore di contesto per impostare una variabile in base al percorso della richiesta:

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}

(Non dimenticare di aggiungere il tuo processore personalizzato a TEMPLATE_CONTEXT_PROCESSORS in settings.py.)

Quindi nel modello di base utilizzo un tag ifequal per collegamento per determinare se aggiungere la classe "attiva". È vero che questo approccio è strettamente limitato alla flessibilità della struttura del percorso, ma funziona per la mia distribuzione relativamente modesta.


Penso che abbia davvero senso averli nel contesto globale, quindi puoi fare riferimento alla sezione del sito in vari modi (utilizzando diversi modelli per diverse sezioni del sito, ad esempio. +1.
shacker

2

Volevo solo condividere il mio miglioramento minore al post di Nivhab. Nella mia applicazione ho le subnavigazioni e non volevo nasconderle usando solo CSS, quindi avevo bisogno di una sorta di tag "if" per visualizzare o meno la subnavigazione per un elemento.

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

Puoi usarlo fondamentalmente allo stesso modo del tag attivo:

{% url product_url as product %}

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

2

Solo un altro miglioramento della soluzione originale.

Questo accetta più pattern e che è meglio anche pattern senza nome scritti come URL relativo racchiuso tra "" ", come segue:

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

Il tag va così:

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

Ho usato jquery per evidenziare i miei navbar. Questa soluzione aggiunge semplicemente la classe css "attiva" all'elemento che si adatta al selettore 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

Un piccolo miglioramento rispetto alla risposta di @tback , senza %if%tag:

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

Usalo nel tuo modello in questo modo:

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

E includi "django.core.context_processors.request"nel tuo TEMPLATE_CONTEXT_PROCESSORSambiente.


2

Ho scoperto che la cosa migliore è usare un tag di inclusione:

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>

Questo è solo il mio elemento di navigazione bootstrap di base che desidero rendere.

Ottiene il valore href e, facoltativamente, il valore link_name. is_activeviene calcolato in base alla richiesta corrente.

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

Quindi usalo in un navigatore: 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>

Solo una lettura superficiale, ma questo non limita le cose alle corrispondenze esatte sull'URL? Uso comunemente suggerimenti di navigazione come questo anche per le pagine profonde. Ad esempio, l'elemento Informazioni di navigazione sarebbe evidenziato se tu fossi in uno /about/company-history/o /about/what-we-do/
Oli

1
Sì, ma is_activepossono essere sostituite e altre chiavi aggiunte al dizionario restituite. Inoltre, il controllo può essere context.request.resolver_match.url_name.startswith(x)o qualsiasi altra cosa. Inoltre, è possibile disporre del codice prima dell'istruzione return per stabilire i valori dict. Inoltre, puoi utilizzare modelli diversi, ad esempio uno top_level_nav.htmlcon una logica diversa, ecc.
Tjorriemorrie

Soluzione semplice e pulita ... bella!
mmw

1

Modificando leggermente la risposta di Andreas, sembra che tu possa passare il nome del percorso da urls.py al tag del modello. Nel mio esempio my_tasks, e poi nella funzione tag del modello usa la funzione inversa per capire quale dovrebbe essere l'URL, quindi puoi abbinarlo all'URL nell'oggetto richiesta (disponibile nel contesto del modello)

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>

Forse, un approccio più diretto: turnkeylinux.org/blog/django-navbar
jgsogo

1

So di essere in ritardo alla festa. Non mi è piaciuta nessuna delle soluzioni popolari però:

Il metodo di blocco sembra sbagliato: penso che la navigazione dovrebbe essere autonoma.

Il metodo template_tag sembra sbagliato: non mi piace che devo prima ottenere l'URL dal tag url. Inoltre, penso che la classe css dovrebbe essere definita nel modello, non nel tag.

Ho quindi scritto un filtro che non presenta gli inconvenienti che ho descritto sopra. Restituisce Truese un url è attivo e può quindi essere utilizzato con {% if %}:

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

Il codice:

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

Assicurati solo di utilizzare RequestContextnelle pagine con navigazione o di abilitare la richiesta context_processor nel tuo filesettings.py

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

1

Ho visto le risposte di jpwatt , 110j , nivhab e Marcus Whybrow , ma sembra che a tutte manchi qualcosa: che dire del percorso di root? Perché è sempre attivo?

Quindi ho creato un altro modo, più semplice, che fa sì che il "controller" decida da solo e penso che risolva la maggior parte dei grandi problemi.

Ecco il mio tag personalizzato:

## myapp_tags.py

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

Quindi, il "controller" dichiara le classi CSS necessarie (infatti, la cosa più importante è che dichiara la sua presenza al template)

## views.py

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

E infine, lo visualizzo nella mia barra di navigazione:

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

Quindi ogni pagina ha il suo nav_css_classvalore da impostare e, se è impostato, il modello diventa attivo: non c'è bisogno di un requestcontesto di modello, nessun parsing dell'URL e niente più problemi con le pagine multi-URL o la pagina principale.


1

Ispirato da questa soluzione , ho iniziato a utilizzare questo approccio:

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

Ecco il mio tentativo. Ho finito per implementare una classe nelle mie viste che contiene la mia struttura di navigazione (piatta con alcuni metadati). Quindi lo inserisco nel modello e lo visualizzo.

La mia soluzione riguarda i18n. Probabilmente dovrebbe essere astratto un po 'di più, ma non me ne sono davvero preoccupato.

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)

Ho definito la logica del modello utilizzando include come questo. Modello di base:

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

Include effettivo (includes / navigation.html):

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

Si spera che qualcuno lo trovi utile! Immagino che sarebbe abbastanza facile estendere l'idea per supportare gerarchie annidate ecc.


0

Crea un modello di inclusione "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>

E includilo nell'elemento 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>

E devi aggiungerlo alle impostazioni:

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


0

da questa domanda SO

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

Ripeti se necessario per ogni collegamento.


Funziona solo per le partite dirette. La maggior parte dei sistemi di navigazione contrassegna l'elemento di navigazione come attivo se è attiva anche una pagina discendente. Cioè se /blog/posts/2021/04/12fosse l'URL l'elemento / blog / nav sarebbe attivo.
Oli

@Oli sì, alcune volte non funzionerà. per esempio in StackOverflow cioè di navigazione Questions, Tags, Users, Badges, Unanswered, Ask Question. non funzionerà per Questions, ma per tutti gli altri nav funzionerà bene.
suhailvs

0

Ho anche usato jQuery per evidenziarlo e trovarlo più elegante che ingombrare il template con tag template Django non semantici.

Il codice seguente funziona con elenchi a discesa nidificati in bootstrap 3 (evidenzia sia l' <li>elemento padre che l' elemento figlio .

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

È anche abbastanza facile aggiungere un clickevento return false(o modificare l' hrefattributo in #) per la pagina corrente, senza modificare il markup 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

Uso una combinazione di questo mixin per le visualizzazioni basate sulle classi:

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

con questo nel modello:

<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

Il mio è un po 'simile a un altro approccio JS presentato in precedenza .. solo senza jQuery ...

Supponiamo di avere in base.html quanto segue:

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

Ho appena creato la mia gerarchia per seguire un certo pattern URL ... dopo l'indirizzo host ... ho la mia categoria principale, ad es. Casa, notizie, analisi, ecc. E la regex estrae la prima parola dalla posizione

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.