Django: How can I access previous results via navigation bar? - django

I implemented a Django website to search my database. My navigation bar has the following permanent tags: Search, Results, Article, About, Contact. After I submit the term on the Search-site,
the Result-site opens and shows a list of all possible hits from the database. From there, I can click on a link to navigate to the Article-site to read the article. However, when I leave the Result-site to another site and want to go back later by clicking on the Result-tag, I want to see the previous results again. But it won’t reopen my previous results because I neither send a request to the corresponding search methods nor can I tell the tag which dynamic url to open (/search/term).
How can I revisit my previous results? Can I somehow keep the result stored on this side until I start another search via the Search-site? I saved the search term as session variable. Can I send a request containing a session variable by clicking on a tag in my navigation bar?
I do not want to use a back-button because it should be possible to go back to the Result-site independent from where you are right now on the website.
Can you please help me?
views.py
class IndexView(ListView):
model = S
template_name = 'app/index.html'
class ResultsView(DetailView):
model = S
template_name = 'app/results.html'
context_object_name = 'results'
def search(request):
term = request.GET['term'] #search term
request.session['query'] = term
results = S.objects.filter(name=term)
list_of_results = [model_to_dict(aresult) for aresult in results]
return render(request, 'app/results.html', {'list_of_results':list_of_results})
base.html
<header class="naviBar">
<nav>
<ul>
{% block index %}
<li>
Search
</li>
{% endblock %}
{% block results %}
<li>
Result
</li>
{% endblock %}
{% block contact %}
<li>
Contact
</li>
{% endblock %}
</ul>
</nav>
</header>

Related

django search returns all entries upon double click

I created search tool within my website using some help from internet.
Problem is when I double click on seach button it brings all entries available, but work if you type whatever you search for. Code in views.py is below.
class SearchResultsView(ListView):
model = Entry
template_name = 'wpb/search.html'
def get_queryset(self): # новый
query = self.request.GET.get('q')
object_list = Entry.objects.filter(
Q(title__contains=query.upper()) | Q(link__icontains=query)
)
return object_list
Code for search.html is below:
<ul>
{% for entry in object_list %}
<li>
{{ entry.title }}
</li>
{% empty %}
<h4>Ничего не найдено.</h4>
{% endfor %}
</ul>
Cuoldn't find anything similar in the internet.
You're using the __icontains filter, which will return all of the entries if you don't write anything in the form. This is because you're sending something like this to your database:
SELECT ... WHERE headline ILIKE '%%';
This will query everything. To fix this, you can use some JavaScript to disable the search when the user doesn't write anything in the input.

Creating links to a page that displays specific rows in my database w/ django?

