Navegación en django

104

Acabo de hacer mi primera pequeña aplicación web en django y me encanta. Estoy a punto de comenzar a convertir un antiguo sitio PHP de producción en django y, como parte de su plantilla, hay una barra de navegación.

En PHP, verifico la URL de cada opción de navegación con la URL actual, en el código de la plantilla y aplico una clase CSS si se alinean. Es terriblemente desordenado.

¿Hay algo mejor para django o una buena forma de manejar el código en la plantilla?

Para empezar, ¿cómo puedo conseguir la URL actual?

Oli
fuente
He creado github.com/orokusaki/django-active-menu para esto - es compatible con las estructuras de URL anidadas, y se basa en la configuración durante la convención (como el mal como suena), por lo que puede definir la jerarquía del sitio que le apetezca. Solo usa <a href="{% url "view:name" %}" {% active_class "view:name" %}>. Opcionalmente, puede usarlo para generar solo el " active"valor (pasando Falsecomo un segundo argumento a la etiqueta) para agregarlo a un atributo de clase existente, pero para la mayoría de los enlaces de navegación, ese ejemplo es el que uso.
orokusaki
Esta pregunta parece estar relacionada con esta stackoverflow.com/a/9801473/5739875
Evgeny Bobkin
Quizás esta cuadrícula ayude: djangopackages.org/grids/g/navigation
guettli

Respuestas:

74

Utilizo la herencia de plantillas para personalizar la navegación. Por ejemplo:

base.html

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

about.html

{% extends "base.html" %}

{% block nav-about %}<strong class="nav-active">About</strong>{% endblock %}
jpwatts
fuente
Me gusta mucho esta idea, especialmente por su flexibilidad, pero viene con la compensación de menos SECO. Sin embargo, comencé a usar esto en un sitio.
cobarde anónimo
22
No estoy entusiasmado con este enfoque porque no es raro tener varias secciones del sitio manejadas por la misma sub-plantilla. Así que terminas colocando vars personalizadas en vistas y condicionales en plantillas, o reorganizando sub-plantillas para que todas sean únicas ... todo solo para detectar la sección actual del sitio. El enfoque de la etiqueta de plantilla termina siendo más limpio al final.
shacker
Miré algunas otras soluciones y parece que todas son un truco. Este, al menos, es bastante sencillo y simple de implementar / desechar.
mlissner
Refactoricé el <ul id="nav">....</ul>archivo a un archivo diferente, digamos tabs.html. Así que ahora base.html contenido {%block nav%}{%include "tabs.html"%}{%endblock%}y luego resaltado de la pestaña activa dejó de funcionar (en about.html arriba). ¿Me estoy perdiendo algo?
Ninguno-da
@Maddy Tienes suficientes indirectas en marcha que no estoy absolutamente seguro de mantenerlo claro en mi cabeza, pero creo que la respuesta tiene que ver con cómo funciona la includeetiqueta. Consulte la nota incluida en los documentos: docs.djangoproject.com/en/dev/ref/templates/builtins/#include En su caso, para cuando intente anular la plantilla base en about.html, creo que ya ha ya tiene un bloque HTML renderizado, en lugar de un bloque de plantilla de Django esperando ser procesado.
jpwatts
117

No necesita un si para hacer eso, eche un vistazo al siguiente código:

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>

Eso es. para obtener detalles sobre la implementación, consulte:
gnuvince.wordpress.com
110j.wordpress.com

