breadcrumbs

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# -*- coding: iso-8859-1 -*-
# $Id: breadcrumbs.py 122 2008-10-01 10:31:57Z tguettler $
# $HeadURL: svn+ssh://svnserver/svn/djangotools/trunk/utils/breadcrumbs.py $

# http://www.djangosnippets.org/snippets/1026/

# Python
from urlparse import urljoin

# Django
from django import http
from django.core import urlresolvers
from django.core.urlresolvers import reverse
from django.conf import settings
from django.utils.http import urlquote
from django.utils.html import conditional_escape as escape
from django.utils.safestring import mark_safe

def join(str, mylist):
    str=escape(str)
    return mark_safe(str.join([escape(item) for item in mylist]))

'''
Simple Breadcrumbs for Django:

The value of request.path gets examined and all views of the 'upper' urls
need to have an attribute'breadcrumbs'. The attribute can be a
string or a method. If it is a method it needs to accept two arguments
(args and kwargs) which correspond to the arguments of the view.

Example: request.path == '/users/foo/config/'

Views:

def config(request, ...):
    ...
config.breadcrumbs=u'Config'

def user(request, username):
    ...
user.breadcrumbs=lambda args, kwargs: args[1]

def users(request):
    ...
users.breadcrumbs='Users'


The URL 
    /users/foo/config/
will get to these breadcrumbs:
    Users>>foo>>Config

All except the last breadcrumb will be links.

Implemented with Django's resolve(url)

Usage:
 breadcrumbs, default_title = breadcrumbs.get_breadcrumbs(request)
     breadcrumbs: SafeUnicode HTML String
     default_title: Unicode String with the name of the current view.

Let Hansel and Grethel find their way home:
  http://en.wikipedia.org/wiki/Hansel_and_Gretel

'''

def link_callback_default(url, bc):
    return mark_safe(u'<a href="%s">%s</a>' % (
        urlquote(url), escape(bc)))

def join_callback_default(breadcrumbs):
    return mark_safe(u'<div class="breadcrumbs">%s</div>' % (join('>>', breadcrumbs)))

def get_breadcrumbs(request, link_callback=None, join_callback=None):
    if link_callback is None:
        link_callback=link_callback_default
    if join_callback is None:
        join_callback=join_callback_default
    path_info=request.META['PATH_INFO']
    url=path_info
    breadcrumbs=[]
    resolver = urlresolvers.get_resolver(None)
    count=0
    while True:
        count+=1
        assert count<1000, count
        callback=None
        try:
            callback, callback_args, callback_kwargs = resolver.resolve(url)
        except http.Http404, exc:
            pass
        else:
            bc=getattr(callback, 'breadcrumbs', None)
            assert bc!=None, u'Callback %s.%s function breadcrumbs does not exist.' % (
                callback.__module__, callback.__name__)
            sub_crumbs=[]
            if isinstance(bc, basestring):
                pass
            elif hasattr(bc, '__call__'):
                bc=bc(callback_args, callback_kwargs)
                if isinstance(bc, tuple):
                    # The callable can return a tuple. The first entry is
                    # the name, the second is a tuple list of (url, name)
                    # Example .../objects/123/ (Object 123 is part of Object 99):
                    # Objects>>99>>123
                    bc, sub_crumbs = bc
            else:
                raise Exception('Unkown type for breadcrumbs attribute: %s %s %r' % (
                    type(bc), bc, bc))
            if url!=path_info:
                bc=link_callback('%s%s' % (request.META['SCRIPT_NAME'], url), bc)
            breadcrumbs.append(bc)
            for bc_url, name in sub_crumbs:
                breadcrumbs.append(link_callback(bc_url, name))
                
        # Parent URL heraussuchen.
        if url=='/':
            break
        url=urljoin(url, '..')
    assert breadcrumbs
    default_title=breadcrumbs[0]
    breadcrumbs.append('')
    breadcrumbs.reverse()
    joined=join_callback(breadcrumbs)
    return (joined, default_title)

def test_all_views():
    u'''
    Unittest: Check if all you views have a breadcrumbs()
    attribute.

    Django views are ignored.
    '''
    resolver=urlresolvers.get_resolver(None)
    missing=[]
    for function, patterns in resolver.reverse_dict.items():
        if not function:
            continue
        sub_resolver=patterns[0]
        if isinstance(function, basestring): # TODO: Alias auflösen.
            continue
        if function.__module__.startswith('django.'):
            continue
        name='%s.%s' % (function.__module__, function.__name__)
        if not hasattr(function, 'breadcrumbs'):
            missing.append(name)
            continue
        bc=function.breadcrumbs
        if isinstance(bc, basestring):
            try:
                unicode(bc)
            except UnicodeError, exc:
                missing.append('UnicodeError, %s: %s' % (name, exc))
    missing.sort()
    assert not missing, 'Missing breadcrumbs function: %s' % (missing)

More like this

  1. Breadcrumbs filter by Oggu 5 years, 3 months ago
  2. django app name & breadcrumbs l10n by xormag 2 years, 9 months ago
  3. Django Breadcrumbs Snippet by flashingpumpkin 4 years, 11 months ago
  4. URL based breadcrumbs by phlex 6 years ago
  5. Generic CSV Export by zbyte64 5 years, 10 months ago

Comments

BeetleB (on December 6, 2008):

I really like this snippet, and customized it to have my own breadcrumb style (a list rather than the >>> method - better for accessibility).

A more serious problem, but easily rectified, is that it throws an assertion error whenever I access a Django builtin view (such as when I log in). In your test function, you wisely omitted checking the Django views. However, your main function doesn't check.

One whould just write wrapper views to get around this problem - as it'll ensure a breadcrumb still appears on those pages. But that would be a poor idea if one were to use the Django admin. So I had to put in a test for internal Django views in the main code (just a single line and lots of indenting).

#

guettli (on March 18, 2009):

I use breadcrumbs only for user visibles pages. I don't use it for the admin page.

#

vicpada (on April 29, 2009):

Hi, Im just starting to use django.

I didnt get where to exactly put the code for the usage:

Usage: breadcrumbs, default_title = breadcrumbs.get_breadcrumbs(request) breadcrumbs: SafeUnicode HTML String default_title: Unicode String with the name of the current view.

Also, what do you have to put on the template?

Thanks in advance!

#

(Forgotten your password?)