tutorial template linebreaksbr bootstrap django navigation

template - linebreaksbr django



NavegaciĆ³n en django (28)

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

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

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

Para comenzar, ¿cómo conseguiré la URL actual?


Al modificar levemente la respuesta de Andreas, parece que puedes pasar el nombre de la ruta de 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 determinar qué 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>


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

Mi solución trata con i18n. Probablemente debería abstraerse un poco más, pero realmente no me he molestado realmente en 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 includes como esta. Plantilla base:

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

Actual incluye (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 muy fácil extender esa idea para admitir jerarquías anidadas, etc.


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 incluirlo en el 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>

Y debes 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'', )


Desde Django 1.5 :

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

Por lo tanto, si usa estas vistas, puede agregar algo parecido a las breadcrumbs como campo de nivel de clase y usarlo en sus plantillas.

Código de vista de ejemplo:

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 extender la lista de breadcrumbs navegación:

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.


El mío es un poco similar a otro enfoque JS presentado anteriormente ... solo 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 que mi jerarquía siga cierto patrón de URL ... después de la dirección del host ... tengo mi categoría principal, por ejemplo, mi hogar, noticias, análisis, etc. y la expresión regular solo saca la primera palabra de la ubicación


Esta es solo una variante de la solución CSS propuesta por Toba arriba:

Incluye lo siguiente en tu plantilla base:

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

Luego, en sus plantillas que amplían la base, use:

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

A continuación, puede usar css para resaltar el área actual en función de 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; }


Gracias por sus respuestas hasta ahora, caballeros. He ido por algo ligeramente diferente otra vez ...

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 resuelto en qué página estoy en la lógica (generalmente en urls.py), paso class="selected" como parte del contexto con el nombre correcto a la plantilla.

Por ejemplo, si estoy en la página {''link1_active'':'' class="selected"''} al contexto de la plantilla para recoger e insertar.

Parece que funciona y está bastante limpio.

Editar: para mantener HTML fuera de mi controlador / vista, he modificado 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 avanzar a través del HTML en bruto desde el archivo de URL.


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

Así que lo hice de otra manera, más fácil, lo 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

Entonces, el "controlador" declara que las clases de CSS son necesarias (de hecho, la más importante es que declara su presencia a 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> ...

Por lo tanto, cada página tiene su propio valor nav_css_class para establecer, y si está configurada, la plantilla se representa activa: no hay necesidad de request en el contexto de la plantilla, no hay un enlace URL y no hay más problemas sobre páginas multi-URL o página raíz.


Inspirado por esta .com/a/9801473/5739875 , 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 %}


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 en mi opinión 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 su opinión:

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


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

  1. la expresión regular coincidía con la url ''home'' contra todas las demás
  2. Necesitaba varias URL asignadas a una pestaña de navegación , así que necesitaba una etiqueta más compleja que tomara una cantidad variable de parámetros
  3. arreglado 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>


Mi solución fue escribir un procesador de contexto simple para establecer una variable en función de 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, utilizo 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 tu ruta, pero funciona para mi implementación relativamente modesta.


No necesita ni si 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 detalles de implementación, eche un vistazo a:
gnuvince.wordpress.com
110j.wordpress.com


Puede aplicar una clase o id 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 */ }


Puede usar la función inversa con los parámetros apropiados para obtener la url actual.


Sé que llego tarde a la fiesta. Aunque no me gustaron las soluciones populares:

El método de bloqueo parece incorrecto: creo que la navegación debe ser independiente.

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 debe definirse en la plantilla, no en la etiqueta.

Por lo tanto, escribí un filtro que no tiene los inconvenientes que describí anteriormente. Devuelve True si 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 RequestContext en las páginas con navegación o para habilitar la solicitud context_processor en su settings.py

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


Solo otro avance de la solución original.

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

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


Solo quería compartir mi mejora menor con la publicación de nivhab. En mi aplicación tengo subnavegaciones y no quería ocultarlas solo con CSS, así que necesitaba una especie de etiqueta "if" para mostrar el 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 ""

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


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

El código siguiente funciona con menús desplegables anidados en bootstrap 3 (resalta tanto el elemento primario 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 evento de click para return false (o cambiar el atributo href a # ) para la página actual, sin cambiar la marca de la plantilla / 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;


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


Tomé el código de nivhab de arriba y eliminé algunos errores y los convertí en una plantilla limpia, la modifiqué para que / account / edit / active / account / tab active.

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


Una pequeña mejora sobre la respuesta de @tback , sin ninguna etiqueta %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 ''''

Ú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 incluya "django.core.context_processors.request" en su configuración TEMPLATE_CONTEXT_PROCESSORS .


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>


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

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>


Yo uso herencia de plantilla 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 %}



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.


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

Me enojé con el método de jpwatts (perfectamente aceptable) en mis propios proyectos y me inspiré en la respuesta de 110j. Lineage 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 con "activo" si el argumento coincide con el inicio de la URL de la página actual.

También se admiten los argumentos de variable y la resolución inversa tipo {% url %} completa. Rocié en algunas opciones de configuración y lo cargué un poco y lo preparé para que todos lo usen.

Si alguien está interesado, lea un poco más al respecto en:

>> django-lineage