django - 使用带动态元素的公共头部
我打算用Django创建一个网站,想让整个网站都有一个统一的头部。虽然我看过Django关于模板继承的文档,但对于头部的“动态”元素,我还是找不到一个好的解决办法。
比如,网站的头部会有一些标签,类似于http://www.google.com/上的“网页”、“图片”等,选中的标签会显示你在网站中的当前位置。
按照Django的模板继承,似乎可以创建一个基础模板,像这样:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<link rel="stylesheet" href="style.css" />
<title>{% block title %}My Amazing Site{% endblock %}</title>
</head>
<body>
<div id="header">
{% block header %}
.... html to create tabs ...
{% endblock header %}
</div>
然后在我其他的页面中,我会这样做:
{% extends "base.html" %}
{% block header % }
.... html to create tabs with one tab "selected" ...
{% endblock header %}
这让我觉得很烦,因为每个页面都得重复写一遍头部的HTML,只是稍微有点不同。所以当我想添加一个新标签时,就得修改每一个HTML文件。
经过进一步阅读,我发现还有其他可能的解决方案:
1 - 创建一个自定义模板标签,传入当前选中的标签,这样在每个HTML页面中我只需要调用: {% block header %} {% mycustomtag abc %} {% endblock header %}
我不喜欢这个方案,因为这需要把HTML放到Python代码里去创建这个自定义标签。
2 - 创建X个子模板,都是从base.html继承的,每个子模板都有适当选中的标签。然后每个页面根据想要选中的标签来继承相应的子模板。
这个方案看起来还不错,但问题是仍然需要X个几乎完全相同的HTML文件,而且当添加或删除标签时,还是得修改所有文件。
3 - 使用JavaScript(比如jQuery)在页面加载时修改头部,以“选中”正确的标签。
这个方案也不错,但需要记得在每个页面的JavaScript中添加这个功能。好处是头部的HTML只需要在一个HTML文件中。
还有其他建议吗?
谢谢!
3 个回答
这是一个相当常见的问题,我想出了几种不同的解决方法。
既然你在寻找选项,这里有三种其他的替代方法来实现这个效果。你提到的选项和下面列出的这些都有各自的优缺点。最终选择哪个,还是要看你自己觉得哪个最合适。
替代方案 1 - 使用正则表达式和哈希表
这个方法可以在客户端(效果不太好)或服务器端(更推荐)执行。你可以有一个标签,里面有一个输入:正则表达式。使用时,它的样子会是这样的……
// In base.html...
<li class="tab {% is_tab_active r'^/cars/' %}"><a>Cars</a></li>
<li class="tab {% is_tab_active r'^/trucks/' %}"><a>Trucks</a></li>
这个自定义标签会把正则表达式应用到当前正在查看的页面上。如果成功,它会添加一个 CSS 类 "active",如果不成功,则添加 "inactive"(或者你自己定义的 CSS 类名)。
我考虑这个方法已经有一段时间了。我觉得应该有个好办法把它和 urls.py 结合起来,但我还没看到过。
替代方案 2 - 使用 CSS
如果你能识别每个 [body] 标签,或者至少为你网站的各个部分有一个共同的模板,那么可以用 CSS 来指定哪个是活动的。考虑以下内容:
body.cars_section .navigation #cars_tab { color: #00000; }
body.truck_section .navigation #trucks_tab { color: #00000; }
对于你的基础模板……
<body class="{% block category %}{% endblock %}">
...
<ul class="navigation">
<li id="cars_tab"><a>Cars</a></li>
<li id="trucks_tab"><a>Trucks</a></li>
然后在任何页面上,你只需放入它所属的类别(与 CSS 规则匹配)……
{% extends "base.html" %}
...
{% block category %}cars_section{% endblock %}
替代方案 3 - 使用一些复杂的中间件
Django 允许你编写中间件来影响几乎所有你想要的行为。这个方法看起来比较复杂,而且可能会对性能产生负面影响,但我觉得还是有必要提一下,作为一个选项。
第一个方案就能解决问题——只需要为这个标签准备一个单独的模板文件。
假设你有两个模型:“分类”(Category)和“文章”(Article)。
class Category(models.Model):
title = models.CharField(_("Name"), max_length=200)
introduction = models.TextField(blank=True, null=True)
slug = models.SlugField(help_text=_("Used for URLs"))
sort_order = models.IntegerField(_("Sortierung"))
class Article(models.Model):
title = models.CharField(_("Full Name"), max_length=255)
slug = models.SlugField(_("Slug Name"), unique=True, help_text=_("This is a short, descriptive name of article that will be used in the URL link to this item"))
text = models.TextField(_("Text of Article"), blank=True, null=True)
category = models.ForeignKey(Category)
在你的视图中,你需要把正在查看的分类传递给模板:
@render_to('cat_index.html')
def category_view(request,string):
cat = Category.objects.get(slug=string)
articles = Article.objects.filter(category = cat).order_by('date')
return {
'articles':articles,
'category':cat,
}
(注意:使用annoying的render_to
装饰器——和render_to_response
是一样的)
然后在你的模板中,你可以这样调用一个包含标签(inclusion_tag):
@register.inclusion_tag('snippets/navigation.html')
def navigation(cat=None):
return {'cats':Category.objects.order_by('sort_order'),
'cat':cat
}
在你的基础模板中使用这个(通常叫做base.html):
{% navigation category %}
现在在包含标签的模板(snippets/navigation.html
)中,你可以对cats
进行循环,如果其中一个等于cat
,你就可以给它设置其他样式。
<ul>
{% for c in cats %}
<li{% ifequal c cat %} class="active"{% endifequal %}>
<a href="{{c|url}}">{{ c }}</a>
</li>
{% endfor %}
</ul>
我假设每个标签页都是你模板 base.html
中的一个列表项。
<ul>
<li>Tab 1</li>
<li>Tab 2</li>
...
</ul>
给每个 li
添加一个额外的块。
<ul>
<li class="{% block class_tab1 %}inactive{% endblock %}">Tab 1</li>
<li class="{% block class_tab2 %}inactive{% endblock %}">Tab 2</li>
<li class="{% block class_tab3 %}inactive{% endblock %}">Tab 3</li>
...
</ul>
然后在你的模板中,如果要选择标签页 1:
{% extends "base.html" %}
{% block class_tab1 %}active{% endblock %}
...
那么标签页 1 渲染出来的 HTML 是:
<ul>
<li class="active">Tab 1</li>
<li class="inactive">Tab 2</li>
<li class="inactive">Tab 3</li>
...
</ul>
你可以写 CSS 来根据需要设置 li .active
的样式。