如何在Django模板中实现面包屑?

时间:2009-05-05 20:59:41

标签: django django-templates

在谷歌搜索“Django面包屑”时提供的一些解决方案包括使用模板和block.super,基本上只是扩展基本块并将当前页面添加到其中。 http://www.martin-geber.com/thought/2007/10/25/breadcrumbs-django-templates/

http://www.djangosnippets.org/snippets/1289/ - 提供了一个模板标记,但如果您没有正确声明urls.py,我不确定这是否会有用。

我想知道最好的方法是什么?如果您之前已经实现了面包屑,那么它是如何做到的?

---编辑 -

我的问题是:在Django中是否有一种普遍接受的做面包屑的方法,但从我看到的答案中没有,并且有许多不同的解决方案,我不确定是谁给出了正确的答案to,因为我使用了block.super方法的变体,而以下所有答案都可以使用。

我猜这是一个太主观的问题。

13 个答案:

答案 0 :(得分:43)

注意:我提供了下面的完整代码段,因为djangosnippets最近很挑剔。

很酷,有人确实发现了my代码段:-)使用我的模板标签非常简单。

要回答你的问题,没有用于处理面包屑的“内置”django机制,但它确实为我们提供了下一个最好的东西:自定义模板标签。

想象一下,你想要像这样的面包屑:

Services -> Programming
Services -> Consulting

然后你可能会有一些名字的网址:“服务”,“编程”,“咨询”:

    (r'^services/$',
     'core.views.services',
     {},
     'services'),

    (r'^services/programming$',
     'core.views.programming',
     {},
     'programming'),

    (r'^services/consulting$',
     'core.views.consulting',
     {},
     'consulting'),

现在你的html模板里面(我们只看一下咨询页面)你需要做的就是:

//consulting.html
{% load breadcrumbs %}

{% block breadcrumbs %}
{% breadcrumb_url 'Services' services %}
{% breadcrumb_url 'Consulting' consulting %}

{% endblock %}

如果您想在面包屑中使用某种自定义文字,并且不想将其链接,则可以使用 breadcrumb 标记。

//consulting.html
{% load breadcrumbs %}

{% block breadcrumbs %}
  {% breadcrumb_url 'Services' services %}
  {% breadcrumb_url 'Consulting' consulting %}
  {% breadcrumb 'We are great!' %}  
{% endblock %}

您可能希望包含特定对象的ID,这也很容易。这是一个更现实的例子:

{% load breadcrumbs %}

{% block breadcrumbs %}
{% breadcrumb_url 'Employees' employee_list %}
{% if employee.id %}
    {% breadcrumb_url employee.company.name company_detail employee.company.id %}
    {% breadcrumb_url employee.full_name employee_detail employee.id %}
    {% breadcrumb 'Edit Employee ' %}
{% else %}
    {% breadcrumb 'New Employee' %}
{% endif %}

{% endblock %}

DaGood面包屑片段

提供两个模板标记以在HTML模板中使用:breadcrumb和breadcrumb_url。第一个允许创建简单的URL,文本部分和URL部分。或者只有未链接的文本(例如,作为痕迹路径中的最后一项)。第二个,实际上可以带参数的命名url!此外,它将标题作为第一个参数。

这是一个应该进入/ templatetags目录的模板标签文件。

只需在create_crumb方法中更改图像的路径,就可以了!

不要忘记在html模板顶部{%load breadcrumbs%}!

from django import template
from django.template import loader, Node, Variable
from django.utils.encoding import smart_str, smart_unicode
from django.template.defaulttags import url
from django.template import VariableDoesNotExist

register = template.Library()

@register.tag
def breadcrumb(parser, token):
    """
    Renders the breadcrumb.
    Examples:
        {% breadcrumb "Title of breadcrumb" url_var %}
        {% breadcrumb context_var  url_var %}
        {% breadcrumb "Just the title" %}
        {% breadcrumb just_context_var %}

    Parameters:
    -First parameter is the title of the crumb,
    -Second (optional) parameter is the url variable to link to, produced by url tag, i.e.:
        {% url person_detail object.id as person_url %}
        then:
        {% breadcrumb person.name person_url %}

    @author Andriy Drozdyuk
    """
    return BreadcrumbNode(token.split_contents()[1:])


