在Django导航

我刚刚在django做了我的第一个小webapp,我喜欢它。 我即将开始将旧生产PHP站点转换为Django,作为其模板的一部分,有一个导航栏。

在PHP中,我检查每个导航选项的URL与模板代码中的当前URL,并应用一个CSS类,如果他们排队。 这是可怕的凌乱。

有没有更好的Django或在模板中处理代码的好方法?

要开始,我将如何去获取当前的url?

我使用模板inheritance来定制导航。 例如:

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

你不需要这样做,看看下面的代码:

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> 

而已。 有关实现细节请看:
gnuvince.wordpress.com
110j.wordpress.com

我喜欢上面110j的清洁,所以我把它的大部分和重构,以解决我的三个问题:

  1. 正则expression式是匹配所有其他人的“家”url
  2. 我需要多个URL映射到一个导航选项卡 ,所以我需要一个更复杂的标签,其中需要可变数量的参数
  3. 修复了一些url问题

这里是:

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

我是专门为解决这个问题而写的django谱系的作者 :D

我在自己的项目中使用(完全可以接受的)jpwatts方法变得恼火,从110j的答案中得到灵感。 天堂看起来像这样:

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

如果参数与当前页面URL的起始位置相匹配,则ancestor被简单地replace为“活动”。

variables参数和完整的{% url %}types反向parsing,也被支持。 我洒了几个configuration选项,并充实了一点,并将其打包,供大家使用。

如果有人有兴趣,请阅读更多关于:

>> github.com/marcuswhybrow/django-lineage

您可以将类或ID应用于页面的body元素,而不是应用于特定的导航项。

HTML:

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

CSS:

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

由于Django 1.5 :

在所有通用的基于类的视图(或从ContextMixininheritance的任何基于类的视图)中,上下文字典包含一个指向View实例的视图variables。

所以,如果你使用这样的视图,你可以添加一些breadcrumbs作为一个类级别的字段,并在你的模板中使用它。

示例视图代码:

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

在你的模板中,你可以这样使用它:

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

如果您想另外“突出显示”父导航项目,则需要扩展breadcrumbs列表:

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

…并在您的模板中:

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

这是简单和干净的解决scheme,并与嵌套的导航工作得很好。

我这样做:

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

然后我所要做的就是在我的视图中为我的上下文字典添加{'active_tab': 'statistics'}

如果您使用的是RequestContext ,则可以在模板中获取当前path:

 {{ request.path }} 

在你看来:

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

我从上面的nivhab上取了代码,删除了一些奇怪的东西,并把它变成了一个干净的模板标签,修改它使/ account / edit /仍然会使/ 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 %} 

这只是上面的Toba提出的css解决scheme的一个变种:

在基本模板中包含以下内容:

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

然后在扩展基本使用的模板中:

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

然后,您可以使用CSS来突出显示基于body标签的当前区域(例如,如果我们有一个nav-home的id链接):

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

您可以使用具有相应参数的反向函数来获取当前的URL。

谢谢你到目前为止的答案,男士们。 我已经去了一些稍微不同的东西..

在我的模板中:

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

一旦我找出了逻辑中的哪个页面(通常在urls.py中),我将class="selected"作为上下文的一部分传递给模板的正确名称。

例如,如果我在link1页面上,我会将{'link1_active':' class="selected"'}到模板的上下文中以获取并注入。

它似乎工作,它相当干净。

编辑:保持HTML我的控制器/视图,我已经修改了这一点:

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

它使得模板的可读性稍差,但我同意,最好不要从urls文件中浏览原始HTML。

我在同一页面上有多个菜单,通过循环dynamic创build。 上述有关上下文的post给了我一个快速的解决办法。 希望这有助于某人。 (除了活动模板标签之外,我还使用这个标签 – 我的修补程序解决了dynamic问题)。 这似乎是一个愚蠢的比较,但它的作品。 我select了variablesactive_something-unique和something-unique,这样它就可以和嵌套菜单一起使用。

这里是视图的一部分(足以理解我在做什么):

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

这是来自模板:

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