I created a watered down project based of my more complex project for this question to help deliver my question more effectively. I'll include the code below for future viewers of this post but for ease/convenience here is the gitlab repository url.
I have a model "NotesModel" that models the architecture of a note that you might take during the day. It's quite simple there's a 'title', 'tag' and of course the main part which I call the 'content'. The tag is like a tag you link to your stack overflow post. It's just to help identify the topic that a particular note might be about.
In my function based view I query every row in my database and hand it off to my render(.., .., {'notes': notes}) function as you'll see below. With some html/bootstrap styling I display every item from the 'tag' column in my database as a label which is linkable with the ...a href=""... syntax as such.
{% for note in notes %}
<span class="label label-primary" id="tags">
<a id="theurl" href="this is where my confusion is">
{{note.tag}}
</a>
</span>
{% endfor %}
I entered some basic notes already and taken a screen-shot to help show you what my page looks like in hope that it better explains the behavior I want which I explain next.
Screen-shot
The behavior that I want is to treat this label as a actual tag you might find on some forum website. The behavior is as follows...
1) The user clicks on the label
2) Django directs the user to another page
3) The rows in the database that the attribute 'tag' (being the column) matches the name of the label will be displayed.
Let me explain a bit more. From the picture above you can see there are already four notes (being four rows in database terminology) entered into the database. Therefore, the 'tag' attribute (being the 'tag' column in database terminology) has four instances being starwars, startrek, avp, and another starwars instance. I want to be able to click on the tag starwars and it directs me to another page that displays all notes with the 'tag' starwars. Same goes for the other tags. If I click on startrek then I want this to direct me to another page that displays all notes with the 'tag' startrek.
I did at one point try creating another page in my templates folder and then used a query set filter like the following that I passed off to the template.
queryset = NotesModel.objects.filter(tag__icontains='starwars')
Then I just typed the direct link to that page in the ...a href=""... piece of code. However, there's two problems with this solution...
1 It only works for starwars
2 If i did it this way I would have to create x number of page.html files in my templates folder with an equal amount of x number of function based views with the above queryset. This is more static and not a dynamic way of doing things so how do I achieve such a task?
The following are files in my project as of now. As I said earlier i'm including the gitlab repository url above should you want to pull the project down yourself.
the_app/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name='home'),
]
the_app/models.py
from django.db import models
class NotesModel(models.Model):
title = models.CharField(max_length=100, blank=True)
tag = models.CharField(max_length=100, blank=False)
content = models.CharField(max_length=1000, blank=False)
def __str__(self):
return self.title
the_app/forms.py
from django import forms
from .models import NotesModel
class NotesForm(forms.ModelForm):
class Meta:
model = NotesModel
fields = ['title', 'tag', 'content']
the_app/views.py
from django.shortcuts import render
from .models import NotesModel
from .forms import NotesForm
def home(request):
# Grab the form data
if request.method == 'POST':
form = NotesForm(request.POST)
if form.is_valid():
form.save()
form = NotesForm()
else:
form = NotesForm()
# Grab the data from the database
notes = NotesModel.objects.all()
return render(request, 'the_app/page_home.html', {'form': form, 'notes': notes})
the_app/templates/the_app/base.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="{% static 'mystyle.css' %}">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.min.js"></script>
</head>
<body>
<div>
{% block notesform %}{% endblock notesform %}
</div>
<div>
{% block notetags %}{% endblock notetags %}
</div>
</body>
</html>
the_app/templates/the_app/page_home.html
{% extends "the_app/base.html" %}
{% block notesform %}
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form }}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
{% endblock notesform %}
{% block notetags %}
{% for note in notes %}
<span class="label label-primary" id="tags">
<!-- I didn't know what to put here so i used google's url as a place holder-->
<a id="theurl" href="https://www.google.com">
{{note.tag}}
</a>
</span>
{% endfor %}
{% endblock notetags %}
Here is the problem, queryset = NotesModel.objects.filter(tag__icontains='starwars') your tag is only containing starwars. So it will not find other tags.
class TagPostView(ListView):
model = NoteModel
template_name = '....'
context_object_name = 'all_notes_of_this_tag'
def get_queryset(self):
result = super(PostTagView, self).get_queryset()
query = self.request.GET.get('q')
if query:
postresult = NoteModel.objects.filter(tag__icontains=query)
result = postresult
else:
result = None
return result
Now you can pass label as q and it will search for that tag and show the result. All you need is only one template for this.
In template you can use,
{% for note in all_notes_of_this_tag %}
{{note.title}}
#..
{% endfor %}
This will solve all three requirements.

How to display {% django block %} dynamically with for loop?

Hello Awesome People!
I'm building a dashboard for my website, in the left section, I have a menu containing multiple items : Home, Messenger, Job Offers ....
When creating an account, Users are able to choose their items, re-order or remove.
I have Menu & Item models,
class Menu(models.Model):
items = models.ManyToManyField('Item',blank=True)
class Item(models.Model):
name = models.CharField(max_length=100)
unique_key = models.CharField(max_length=100,unique=True)
url = ......
For the menu of the user, in my base_template I display the items of the current users with for loop. With a CSS class I want to highlight the current view, the current item.
When the user visits his messenger, I want to highlight that item messenger with a CSS class.
<ul>
{% for item in user.menu.items.all %}
<li class='{% block item.unique_key %}{% endblock %}'>
{{item.name}}
</li>
{% endfor %}
</ul>
Now, when being in messenger.html for example, knowing that I have a unique_key for messenger called item_messenger, I do
{% block 'item_messenger' %}active{% endfor%}
It's not working, the item Messenger or whatever I choose doesn't become highlighted, I wonder why?
Is there another way to achieve that?
Any hint will be helpful, thanks in advance!
You are trying to have the block tag interpret a variable as it's name. That doesn't work.
You will need to pass along the active item's key in the template context and then in your base_template you can just do a simple if statement to add the active class to the correct entry.
<ul>
{% for item in user.menu.items.all %}
<li class='{% if item.unique_key == active_key %}active{% endif %}'>
{{item.name}}
</li>
{% endfor %}
</ul>

Factorizing a header menu in Django template