@register.tag
def breadcrumb_url(parser, token):
    """
    Same as breadcrumb
    but instead of url context variable takes in all the
    arguments URL tag takes.
        {% breadcrumb "Title of breadcrumb" person_detail person.id %}
        {% breadcrumb person.name person_detail person.id %}
    """

    bits = token.split_contents()
    if len(bits)==2:
        return breadcrumb(parser, token)

    # Extract our extra title parameter
    title = bits.pop(1)
    token.contents = ' '.join(bits)

    url_node = url(parser, token)

    return UrlBreadcrumbNode(title, url_node)


class BreadcrumbNode(Node):
    def __init__(self, vars):
        """
        First var is title, second var is url context variable
        """
        self.vars = map(Variable,vars)

    def render(self, context):
        title = self.vars[0].var

        if title.find("'")==-1 and title.find('"')==-1:
            try:
                val = self.vars[0]
                title = val.resolve(context)
            except:
                title = ''

        else:
            title=title.strip("'").strip('"')
            title=smart_unicode(title)

        url = None

        if len(self.vars)>1:
            val = self.vars[1]
            try:
                url = val.resolve(context)
            except VariableDoesNotExist:
                print 'URL does not exist', val
                url = None

        return create_crumb(title, url)


class UrlBreadcrumbNode(Node):
    def __init__(self, title, url_node):
        self.title = Variable(title)
        self.url_node = url_node

    def render(self, context):
        title = self.title.var

        if title.find("'")==-1 and title.find('"')==-1:
            try:
                val = self.title
                title = val.resolve(context)
            except:
                title = ''
        else:
            title=title.strip("'").strip('"')
            title=smart_unicode(title)

        url = self.url_node.render(context)
        return create_crumb(title, url)


def create_crumb(title, url=None):
    """
    Helper function
    """
    crumb = """<span class="breadcrumbs-arrow">""" \
            """<img src="/media/images/arrow.gif" alt="Arrow">""" \
            """</span>"""
    if url:
        crumb = "%s<a href='%s'>%s</a>" % (crumb, url, title)
    else:
        crumb = "%s&nbsp;&nbsp;%s" % (crumb, title)

    return crumb

答案 1 :(得分:12)

Django管理视图模块具有自动面包屑,其实现如下:

{% block breadcrumbs %}
    <div class="breadcrumbs">
        <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
        {% block crumbs %}
            {% if title %} &rsaquo; {{ title }}{% endif %}
        {% endblock %}
    </div>
{% endblock %}

所以对此有一些内置的支持..

答案 2 :(得分:6)

我的视图函数将面包屑作为简单列表发出。

某些信息保存在用户的会话中。然而,间接地,它来自URL。

Breadcrumbs并不是一个简单的线性列表,它们就是这样的 - 这就是浏览器历史记录的用途。他们去过的地方的简单列表并没有成为良好的面包屑痕迹,因为它没有反映出任何意义。

对于我们的大多数视图功能,导航非常固定,并且基于模板/视图/ URL设计。在我们的案例中,有很多深入细节,面包屑反映了缩小 - 我们有一个“领域”,一个“列表”,一个“父母”和一个“孩子”。它们形成了从一般到特定的简单层次结构。

在大多数情况下,一个明确定义的URL可以简单地分解成一个很好的痕迹痕迹。实际上,这是一个良好的URL设计测试 - URL可以被解释为面包屑并有意义地显示给用户。

对于一些视图函数,我们提供的信息是“多对多”联接的一部分,例如,有两个候选父项。 URL可以说一件事,但会话的上下文堆栈说另一个。

因此,我们的视图函数必须在会话中留下上下文线索,以便我们可以发出面包屑。

答案 3 :(得分:5)

尝试django-breadcrumbs - 一个可插入的中间件,在您的请求对象中添加可调用/可迭代的面包屑。

它支持简单视图,通用视图和Django FlatPages应用程序。

答案 4 :(得分:5)

我遇到了同样的问题,最后我为它制作了简单的django模板标签:https://github.com/prymitive/bootstrap-breadcrumbs

答案 5 :(得分:3)

  

http://www.djangosnippets.org/snippets/1289/ - 提供了一个模板标记,但如果你没有正确宣布你的urls.py,我不确定这会有用。

如果您没有正确宣布urls.py,那么什么都行不通。话虽如此,它似乎不是从urls.py导入的。事实上,它似乎正确使用该标记,您仍然必须传递模板的一些变量。好的,这不是真的:间接通过url标记调用的默认breadcrumb标记。但据我所知,它实际上并没有称之为标签;所有出现的url都是本地创建的变量。