pradyunsg
fuente
2
A las propiedades de href les faltan los corchetes de plantilla de django {{,}}. Por ejemplo, <a class="{% active request home %}" href="home"> Home </a> debería ser <a class = "{% active request home%}" href = "{{home} } "> Inicio </a>, el archivo tags.py también necesitará algunas inclusiones. De lo contrario, ¡gran solución!
bsk
2
+1 Esto está más débilmente acoplado a las aplicaciones. Como principiante, descubrí que las etiquetas necesitan su propia aplicación, no puedes simplemente volcar eso en un archivo tags.py global. Creé una nueva aplicación llamada etiquetas y todo salió bien. docs.djangoproject.com/en/dev/howto/custom-template-tags
Keyo
3
@Keyo, cree un directorio templatetags en su proyecto y agregue su proyecto a installedapps. Eso también funcionará. Alternativamente, como dijiste, crea tu sitio principal como una aplicación dentro de tu proyecto.
Josh Smeaton
5
No olvides agregar django.core.context_processors.requesta tu TEMPLATE_CONTEXT_PROCESSORSinsettings.py
amigcamel
1
Esto no es válido para estados que pueden estar anidados, p. Ej. mysite.com(Como inicio) y mysite.com/blog, ya que la ruta se mostrará como /y /blog/(respectivamente) dando una coincidencia para el primero cada vez. Si no lo usa /como aterrizaje, puede estar bien, de lo contrario, solo lo uso return 'active' if pattern == request.path else ''(todavía no he visto problemas con esto, pero solo configuré el uso de esto).
nerdwaller
33

Me gustó la limpieza de 110j anterior, así que tomé la mayor parte y lo refactoricé para resolver los 3 problemas que tuve con él:

  1. la expresión regular coincidía con la URL de inicio con todas las demás
  2. Necesitaba varias URL asignadas a una pestaña de navegación , por lo que necesitaba una etiqueta más compleja que toma una cantidad variable de parámetros
  3. solucionó algunos problemas de URL

Aquí está:

tags.py:

from django import template

register = template.Library()

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

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

urls.py:

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

base.html:

{% load tags %}

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

<div id="navigation">
    <a class="{% active request home %}" href="home">Home</a>
    <a class="{% active request services %}" href="services">Services</a>
    <a class="{% active request contact contact2 %}" href="contact">Contact</a>
</div>
nivhab
fuente
Quizás sea mejor responder con Marcus uno, pero ¿cómo funciona con el "hogar"? siempre esta activo? ¿Cómo activarlo solo en la llamada de URL raíz (www.toto.com/ y www.toto.com/index)? Ambas respuestas no dan como resultado este problema ...
DestyNova
20

Soy el autor de django-lineage que escribí específicamente para resolver esta pregunta: D

Me molestó usar el método jpwatts (perfectamente aceptable) en mis propios proyectos y me inspiré en la respuesta de 110j. El linaje se ve así:

{% 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 simplemente se reemplaza por "activo" si el argumento coincide con el inicio de la URL de la página actual.

{% url %}También se admiten argumentos variables y resolución inversa de tipo completo . Agregué algunas opciones de configuración, lo desarrollé un poco y lo empaqueté para que todos lo usaran.

Si alguien está interesado, lea un poco más sobre esto en:

>> github.com/marcuswhybrow/django-lineage

Marcus Whybrow
fuente
1
rutas de codificación rígidas en la plantilla :(
CpILL
10

Desde Django 1.5 :

En todas las vistas genéricas basadas en clases (o en cualquier vista basada en clases heredada de ContextMixin), el diccionario de contexto contiene una variable de vista que apunta a la instancia de View.

Entonces, si está usando tales vistas, puede agregar algo breadcrumbscomo un campo de nivel de clase y usarlo en sus plantillas.

Ejemplo de código de vista:

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

En su plantilla, puede usarlo de esta manera:

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

Si desea "resaltar" adicionalmente los elementos de navegación principales, debe ampliar la breadcrumbslista:

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

... y en tu plantilla:

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

Esta es una solución fácil y limpia y funciona bastante bien con la navegación anidada.

Konrad Hałas
fuente
En ese ejemplo, ¿no serían los tres elementos de navegación .active?
Oli
Sí, pero esto es normalmente lo que desea lograr con la navegación de varios niveles. Por supuesto, puede poner un elemento breadcrumbssi lo desea. Pero tienes razón: mi ejemplo no es el mejor.
Konrad Hałas
@Oli ejemplo mejorado.
Konrad Hałas
9

Puede aplicar una clase o identificación al elemento del cuerpo de la página, en lugar de a un elemento de navegación específico.

HTML:

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

CSS:

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

Lo hago así:

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

y luego todo lo que tengo que hacer es agregar {'active_tab': 'statistics'}a mi diccionario de contexto.

Si está utilizando RequestContext, puede obtener la ruta actual en su plantilla como:

{{ request.path }}

Y en tu opinión:

from django.template import RequestContext

def my_view(request):
    # do something awesome here
    return template.render(RequestContext(request, context_dict))
muhuk
fuente
Gracias por compartir esta información. Usé este método, pero también tenía una página plana en mi barra de navegación, así que para detectarlo y resaltarlo correctamente, usé {% ifequal flatpage.url '/ about /'%}. No me gusta la detección codificada de la URL, pero funciona para un truco único.
Matt Garrison
El problema con esta solución es que tiene "estadísticas" codificadas en el código. Esto anula el propósito de usar la etiqueta url para obtener la URL de la página.
Justin
7

Tomé el código de nivhab anterior y eliminé algunas rarezas y lo convertí en una etiqueta de plantilla limpia, la modifiqué para que / account / edit / aún active / account / tab.

#current_nav.py
from django import template

register = template.Library()

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

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

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



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

Esta es solo una variante de la solución css propuesta por Toba anteriormente:

Incluya lo siguiente en su plantilla base:

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

Luego, en tus plantillas que extienden el uso base:

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

Luego puede usar css para resaltar el área actual según la etiqueta del cuerpo (por ejemplo, si tenemos un enlace con una identificación de nav-home):

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

Gracias por sus respuestas hasta ahora, caballeros. He optado por algo ligeramente diferente de nuevo ...

En mi plantilla:

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

Una vez que he descubierto en qué página estoy en la lógica (generalmente en urls.py), paso class="selected"como parte del contexto bajo el nombre correcto a la plantilla.

Por ejemplo, si estoy en la página link1, agregaré {'link1_active':' class="selected"'}el contexto para que la plantilla se recupere e inyecte.

Parece funcionar y está bastante limpio.

Editar: para mantener HTML fuera de mi controlador / vista, modifiqué esto un poco:

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

Hace que la plantilla sea un poco menos legible, pero estoy de acuerdo, es mejor no presionar HTML sin procesar desde el archivo de URL.

Oli
fuente
2
Realmente debería evitar manejar HTML sin formato en su vista, que es lo que requiere esta técnica. ¿Ha pensado en escribir una etiqueta de plantilla personalizada?
Justin Voss
Tienes razón. He editado para dejar de pasar por el HTML. Acabo de pasar por True ahora. Todavía no he escrito ninguna etiqueta de plantilla, pero sí, este podría ser un buen lugar para comenzar.
Oli
2

Tengo varios menús en la misma página que se crean dinámicamente a través de un bucle. Las publicaciones anteriores relacionadas con el contexto me dieron una solución rápida. Espero que esto ayude a alguien. (Utilizo esto además de la etiqueta de plantilla activa; mi solución resuelve el problema dinámico). Parece una comparación tonta, pero funciona. Elegí nombrar las variables active_something-unique y something-unique, de esta manera funciona con menús anidados.

Aquí hay una parte de la vista (suficiente para entender lo que estoy haciendo):

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

    }

Y esto es de la plantilla:

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

Mi solución fue escribir un procesador de contexto simple para establecer una variable basada en la ruta de solicitud:

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}

(No olvide agregar su procesador personalizado a TEMPLATE_CONTEXT_PROCESSORS en settings.py).

Luego, en la plantilla base, uso una etiqueta ifequal por enlace para determinar si agregar la clase "activa". De acuerdo, este enfoque está estrictamente limitado a la flexibilidad de la estructura de su ruta, pero funciona para mi implementación relativamente modesta.


fuente
Creo que realmente tiene sentido
tenerlos
2

