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