但我不是解析模板标签定义的专家。所以说在代码中的其他地方它神奇地复制了url标记的功能。用法似乎是您将参数传递给反向查找。同样,无论您的项目是什么,都应该配置urls.py,以便可以通过反向查找到达任何视图。面包屑尤其如此。想一想:

home > accounts > my account

如果帐户持有任意的,硬编码的网址吗? 可以“我的帐户”曾经拥有一个任意的,硬编码的网址吗?某种程度上,你会以某种方式编写面包屑,以使你的urls.py被颠倒过来。这实际上只会发生在以下两个地方之一:在您的视图中,通过调用reverse或在模板中调用模仿reverse功能的模板标记。可能有理由更喜欢前者而不是后者(链接的代码段锁定了你),但避免urls.py文件的逻辑配置不是其中之一。

答案 6 :(得分:3)

尝试django-mptt

  

使用Django Model类实现Modified Preorder Tree Traversal(MPTT)并使用Model实例树的实用程序。

答案 7 :(得分:1)

显然,没有一个最好的答案,但出于实际原因,我发现值得考虑天真的方式。 只是覆盖并重写整个面包屑...... (至少在官方django.contrib.breadcrumb发布之前)

不要过于花哨,最好保持简单。它有助于新人理解。它是非常可定制的(例如权限检查 breadcrumb图标分隔符活动面包屑等等... )

基本模板

<!-- File: base.html -->
<html>
<body>
  {% block breadcrumb %}
  <ul class="breadcrumb">
    <li><a href="{% url 'dashboard:index' %}">Dashboard</a></li>
  </ul>
  {% endblock breadcrumb %}
  {% block content %}{% endblock content %}
</body>
</html>

实施模板

稍后在每个页面上我们重写覆盖整个面包屑块。

<!-- File: page.html -->
{% extends 'base.html' %}
{% block breadcrumb %}
<ul class="breadcrumb">
  <li><a href="{% url 'dashboard:index' %}">Dashboard</a></li>
  <li><a href="{% url 'dashboard:level-1:index' %}">Level 1</a></li>
  <li class="active">Level 2</li>
</ul>
{% endblock breadcrumb %}

Practicallity

Realworld用例:

答案 8 :(得分:0)

这样的事情可能适合你的情况:

捕获视图中的整个网址并从中创建链接。这需要修改urls.py,每个需要包含面包屑的视图和模板。

首先,您将在urls.py文件中捕获整个网址

原始urls.py
...
(r'^myapp/$', 'myView'),
(r'^myapp/(?P<pk>.+)/$', 'myOtherView'),
...
新的urls.py
...
(r'^(?P<whole_url>myapp/)$', 'myView'),
(r'^(?P<whole_url>myapp/(?P<pk>.+)/)$', 'myOtherView'),
...

然后在你看来像:

views.py
...
def myView(request, whole_url):
    # dissect the url
    slugs = whole_url.split('/')
    # for each 'directory' in the url create a piece of bread
    breadcrumbs = []
    url = '/'
    for slug in slugs:
        if slug != '':
            url = '%s%s/' % (url, slug)
            breadcrumb = { 'slug':slug, 'url':url }
            breadcrumbs.append(breadcrumb)

    objects = {
        'breadcrumbs': breadcrumbs,
    }
    return render_to_response('myTemplate.html', objects)
...

应该将其导入到导入到需要它的视图中的函数

然后在你的模板中打印出面包屑

myTemplate.html
...
<div class="breadcrumb-nav">
    <ul>
    {% for breadcrumb in breadcrumbs %}
        <li><a href="{{ breadcrumb.url }}">{{ breadcrumb.slug }}</a></li>
    {% endfor %}
    </ul>
</div>
...

这样做的一个缺点是,就目前而言,您只能将网址的“目录”部分显示为链接文本。我的头顶(可能不是一个好的)的一个解决方法是在文件中保留一个定义面包屑功能的字典。

无论如何,一种方式你可以完成面包屑,欢呼:)

答案 9 :(得分:0)

你可能想尝试django-headcrumbs(别担心,他们不会吃掉你的大脑)。