I'm building a website using django with a header on top of every page, which basically is a menu with a few links, constant throughout the pages.
However, depending on the page you're on I'd like to highlight the corresponding link on the menu by adding the class "active". To do so, I am currently doing as follow: each page has a full menu block that integrates within a general layout, which does NOT contain the menu. For exemple, page2 would look like this:
{% extends "layout.html" %}
{% block menu %}
<li>Home</li>
<li>page1</li>
<li class="active">page2</li>
<li>page3</li>
{% endblock %}
The problem is that, beside from that solution being not so pretty, every time I want to add a link to the header menu I have to modify each and every page I have. Since this is far from optimal, I was wondering if any of you would know about a better way of doing so.
Thanks in advance!
You can create a custom templatetag:
from django import template
from django.core.urlresolvers import reverse, NoReverseMatch, resolve
register = template.Library()
#register.simple_tag
def active(request, view_name):
url = resolve(request.path)
if url.view_name == view_name:
return 'active'
try:
uri = reverse(view_name)
except NoReverseMatch:
uri = view_name
if request.path.startswith(uri):
return 'active'
return ''
And use it in the template to recognize which page is loaded by URL
<li class="{% active request 'car_edit' %}">Edit</li>
If you have a "page" object at every view, you could compare a navigation item's slug to the object's slug
navigation.html
<ul>
{% for page in navigation %}
<li{% ifequal object.slug page.slug %} class="active"{% endifequal %}>
{{ page.title }}
</li>
{% endfor %}
</ul>
base.html
<html>
<head />
<body>
{% include "navigation.html" %}
{% block content %}
Welcome Earthling.
{% endblock %}
</body>
</html>
page.html
{% extends "base.html" %}
{% block content %}
{{ object }}
{% endblock %}
Where navigation is perhaps a context_processor variable holding all the pages, and object is the current PageDetailView object variable
Disclaimer
There are many solutions for your problem as noted by Paulo. Of course this solution assumes that every view holds a page object, a concept usually implemented by a CMS. If you have views that do not derive from the Page app you would have to inject page pretenders within the navigation (atleast holding a get_absolute_url and title attribute).
This might be a very nice learning experience, but you'll probably save loads time installing feinCMS or django-cms which both define an ApplicationContent principle also.
You may use the include tag and pass it a value which is the current page.
For example, this may be a separate file for declaring the menu template only:
menu.html
{% if active = "a" %}
<li>Home</li>
{% if active = "b" %}
<li>page1</li>
{% if active = "c" %}
<li class="active">page2</li>
{% if active = "d" %}
<li>page3</li>
And call this from within your template like this:
{% include 'path/to/menu.html' with active="b"%} # or a or c or d.
Hope it helps!

