views:

2800

answers:

6

Some solutions provided on doing a Google search for "Django breadcrumbs" include using templates and block.super, basically just extending the base blocks and adding the current page to it. http://www.martin-geber.com/thought/2007/10/25/breadcrumbs-django-templates/

http://www.djangosnippets.org/snippets/1289/ - provides a template tag but I'm not sure this would work if you don't have your urls.py properly declared.

I'm wondering what's the best way? And if you have implemented breadcrumbs before how did you do it?

--- Edit --

My question was meant to be: is there a general accepted method of doing breadcrumbs in Django, but from the answers I see there is not, and there are many different solutions, I'm not sure who to award the correct answer to, as I used a variation of using the block.super method, while all the below answers would work.

I guess then this is too much of a subjective question.

+2  A: 

My view functions emit the breadcrumbs as a simple list.

Some information is kept in the user's session. Indirectly, however, it comes from the URL's.

Breadcrumbs are not a simple linear list of where they've been -- that's what browser history is for. A simple list of where they've been doesn't make a good breadcrumb trail because it doesn't reflect any meaning.

For most of our view functions, the navigation is pretty fixed, and based on template/view/URL design. In our cases, there's a lot of drilling into details, and the breadcrumbs reflect that narrowing -- we have a "realm", a "list", a "parent" and a "child". They form a simple hierarchy from general to specific.

In most cases, a well-defined URL can be trivially broken into a nice trail of breadcrumbs. Indeed, that's one test for good URL design -- the URL can be interpreted as breadcrumbs and displayed meaningfully to the users.

For a few view functions, where we present information that's part of a "many-to-many" join, for example, there are two candidate parents. The URL may say one thing, but the session's context stack says another.

For that reason, our view functions have to leave context clues in the session so we can emit breadcrumbs.

S.Lott
your method sounds like a more complex variation of the first method of using block.super which i used to solve my problem
Rasiel
A: 

http://www.djangosnippets.org/snippets/1289/ - provides a template tag but i'm not sure this would work if you don't have your urls.py properly declared.

Nothing will work if you don't have your urls.py properly declared. Having said that, it doesn't look as though it imports from urls.py. In fact, it looks like to properly use that tag, you still have to pass the template some variables. Okay, that's not quite true: indirectly through the default url tag, which the breadcrumb tag calls. But as far as I can figure, it doesn't even actually call that tag; all occurrences of url are locally created variables.

But I'm no expert at parsing template tag definitions. So say somewhere else in the code it magically replicates the functionality of the url tag. The usage seems to be that you pass in arguments to a reverse lookup. Again, no matter what your project is, you urls.py should be configured so that any view can be reached with a reverse lookup. This is especially true with breadcrumbs. Think about it:

home > accounts > my account

Should accounts, ever hold an arbitrary, hardcoded url? Could "my account" ever hold an arbitrary, hardcoded url? Some way, somehow you're going to write breadcrumbs in such a way that your urls.py gets reversed. That's really only going to happen in one of two places: in your view, with a call to reverse, or in the template, with a call to a template tag that mimics the functionality of reverse. There may be reasons to prefer the former over the latter (into which the linked snippet locks you), but avoiding a logical configuration of your urls.py file is not one of them.

David Berger
right agreed with your point of hard coded urls
Rasiel
A: 

Something like this may work for your situation:

Capture the entire URL in your view and make links from it. This will require modifying your urls.py, each view that needs to have breadcrumbs, and your templates.

First you would capture the entire URL in your urls.py file

original urls.py
...
(r'^myapp/$', 'myView'),
(r'^myapp/(?P<pk>.+)/$', 'myOtherView'),
...
new urls.py
...
(r'^(?P<whole_url>myapp/)$', 'myView'),
(r'^(?P<whole_url>myapp/(?P<pk>.+)/)$', 'myOtherView'),
...

Then in your view something like:

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

Which should be pulled out into a function that gets imported into the views that need it

Then in your template print out the breadcrumbs

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

One shortcoming of doing it this way is that as it stands you can only show the 'directory' part of the url as the link text. One fix for this off the top of my head (probably not a good one) would be to keep a dictionary in the file that defines the breadcrumb function.

Anyways that's one way you could accomplish breadcrumbs, cheers :)

rennat
i'm not too sure about this, anybody else have input on this?
Rasiel
Yea since I started using Django I haven't had a project that needed breadcrumbs so this was made up on the spot, reading through it again you don't even need to mess with your urls.py file, you can pull the url out of the request object (duh!) so that's one less step, then it could even be applied as a context processor and then any template that needs it can use it.
rennat
+10  A: 

Cool, someone actually found my snippet :-) I don't know what @David is on about, the use of my template tag is rather simple.

To answer your question there is no "built-in" django mechanism for dealing with breadcrumbs, but it does provide us with the next best thing: custom template tags.

Imagine you want to have breadcrumbs like so:

Services -> Programming
Services -> Consulting

Then you will probably have a few named urls: "services", and "programming", "consulting":

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

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

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

Now inside your html template (lets just look at consulting page) all you have to put is:

//consulting.html
{% load breadcrumbs %}

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

{% endblock %}

If you want to use some kind of custom text within the breadcrumb, and don't want to link it, you can use breadcrumb tag instead.

//consulting.html
{% load breadcrumbs %}

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

There are more involved situations where you might want to include an id of a particular object, which is also easy to do. This is an example that is more realistic:

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

I guess I should update my (shameless plug) blog with more info about this.

drozzy
i think so, i did visit your blog, but an update with the above explanation would be helpful.i implemented a version of this, but instead of using named links, just used hardcoded urls! ooops! i will attempt to install your template filter! thanks
Rasiel
A: 

Try django-mptt.

Utilities for implementing Modified Preorder Tree Traversal (MPTT) with your Django Model classes and working with trees of Model instances.

TomA
A: 

Try django-breadcrumbs. It's completely straightforward and works great with FlatPages after a small patch.

minder