它非常轻巧,使用起来非常简单,所有你需要做的就是注释你的视图(因为在模板中定义crumbs结构对我来说听起来很疯狂),装饰器解释了如何从给定视图中返回。

以下是文档中的示例:

from headcrumbs.decorators import crumb
from headcrumbs.util import name_from_pk

@crumb('Staff')  # This is the root crumb -- it doesn’t have a parent
def index(request):
    # In our example you’ll fetch the list of divisions (from a database)
    # and output it.

@crumb(name_from_pk(Division), parent=index)
def division(request, slug):
    # Here you find all employees from the given division
    # and list them.

还有一些实用程序函数(例如,在示例中可以看到name_from_pk),可以自动为您的面包屑生成漂亮的名称,而无需编写大量代码。

答案 10 :(得分:0)

我为此创建了模板过滤器。

将自定义过滤器(我将其命名为'makebreadcrumbs')应用于request.path,如下所示:

{% with request.resolver_match.namespace as name_space %}
    {{ request.path|makebreadcrumbs:name_space|safe }}
{% endwith %}

我们需要将url名称空间作为arg传递给我们的过滤器。

还使用安全过滤器,因为我们的过滤器将返回需要解析为html内容的字符串。

自定义过滤器应如下所示:

@register.filter
def makebreadcrumbs(value, arg):
    my_crumbs = []
    crumbs = value.split('/')[1:-1]  # slice domain and last empty value
    for index, c in enumerate(crumbs):
        if c == arg and len(crumbs) != 1:  
        # check it is a index of the app. example: /users/user/change_password - /users/ is the index.
            link = '<a href="{}">{}</a>'.format(reverse(c+':index'), c)
        else:
            if index == len(crumbs)-1:
                link = '<span>{}</span>'.format(c)  
                # the current bread crumb should not be a link.
            else:
                link = '<a href="{}">{}</a>'.format(reverse(arg+':' + c), c)
        my_crumbs.append(link)
    return ' &gt; '.join(my_crumbs)  
    # return whole list of crumbs joined by the right arrow special character.

重要的:

我们过滤器中'value'的拆分部分应该等于urls.py中的命名空间,因此可以调用reverse方法。

希望它有所帮助。

答案 11 :(得分:0)

您还可以通过在视图中添加面包屑属性来减少使用django-view-breadcrumbs管理面包屑所需的样板。

urls.py

urlpatterns = [
    ...
    path('posts/<slug:slug>', views.PostDetail.as_view(), name='post_detail'),
    ...
] 

views.py

from django.views.generic import DetailView
from view_breadcrumbs import DetailBreadcrumbMixin


class PostDetail(DetailBreadcrumbMixin, DetailView):
    model = Post
    template_name = 'app/post/detail.html'

base.html

{% load django_bootstrap_breadcrumbs %}

{% block breadcrumbs %}
    {% render_breadcrumbs %}
{% endblock %}

答案 12 :(得分:0)

收集当前 url 的所有可调用路径的通用方法可以通过以下代码段解析:

from django.urls import resolve, Resolver404

path_items = request.path.split("/")
path_items.pop(0)
path_tmp = ""

breadcrumb_config = OrderedDict()
for path_item in path_items:
    path_tmp += "/" + path_item
    try:
        resolve(path_tmp)
        breadcrumb_config[path_item] = {'is_representative': True, 'current_path': path_tmp}
    except Resolver404:
        breadcrumb_config[path_item] = {'is_representative': False, 'current_path': path_tmp}

如果 resolve 函数无法从任何 urlpattern 中获取真实路径,则会抛出 Resolver404 异常。对于这些项目,我们将 is_representative 标志设置为 false。 OrderedDict breadcrumb_config 在那之后保存具有那里配置的面包屑项目。

例如,对于 bootstrap 4 面包屑,您可以在模板中执行以下操作:

<nav aria-label="breadcrumb">
  <ol class="breadcrumb">
      {% for crumb, values in BREADCRUMB_CONFIG.items %}
        <li class="breadcrumb-item {% if forloop.last or not values.is_representative %}active{% endif %}" {% if forloop.last %}aria-current="page"{% endif %}>
            {% if values.is_representative %}
                <a href="{{values.current_path}}">
                    {{crumb}}
                </a>
            {% else %}
                {{crumb}}
            {% endif %}
        </li>
      {% endfor %}
  </ol>
</nav>

只有不会引发 404 的链接是可点击的。

相关问题