Solo quería compartir mi pequeña mejora en la publicación de nivhab. En mi aplicación tengo subnavegaciones y no quería ocultarlas usando solo CSS, así que necesitaba algún tipo de etiqueta "if" para mostrar la subnavegación de un elemento o no.

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

Puede usar esto básicamente de la misma manera que la etiqueta activa:

{% url product_url as product %}

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

Solo otra mejora de la solución original.

Esto acepta múltiples patrones y lo mejor es también patrones sin nombre escritos como URL relativa envuelta en '"', como sigue:

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

La etiqueta dice así:

from django import template

register = template.Library()

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

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

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

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

            if (cpath == '/' or cpath == '') and not (path == '/' or path == ''):
                return ""
            if path.startswith(cpath):
                return 'active'
        return ""
xaralis
fuente
2

Usé jquery para resaltar mis barras de navegación. Esta solución simplemente agrega la clase css "activa" al elemento que se ajusta al selector css.

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

Una pequeña mejora sobre la respuesta de @tback , sin %if%etiquetas:

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

Úselo en su plantilla así:

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

E incluir "django.core.context_processors.request"en su TEMPLATE_CONTEXT_PROCESSORSentorno.

MadeOfAir
fuente
2

Encontré que lo mejor es usar una etiqueta de inclusión:

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>

Este es solo mi elemento básico de navegación bootstrap que deseo representar.

Obtiene el valor href y, opcionalmente, el valor link_name. is_activese calcula en función de la solicitud actual.

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

Luego úsalo en un navegador: templates/fnf/nav.html

{% load nav %}
<nav class="navbar navbar-expand-lg navbar-light bg-light">
        <ul class="navbar-nav mr-auto">
                {% nav_item 'dashboard' %}
            </ul>
Tjorriemorrie
fuente
Solo una lectura superficial, pero ¿esto no limita las cosas a coincidencias exactas en la URL? También utilizo comúnmente sugerencias de navegación como esta para páginas profundas. Por ejemplo, el elemento Acerca de la navegación se resaltaría si estuviera en /about/company-history/o /about/what-we-do/
Oli
1
Sí, pero is_activese pueden reemplazar y se pueden devolver otras claves agregadas al diccionario. Además, el cheque puede ser context.request.resolver_match.url_name.startswith(x)o cualquier otra cosa. Además, puede tener código antes de la declaración de retorno para establecer los valores de dict. Además, puede usar diferentes plantillas, es decir, una top_level_nav.htmlcon lógica diferente, etc.
Tjorriemorrie
Solución limpia y sencilla ... ¡agradable!
mmw
1

Modificando ligeramente la respuesta de Andreas, parece que puede pasar el nombre de la ruta desde urls.py a la etiqueta de la plantilla. En mi ejemplo my_tasks, y luego, en la función de etiqueta de plantilla, use la función inversa para averiguar cuál debería ser la URL, luego puede compararla con la URL en el objeto de solicitud (disponible en el contexto de la plantilla)

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

register = template.Library()

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

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

    def render(self, context):

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

urls.py

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

template.html

<li class="{% active request all_tasks %}"><a href="{% url all_tasks %}">Everyone</a></li>
errkk
fuente
Quizás, un enfoque más sencillo: turnkeylinux.org/blog/django-navbar
jgsogo
1

Sé que llego tarde a la fiesta. Sin embargo, no me gustó ninguna de las soluciones populares:

El método de bloqueo parece incorrecto: creo que la navegación debería ser autónoma.

El método template_tag parece incorrecto: no me gusta que primero tenga que obtener la URL de la etiqueta url. Además, creo que la clase css debería definirse en la plantilla, no en la etiqueta.

Por lo tanto, escribí un filtro que no tiene los inconvenientes que describí anteriormente. Devuelve Truesi una URL está activa y, por lo tanto, se puede usar con {% if %}:

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

El código:

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

Solo asegúrese de usar RequestContexten páginas con navegación o de habilitar la solicitud context_processor en susettings.py

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