我的解决scheme是编写一个简单的上下文处理器来设置基于请求path的variables:

 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} 

(不要忘了在settings.py中添加自定义的处理器到TEMPLATE_CONTEXT_PROCESSORS。)

然后,在基本模板中,我使用每个链接的ifequal标记来确定是否附加“活动”类。 当然,这种方法严格限制在path结构的灵活性上,但是对于我相对适中的部署来说却是有效的。

我只是想分享一下nivhab的职位。 在我的应用程序中我有subnavigations,我不想用CSS来隐藏它们,所以我需要某种“if”标签来显示一个项目的子导航。

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

你可以像使用激活标签一样使用它:

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

只是另一个原始解决scheme的保证。

这接受多个模式,最好也是用'''包装成相对URL的未命名模式,如下所示:

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

标签是这样的:

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

我用jquery来突出我的navbars。 这个解决scheme只是简单地将css类“active”添加到适合cssselect器的项目中。

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

比@tback的答案略有增强,没有任何%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 '' 

像这样在你的模板中使用它:

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

并在TEMPLATE_CONTEXT_PROCESSORS设置中包含"django.core.context_processors.request"

稍微修改Andreas的答案,看起来你可以传递从urls.py到模板标签的路由名称。 在我的示例my_tasks ,然后在模板标记函数中使用反向函数来确定URL应该是什么,然后可以将其与请求对象中的URL匹配(在模板上下文中可用)

 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> 

我已经看到了jpwatts , 110j , nivhab和Marcus Whybrow的答案,但是他们似乎都缺乏某些东西:根源呢? 为什么它总是活跃?

所以我做了另一种更容易的方式,这使得“控制器”自己决定,我认为它解决了大部分的大问题。

这是我的自定义标签:

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

然后,“控制器”声明所需的CSS类(事实上,最重要的是它声明它的存在的模板)

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

最后,我将其呈现在我的导航栏中:

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

因此,每个页面都有自己的nav_css_class值来设置,如果设置了,模板呈现活动状态:不需要模板上下文中的request ,不需要URL划分,也不会出现多URL页面或根页面的问题。

这是我的。 我最终在我的视图中实现了一个包含我的导航结构(包含一些元数据)的类。 然后我把这个注入到模板中并渲染出来。

我的解决scheme涉及国际化。 它可能应该被抽象出来多一点,但我真的没有真正打扰。

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) 

我使用这样的包含来定义模板逻辑。 基本模板:

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

实际包括(includes / navigation.html):

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

希望有人会觉得这有用! 我想这可以很容易地扩展这个想法来支持嵌套的层次结构等

创build一个包含模板“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> 

并将其纳入导航元素中:

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

你需要添加这个设置:

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

这里是非常简单的解决scheme, https://github.com/hellysmile/django-activeurl

从这个SO问题

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

根据需要重复每个链接。

我知道我迟到了。 我不喜欢任何stream行的解决scheme:

块方法似乎是错误的:我认为导航应该是自包含的。

template_tag方法似乎是错误的:我不喜欢,我必须先从url-tag获取url。 另外,我认为应该在模板中定义css类,而不是标签。

因此,我写了一个没有我上面描述的缺点的filter。 如果URL是活动的,它将返回True ,因此可以与{% if %}

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

代码:

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

只要确保在带有导航的页面上使用RequestContext ,或者在settings.py启用请求context_processor

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

我还使用jQuery来突出显示它,并发现它比使用非语义Django模板标签混淆模板更优雅。

下面的代码在引导程序3中使用嵌套下拉列表(突出显示父代和子代<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'); }); }); 

为当前页面添加一个click事件以return false (或将href属性更改为# )也很容易,而无需更改模板/ 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; 

我使用这个mixin的基于类的视图的组合:

 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 

与此模板中:

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

我有点类似于以前提交的另一个JS方法..只是没有jQuery …

假设我们在base.html中有以下内容:

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

我只是让我的层次结构遵循一个特定的URL模式…在主机地址之后…我有我的主要类别,例如,家庭,新闻,分析等,正则expression式只是拉出第一个字