姜戈导航

我刚刚完成了我的第一个小网络应用程序在 django,我喜欢它。我即将开始将一个旧的生产 PHP 站点转换为 django,作为其模板的一部分,有一个导航栏。

在 PHP 中,我在模板代码中根据当前 URL 检查每个导航选项的 URL,如果它们对齐,则应用一个 CSS 类。真是一团糟。

是否有更好的 django 或处理模板中代码的好方法?

首先,我应该如何获取当前的 URL?

102982 次浏览

您可以使用带有适当参数的 反向函数来获取当前 URL。

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

HTML:

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

CSS:

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

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

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>

关于

{% extends "base.html" %}


{% block nav-about %}<strong class="nav-active">About</strong>{% endblock %}

谢谢你们到目前为止的回答,先生们,我又有些不同的想法了。

在我的模板中:

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

我是这样做的:

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

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

如果您使用 RequestContext,您可以得到当前路径在您的模板为:

\{\{ request.path }}

在你看来:

from django.template import RequestContext


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

你不需要一个 if 来做这件事,看看下面的代码:

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

I liked the cleanness of 110j above so I took most of it and refactored to solve the 3 problems I had with it:

  1. 正则表达式是 将“ home”url 与所有 others
  2. 我需要 < strong > 多个 URL 映射到一个导航标签 ,所以我 需要一个更复杂的标签 可变参数数量
  3. fixed some url problems

这就是:

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

网址:

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

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>

我在同一个页面上有多个菜单,它们是通过循环动态创建的。上面关于上下文的帖子给了我一个快速解决方案。希望这对谁有帮助。(我除了使用活动模板标记之外还使用了这个功能——我的补丁解决了动态问题)。这个比较看起来很傻,但是很有用。我选择将变量命名为 active _ something-only 和 something-only,这样它就可以使用嵌套菜单了。

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

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>

我的解决方案是编写一个简单的上下文处理器,根据请求路径设置一个变量:

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}

(不要忘记将自定义处理器添加到 setings.py 中的 TEMPLATE _ CONTEXT _ PROCESSORS。)

然后在基本模板中,对每个链接使用一个等价的标记来确定是否追加“ active”类。虽然这种方法严格限制了路径结构的灵活性,但是它适用于我相对适度的部署。

我只是想分享一下我对 Nivhab 职位的小小改进。在我的应用程序中,我有子导航,我不想仅仅使用 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 %}

我从上面的 nivhab 中提取了代码,去除了一些奇怪的地方,将其制作成一个干净的 templatetag,并对其进行了修改,以便/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 %}

这只是妥坝提出的 css 解决方案的一个变体:

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

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

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

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

然后,您可以使用 css 根据 body 标记突出显示当前区域(例如,如果我们有一个 id 为 nav-home 的链接) :

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

只不过是原始解决方案的又一次改进。

它接受多个模式,最好也是未命名的模式,作为相对 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 ""

Here's my go at it. I ended up implementing a class in my views that contains my navigation structure (flat with some metadata). I then inject this to the template and render it out.

我的解决方案处理 i18n。它可能应该被抽象出来一点,但我真的没有真正的麻烦,真的。

视图:

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 定义了模板逻辑:

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

实际包括(包括/导航.html) :

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

希望有人会发现这个有用!我想将这个想法扩展到支持嵌套层次结构等应该很容易。

稍微修改一下 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' ),

翻译: 奇芳

<li class="{% active request all_tasks %}"><a href="{% url all_tasks %}">Everyone</a></li>

创建一个 include 模板“ 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',
)

I used jquery to highlight my navbars. This solution simply adds the css class "active" to the item which fits the css selector.

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

这里有一个非常简单的解决方案,https://github.com/hellysmile/django-activeurl

我是 姜戈血统 的作者,我写这本书是为了解决这个问题: 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简单地替换为“ active”。

还支持变量参数和完整的 {% url %}类型反向解析。我添加了一些配置选项,并对其进行了一些充实,然后将其打包供大家使用。

如果有人感兴趣,可以在以下网址了解更多:

>> github.com/marcuswhybrow/django-lineage

姜戈1.5以来:

在所有基于类的泛型视图中(或者在继承 来自 ContextMixin) ,上下文字典包含一个视图变量 指向 View 实例的。

So if you are using such views, you could add something likie breadcrumbs as a class level field and use it in your templates.

视图代码示例:

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

In your template you could use it in this way:

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

这是一个简单而干净的解决方案,可以很好地使用嵌套导航。

从这个 有个问题

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

根据需要为每个链接重复操作。

我知道我迟到了,但我不喜欢任何流行的解决办法:

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

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

因此,我编写了一个过滤器,它没有我上面描述的缺点。如果 URL 是活动的,它返回 True,因此可以与 {% if %}一起使用:

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

The code:

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

只要确保在带导航的页面上使用 RequestContext,或者在 settings.py中启用 request context _ 處理器

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

It's also quite easy to add a click event to return false (or change the href attribute to #) for the current page, without changing the template/html markup:

        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;

对于基于类的视图,我使用了这种 mix 的组合:

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>

Mine is a bit similar to another JS approach submitted previously.. just without 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>

我只是让我的层次结构遵循一定的网址模式... 后主机地址... 我有我的主要类别,例如,主页,新闻,分析等和正则表达式只是拉出第一个单词的位置

在没有任何 %if%标签的情况下,对 @ tback的答案进行了一点增强:

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

Use it in your template like that:

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

我看过 Jpwatts’、 110JNivhab马库斯 · 惠布劳的答案,但它们似乎都缺少一些东西: 根路径怎么样?为什么它总是处于活跃状态?

所以我用了另一种更简单的方法,让“控制器”自己决定,我认为它解决了大多数大问题。

这是我的定制标签:

## 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 页面或根页面的问题。

受到这个 解决方案的启发,我开始使用这种方法:

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

我发现最好的方法是使用一个包含标签:

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>

这只是我的基本引导导航项目,我希望呈现。

它获取 href 值,还可以根据当前请求计算 link _ name 值. is_active

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

然后在导航中使用它: 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>

**

只需像这样添加 jinja 格式的 url 和 name

**

 <ul class="nav navbar-nav">
<li>
<a href="{% url 'index' %}">Cities</a>
</li>
<li>
<a href="{% url 'all_cafes' %}">Cafes</a>
</li>
</ul>