He visto las respuestas de jpwatts ', 110j 's, nivhab ' s y Marcus Whybrow , pero todas parecen carecer de algo: ¿qué pasa con la ruta raíz? ¿Por qué siempre está activo?

Así que hice otra manera, más fácil, que hace que el "controlador" decida por sí mismo y creo que resuelve la mayoría de los grandes problemas.

Aquí está mi etiqueta personalizada:

## myapp_tags.py

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

Luego, el "controlador" declara las clases CSS necesarias (de hecho, lo más importante es que declara su presencia en la plantilla)

## views.py

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

Y finalmente, lo renderizo en mi barra de navegación:

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

Entonces, cada página tiene su propio nav_css_classvalor para configurar, y si está configurado, la plantilla se vuelve activa: no es necesario requesten el contexto de la plantilla, no hay parcelación de URL y no más problemas con las páginas de múltiples URL o la página raíz.

DestyNova
fuente
1

Inspirado por esta solución , comencé a usar este enfoque:

**Placed in templates as base.html**

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

**Placed in your page template**

{% extends "base.html" %}

{% block tab_menu %}
  {% with active_tab="tab1" %} {{ block.super }} {% endwith %}
{% endblock tab_menu %}
Evgeny Bobkin
fuente
0

Aquí está mi camino. Terminé implementando una clase en mis vistas que contiene mi estructura de navegación (plana con algunos metadatos). Luego inyecto esto en la plantilla y lo renderizo.

Mi solución trata con i18n. Probablemente debería resumirse un poco más, pero realmente no me he molestado con eso.

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)

Definí la lógica de la plantilla usando incluye como este. Plantilla base:

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

Incluido real (incluye / navigation.html):

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

¡Ojalá alguien lo encuentre útil! Supongo que sería bastante fácil extender esa idea para admitir jerarquías anidadas, etc.

Juho Vepsäläinen
fuente
0

Cree una plantilla de inclusión "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 inclúyelo en el elemento de navegación:

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

Y necesitas agregar esto a la configuración:

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

aquí hay una solución bastante simple, https://github.com/hellysmile/django-activeurl

Віктор Ковтун
fuente
1
Tenga en cuenta que debe publicar los puntos útiles de una respuesta aquí, en este sitio, o su publicación corre el riesgo de ser eliminada como "No es una respuesta" . Aún puede incluir el enlace si lo desea, pero solo como una "referencia". La respuesta debe mantenerse por sí sola sin necesidad del enlace.
Andrew Barber
0

de esta pregunta SO

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

Repita según sea necesario para cada enlace.

suhailvs
fuente
Esto solo funciona para coincidencias directas. La mayoría de los sistemas de navegación marcan el elemento de navegación como activo si también lo está una página descendiente. Es decir, si /blog/posts/2021/04/12fuera la URL, el elemento / blog / nav estaría activo.
Oli
@Oli sí, algunas veces no funcionará. por ejemplo en la navegación es decir stackoverflow Questions, Tags, Users, Badges, Unanswered, Ask Question. no funcionará Questions, pero para todas las demás navegaciones funcionará bien.
suhailvs
0

También utilicé jQuery para resaltarlo y lo encontré más elegante que abarrotar la plantilla con etiquetas de plantilla Django no semánticas.

El siguiente código funciona con menús desplegables anidados en bootstrap 3 (destaca tanto el elemento principal como el secundario <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');
    });
});

También es bastante fácil agregar un clickevento a return false(o cambiar el hrefatributo a #) para la página actual, sin cambiar la plantilla / marcado html:

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

Utilizo una combinación de este mixin para vistas basadas en clases:

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 esto en la plantilla:

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

El mío es un poco similar a otro enfoque JS presentado anteriormente ... solo que sin jQuery ...

Digamos que tenemos en base.html lo siguiente:

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

Acabo de hacer mi jerarquía para seguir un cierto patrón de URL ... después de la dirección de host ... tengo mi categoría principal, por ejemplo, casa, noticias, análisis, etc. y la expresión regular solo saca la primera palabra de la ubicación

Sonidos Logik
fuente