How to implement breadcrumbs in a Django template?

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.
Note: I provide the full snippet below, since djangosnippets has been finicky lately.
Cool, someone actually found my snippet :-) 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 %}
DaGood breadcrumbs snippet
Provides two template tags to use in your HTML templates: breadcrumb and breadcrumb_url. The first allows creating of simple url, with the text portion and url portion. Or only unlinked text (as the last item in breadcrumb trail for example). The second, can actually take the named url with arguments! Additionally it takes a title as the first argument.
This is a templatetag file that should go into your /templatetags directory.
Just change the path of the image in the method create_crumb and you are good to go!
Don't forget to {% load breadcrumbs %} at the top of your html template!
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 %s" % (crumb, title)
return crumb
The Django admin view modules have automatic breadcumbs, which are implemented like this:
{% block breadcrumbs %}
<div class="breadcrumbs">
{% trans 'Home' %}
{% block crumbs %}
{% if title %} › {{ title }}{% endif %}
{% endblock %}
</div>
{% endblock %}
So there is some kind of built-in support for this..
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.
Try django-breadcrumbs — a pluggable middleware that add a breadcrumbs callable/iterable in your request object.
It supports simple views, generic views and Django FlatPages app.
I had the same issue and finally I've made simple django tempalate tag for it: https://github.com/prymitive/bootstrap-breadcrumbs
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.
Try django-mptt.
Utilities for implementing Modified Preorder Tree Traversal (MPTT) with your Django Model classes and working with trees of Model instances.
This answer is just the same as #Andriy Drozdyuk (link). I just want to edit something so it works in Django 3.2 (in my case) and good in bootstrap too.
for create_crumb function (Remove the ">" bug in the current code)
def create_crumb(title, url=None):
"""
Helper function
"""
if url:
crumb = '<li class="breadcrumb-item">{}</li>'.format(url, title)
else:
crumb = '<li class="breadcrumb-item active" aria-current="page">{}</li>'.format(title)
return crumb
And for __init__ in BreadcrumbNode, add list() to make it subscriptable. And change smart_unicode to smart_text in render method
from django.utils.encoding import smart_text
class BreadcrumbNode(Node):
def __init__(self, vars):
"""
First var is title, second var is url context variable
"""
self.vars = list(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_text(title)
And add this in base.html for the view for Bootstrap. Check the docs
<nav style="--bs-breadcrumb-divider: '>';" aria-label="breadcrumb">
<ol class="breadcrumb">
{% block breadcrumbs %}
{% endblock breadcrumbs %}
</ol>
</nav>
Obviously, no one best answer, but for practical reason I find that it is worth considering the naïve way. Just overwrite and rewrite the whole breadcrumb... (at least until the official django.contrib.breadcrumb released )
Without being too fancy, it is better to keep things simple. It helps the newcomer to understand. It is extremely customizable (e.g. permission checking, breadcrumb icon, separator characters, active breadcrumb, etc...)
Base Template
<!-- File: base.html -->
<html>
<body>
{% block breadcrumb %}
<ul class="breadcrumb">
<li>Dashboard</li>
</ul>
{% endblock breadcrumb %}
{% block content %}{% endblock content %}
</body>
</html>
Implementation Template
Later on each pages we rewrite and overwrite the whole breadcrumb block.
<!-- File: page.html -->
{% extends 'base.html' %}
{% block breadcrumb %}
<ul class="breadcrumb">
<li>Dashboard</li>
<li>Level 1</li>
<li class="active">Level 2</li>
</ul>
{% endblock breadcrumb %}
Practicallity
Realworld use cases:
Django Oscar: base template, simple bread
Django Admin: base template, simple bread, permission check breadcrumb
You could also reduce the boiler plate required to manage breadcrumbs using django-view-breadcrumbs, by adding a crumbs property to the view.
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 %}
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>{{ breadcrumb.slug }}</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 :)
You might want to try django-headcrumbs (don’t worry, they are not going to eat your brains).
It’s very lightweight and absolutely straightforward to use, all you have to do is annotate your views (because defining crumbs structure in templates sounds crazy to me) with a decorator that explains how to get back from the given view.
Here is an example from the documentation:
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.
There are also some utility functions (e.g. name_from_pk you can see in the example) that automagically generate nice names for your crumbs without you having to wright lots of code.
I've created template filter for this.
Apply your custom filter (I've named it 'makebreadcrumbs') to the request.path like this:
{% with request.resolver_match.namespace as name_space %}
{{ request.path|makebreadcrumbs:name_space|safe }}
{% endwith %}
We need to pass url namespace as an arg to our filter.
Also use safe filter, because our filter will be returning string that needs to be resolved as html content.
Custom filter should look like this:
#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 = '{}'.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 = '{}'.format(reverse(arg+':' + c), c)
my_crumbs.append(link)
return ' > '.join(my_crumbs)
# return whole list of crumbs joined by the right arrow special character.
Important:
splited parts of the 'value' in our filter should be equal to the namespace in the urls.py, so the reverse method can be called.
Hope it helped.
A generic way, to collect all callable paths of the current url could be resolved by the following code snippet:
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}
If the resolve function can't get a real path from any urlpattern, the Resolver404 exception will be thrown. For those items we set the is_representative flag to false. The OrderedDict breadcrumb_config holds after that the breadcrumb items with there configuration.
For bootstrap 4 breadcrumb for example, you can do something like the following in your template:
<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>
Only the links which won't raises a 404 are clickable.
I believe there is nothing simpler than that (django 3.2):
def list(request):
return render(request, 'list.html', {
'crumbs' : [
("Today", "https://www.python.org/"),
("Is", "https://www.python.org/"),
("Sunday", "https://www.djangoproject.com/"),
]
})
Breadcrumbs.html
<div class="page-title-right">
<ol class="breadcrumb m-0">
{% if crumbs %}
{% for c in crumbs %}
<li class="breadcrumb-item {{c.2}}">{{c.0}}</li>
{% endfor %}
{% endif %}
</ol>
</div>
css:
.m-0 {
margin: 0!important;
}
.breadcrumb {
display: flex;
flex-wrap: wrap;
padding: 0 0;
margin-bottom: 1rem;
list-style: none;
border-radius: .25rem;
}
dl, ol, ul {
margin-top: 0;
margin-bottom: 1rem;
}
ol, ul {
padding-left: 2rem;
}