How to render menu with one active item with DRY? - django

I would like to render a constructions like:
<a href='/home'>Home</a>
<span class='active'>Community</span>
<a href='/about'>About</a>
Where Community is selected menu item. I have menu with same options for several templates but I would not like to create combinations for each template:
<!-- for Home template-->
<span class='active'>Home</span>
<a href='/comminuty'>Community</a>
<a href='/about'>About</a>
...
<!-- for Community template-->
<a href='/home'>Home</a>
<span class='active'>Community</span>
<a href='/about'>About</a>
...
<!-- for About template-->
<a href='/home'>Home</a>
<a href='/community'>Community</a>
<span class='active'>About</span>
We have permanent list of menu items, so, it can be more effective way - to create only one generalized structure of menu then render menu with required option for template.
For example it could be a tag that allows to do that.

Figured out another way to do it, elegant enough thanks to this answer : https://stackoverflow.com/a/17614086/34871
Given an url pattern such as:
url(r'^some-url', "myapp.myview", name='my_view_name'),
my_view_name is available to the template through request ( remember you need to use a RequestContext - which is implicit when using render_to_response )
Then menu items may look like :
<li class="{% if request.resolver_match.url_name == "my_view_name" %}active{% endif %}">Shortcut1</li>
<li class="{% if request.resolver_match.url_name == "my_view_name2" %}active{% endif %}">Shortcut2</li>
etc.
This way, the url can change and it still works if url parameters vary, and you don't need to keep a list of menu items elsewhere.

Using template tag
You can simply use the following template tag:
# path/to/templatetags/mytags.py
import re
from django import template
try:
from django.urls import reverse, NoReverseMatch
except ImportError:
from django.core.urlresolvers import reverse, NoReverseMatch
register = template.Library()
#register.simple_tag(takes_context=True)
def active(context, pattern_or_urlname):
try:
pattern = '^' + reverse(pattern_or_urlname)
except NoReverseMatch:
pattern = pattern_or_urlname
path = context['request'].path
if re.search(pattern, path):
return 'active'
return ''
So, in you your template:
{% load mytags %}
<nav><ul>
<li class="nav-home {% active 'url-name' %}">Home</li>
<li class="nav-blog {% active '^/regex/' %}">Blog</li>
</ul></nav>
Using only HTML & CSS
There is another approach, using only HTML & CSS, that you can use in any framework or static sites.
Considering you have a navigation menu like this one:
<nav><ul>
<li class="nav-home">Home</li>
<li class="nav-blog">Blog</li>
<li class="nav-contact">Contact</li>
</ul></nav>
Create some base templates, one for each session of your site, as for example:
home.html
base_blog.html
base_contact.html
All these templates extending base.html with a block "section", as for example:
...
<body id="{% block section %}section-generic{% endblock %}">
...
Then, taking the base_blog.html as example, you must have the following:
{% extends "base.html" %}
{% block section %}section-blog{% endblock %}
Now it is easy to define the actived menu item using CSS only:
#section-home .nav-home,
#section-blog .nav-blog,
#section-contact .nav-contact { background-color: #ccc; }

I found easy and elegant DRY solution.
It's the snippet:
http://djangosnippets.org/snippets/2421/
**Placed in templates/includes/tabs.html**
<ul class="tab-menu">
<li class="{% if active_tab == 'tab1' %} active{% endif %}">Tab 1</li>
<li class="{% if active_tab == 'tab2' %} active{% endif %}">Tab 2</li>
<li class="{% if active_tab == 'tab3' %} active{% endif %}">Tab 3</li>
</ul>
**Placed in your page template**
{% include "includes/tabs.html" with active_tab='tab1' %}

You could make a context variable links with the name, URL and whether it's an active item:
{% for name, url, active in links %}
{% if active %}
<span class='active'>{{ name }}</span>
{% else %}
<a href='{{ url }}'>{{ name }}</a>
{% endif %}
{% endfor %}
If this menu is present on all pages, you could use a context processor:
def menu_links(request):
links = []
# write code here to construct links
return { 'links': links }
Then, in your settings file, add that function to TEMPLATE_CONTEXT_PROCESSORS as follows: path.to.where.that.function.is.located.menu_links. This means the function menu_links will be called for every template and that means the variable links is available in each template.

I have come up with a way to utilize block tags within menu-containing parent template to achieve something like this.
base.html - the parent template:
Home
About
Contact
{% block content %}{% endblock %}
about.html - template for a specific page:
{% extends "base.html" %}
{% block menu_about_class %}active{% endblock %}
{% block content %}
About page content...
{% endblock %}
As you can see, the thing that varies between different page templates is the name of the block containing active. contact.html would make use of menu_contact_class, and so on.
One benefit of this approach is that you can have multiple subpages with the same active menu item. For example, an about page might have subpages giving information about each team members of a company. It could make sense to have the About menu item stay active for each of these subpages.

Here ist my solution:
{% url 'module:list' as list_url %}
{% url 'module:create' as create_url %}
<ul>
<li>List Page</li>
<li>Creation Page</li>
</ul>

Assuming the nav item is a link with the same URL as the current page, you could just use JavaScript. Here's an annotated method that I use to add a class="active" to a li in a navigation menu with class="nav":
// Get the path name (the part directly after the URL) and append a trailing slash
// For example, 'http://www.example.com/subpage1/sub-subpage/'
// would become '/subpage1/'
var pathName = '/' + window.location.pathname.split('/')[1];
if ( pathName != '/' ) { pathName = pathName + '/'; }
// Form the rest of the URL, so that we now have 'http://www.example.com/subpage1/'
// This returns a top-level nav item
var url = window.location.protocol + '//' +
window.location.host +
pathName;
console.log(url);
// Add an 'active' class to the navigation list item that contains this url
var $links = document.querySelectorAll('.nav a');
$link = Array.prototype.filter.call( $links, function(el) {
return el.href === url;
})[0];
$link.parentNode.className += ' active';
This method means you can simply pop it into your base template once and forget about it. No repetition, and no manual specification of the page URL in each template.
One caveat: this obviously only works if the url found matches a navigation link href. It would additionally be possible to specify a couple of special use cases in the JS, or target a different parent element as needed.
Here's a runnable example (keep in mind, snippets run on StackSnippets):
// Get the path name (the part directly after the URL) and append a trailing slash
// For example, 'http://www.example.com/subpage1/sub-subpage/'
// would become '/subpage1/'
var pathName = '/' + window.location.pathname.split('/')[1];
if ( pathName != '/' ) { pathName = pathName + '/'; }
// Form the rest of the URL, so that we now have 'http://www.example.com/subpage1/'
// This returns a top-level nav item
var url = window.location.protocol + '//' +
window.location.host +
pathName;
console.log(url);
// Add an 'active' class to the navigation list item that contains this url
var $links = document.querySelectorAll('.nav a');
$link = Array.prototype.filter.call( $links, function(el) {
return el.href === url;
})[0];
$link.parentNode.className += ' active';
li {
display: inline-block;
margin: 0 10px;
}
a {
color: black;
text-decoration: none;
}
.active a {
color: red;
}
<ul class="nav">
<li>
Example Link
</li>
<li>
This Snippet
</li>
<li>
Google
</li>
<li>
StackOverflow
</li>
</ul>

I ran into this challenge today with how to dynamically activate a "category" in a sidebar. The categories have slugs which are from the DB.
I solved it by checking to see category slug was in the current path. The slugs are unique (standard practice) so I think this should work without any conflicts.
{% if category.slug in request.path %}active{% endif %}
Full example code of the loop to get the categories and activate the current one.
{% for category in categories %}
<a class="list-group-item {% if category.slug in request.path %}active{% endif %}" href="{% url 'help:category_index' category.slug %}">
<span class="badge">{{ category.article_set.count }}</span>
{{ category.title }}
</a>
{% endfor %}

Based on #vincent's answer, there is an easier way to do this without messing up with django url patterns.
The current request path can be checked against the rendered menu item path, and if they match then this is the active item.
In the following example I use django-mptt to render the menu but one can replace node.path with each menu item path.
<li class="{% if node.path == request.path %}active{% endif %}">
node.title
</li>

I am using an easier and pure CSS solution. It has its limitations, of which I know and can live with, but it avoids clumsy CSS class selectors, like this:
index
Because a space character before active is missing the class selector gets called itemactive instead of item active and this isn't exactly too difficult to get wrong like that.
For me this pure CSS solution works much better:
a.item /* all menu items are of this class */
{
color: black;
text-decoration: none;
}
a.item[href~="{{ request.path }}"] /* just the one which is selected matches */
{
color: red;
text-decoration: underline;
}
Notice: This even works if the URL has additional path components, because then href also matches partially. That could eventually cause 'collisions' with more than one match, but often enough it just works, because on well structured websites a "subdirectory" of an URL usually is a child of the selected menu item.

I personally find the simplest way is to create blocks for each link like so:
# base.py
...
<a href="{% url 'home:index' %}" class={% block tab1_active %}{% endblock %}>
...
<a href="{% url 'home:form' %}" class={% block tab2_active %}{% endblock %}>
...
And then in each relative template declare that link as "active" e.g.:
tab1 template:
{% block tab1_active %}"active"{% endblock %}
tab2 template:
{% block tab2_active %}"active"{% endblock %}

Simply use template tags
# app/templatetags/cores.py
from django import template
from django.shortcuts import reverse
register = template.Library()
#register.simple_tag
def active(request, url, classname):
if request.path == reverse(url):
return classname
return ""
Do like this in your template
{% load cores %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Example</title>
</head>
<body>
<div>
myUrl
</div>
</body>
</html>
Have fun 😎

Related

Django ChoiceField RadioSelect widget in form, test whitch element selected in template

I have 2 radio buttons in a ChoiceField and I would like to display some parts of the template, depending of witch radio button is selected.
Following :
form.py
class CtdForm(forms.Form):
protocol_name = forms.CharField(max_length=100)
rb = forms.BooleanField(required=False, label='RB')
mr = forms.BooleanField(required=False, label='MR')
CHOICES = [('rb' ,'RB'), ('mr', 'MR')]
analyse_type = forms.ChoiceField(choices=CHOICES, widget=forms.RadioSelect)
template html
...
{{ form.analyse_type }}
Here I would like to test which button is selected and display the template depending of the selection
something like : {% if form.analyse_type.? == true %}
...
I test a lot of syntaxe with form.analyse_type.? like form.analyse_type.field.widget.choices to have each choices in a loop ect. , but I do not found the right one returning the selected radiobutton...
Maybe this way is not the right one to do what I want.
If you have any idea, solution thank you ;)
Edit for user2497126
Thanks for all tips :) !
I have an error Uncaught Error: GET_ELEMENTS: -> form[data-sb-form-api-token] seems to be in link with the object analyse_type
As requested, following a print screen with the html element.I tired different synthaxe like
let radioValue =$("input[name='analyse_type']:checked").value();
I also put my HTML code in case
{% extends "index.html" %}
{% load static %}
{% block content %}
<div style="padding-left: 30%;" class="col-lg-6 col-xl-6"
<h2 class="h4 fw-bolder">
Analyse Type
</h2>
<br>
{{ form.analyse_type }}
<br> </div>
<div id="template-one" style="display:none;">
<div style="padding-right: 30%;" class="col-lg-6 col-xl-6">
<h2 class="h4 fw-bolder">
TEST
</h2>
</div>
</div>
{% endblock %}
{% block javascript %}
<script type="text/javascript">
$(document).ready(function () {
$(".custom-control-input").change(function () {
let radioValue = $("input[name='analyse_type']:checked").value();
let templateOne = document.getElementById('template-one')
if (radioValue == "rb") {
$("#template-one").show();
} else {
$("#template-one").hide();
}
});
});
</script>
{% endblock javascript %}
EDIT
if I do a
console.log($("input[name='analyse_type']:checked").val())
in the debugger of chrome I have a return of rb.
But the error is still there with no result.
I also change the html, include the template-one in the same div of the form like your example
The problem seems here
$(".custom-control-input").change(function () {
I replace .custom by select or select#id_analyse_type, based on some forum answers, but I have no result
Thanks for your time and your help :)
EDIT
Here the solution :
{% block javascript %}
<script type="text/javascript">
$(document).ready(function () {
let radioValue = $("input[name='analyse_type']:checked").val();
if (radioValue == "rb") {
$("#template-one").show();
} else {
$("#template-one").hide();
}
$('#id_analyse_type').change(function () {
let radioValue = $("input[name='analyse_type']:checked").val();
if (radioValue == "rb") {
$("#template-one").show();
} else {
$("#template-one").hide();
}
});
});
</script>
{% endblock javascript %}
Thank you for your help !
Here is an edited answer which I have tried to adapt to your question
For the change function (i.e when selecting the radio) inspect and replace the class value with the one in your radio input. Also ensure you supply the correct name when instantiating the variable radioValue, I have used 'analyze_type' for the sake of the example but inspect and confirm the correct name value of the radio input.
You can achieve it using JS like this in your template
{% extends "base.html" %}
{% block title %}Form title{% endblock title %}
{% block content %}
<!-- page content -->
<div class="x_content">
{{ form.analyse_type}}
<div id="template-one" style="display:none;">
Template one
</div>
<div id="template-two" style="display:none;">
Template two
</div>
</div>
<!-- /page content -->
{% endblock %}
{% block javascript %}
<script>
$(document).ready(function(){
$(".custom-control-input").change(function(){
let radioValue = $("input[name='analyse_type']:checked").val();
let templateOne = document.getElementById('template-one')
if(radioValue == "RB"){
$("#template-one").show();
$("#template-two").hide();
}
else {
$("#template-two").show();
$("#template-one").hide();
}
});
});
</script>
{% endblock javascript %}

How to store nav menu urls in database?

I'm working on my first django app, and i have side nav menu like in twitter. To prevent the dozens of lines in my template like this
<ul class="nav d-flex flex-column">
<li class="nav-item align-self-start rounded-pill mb-3"><li>
<li class="nav-item align-self-start rounded-pill mb-3"><li>
...
<li class="nav-item align-self-start rounded-pill mb-3"><li>
</ul>
and for app extensibility i want to store nav menu in database to be able to loop over the menu items
<ul class="nav d-flex flex-column">
{% for item in menu %}
<li class="nav-item align-self-start rounded-pill mb-3"><li>
{% endfor %}
</ul>
But the problem is that i can't store direct urls for menu items in database, because several of them have dynamic urls e.g. profile page, which has 'slug:username/' pattern.
I've tried to store template tags in database like
{% url 'app_name:view_name' %}
but of course it doesn't work.
My current idea is to store in database namespaced url e.g. 'app_name:view_name' for static urls and 'request.user.get_absolute_url()' for pages which have username in urls.
The next step is to get QuerySet with menu items from database, loop over them and transform namespaces url with reverse (it works), but 'request.user.get_absolute_url()' is just a string and it doesn't work. Then make list of ditcs and pass it to context
menu = [{item1 attrs}, {item2 attrs},...,]
Is exists a better approach to solve my problem? And finally what i should do with dynamic urls?
UPD:
If we're dealing with menu items whose urls depend only on usernames i.e. only on User model, we can do smth like this (NavigationMenu - table model, url_link - column with following values: 'app_name:view_name' or 'get_absolute_url'):
menu = NavigationMenu.object.all()
for item in menu:
url_link = item.url_link
if ':' in item.url_link:
url = reverse(url_link)
else:
method = getattr(request.user, url_link)
url = method()
# then store all instance attrs in dict
But it seems that is not a good way to solve this.
The ideal approach to achieve this is to create a base.html in which you build this sidebar nav item, and then include that base.html on every page where you want that sidebar.
This is an example of base.html
<!DOCTYPE html>
<html>
<head>
<title>My Project</title>
</head>
<body>
<header>
<ul>
<li>Home</li>
<li>News</li>
</ul>
</header>
{% block content %}{% endblock content %}
</body>
</html>
Include base.html in other Html files
{% extends "base.html" %}
{% block content %}
<h2>Content for My App</h2>
<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry..</p>
{% endblock content %}

Single page design - loading new content on link click

I'm working on a django webpage using single page design approach. Generally what I'm trying to achieve is to have some new content being loaded (picture gallery) to my main webpage after clicking certain links.
What I have already achieved is that the new content is being loaded on a link click but unfortunately it seems like the whole page is being reloaded and not rendered correctly. My current implementation is based on having main index.html template and extension template, both using {% block content %} relation.
views.py
def index(request):
categories = Category.objects.all().order_by('name')
return render(request, 'index.html', {'categories': categories})
def gallery(request, id):
category = Category.objects.get(id=id)
return render(request, 'gallery.html', {'category': category})
urls.py
urlpatterns = [
path('', views.index, name='index'),
path('view_gallery/<int:id>/', views.gallery, name='view_gallery')
]
index.html
<div class="collapse navbar-collapse" id="collapsibleNavbar">
<ul class="navbar-nav">
{% for category in categories%}
<li class="nav-item">
<a class="nav-link" href="{% url 'view_gallery' category.id %}">{{ category.name }}</a>
</li>
{% endfor %}
</ul>
</div>
<div>
{% block content %}
{% endblock %}
</div>
gallery.html
{% extends 'index.html' %}
{% block content %}
<p>{{ category.name }}</p>
{% endblock %}
I hope I have explained clearly what I'm trying to achieve. Could you please point me in the right direction?
Requests and page changes in single page sites work quite differently than those for normal web pages. Single page sites use JavaScript to change the page content rather than requesting a new page from the server. Single page sites can request content from the server, but generally that content is just data while the structure (HTML) of the page is decided by the client in JavaScript. The only time a full HTML page is sent by the server is on the initial request, which should be responded to with index.html.
In your example, you could have this work by adding a script that requests content from the server and modifies the DOM when a link is clicked.
For example:
const a1 = document.querySelector("a.link1");
const a2 = document.querySelector("a.link2");
a1.addEventListener("click", () => {
setContent("<p>Content from link 1</p>")
});
a2.addEventListener("click", () => {
setContent("<p>Content from link 2</p>")
});
function setContent(content) {
const contentDiv = document.querySelector("div.content");
contentDiv.innerHTML = content;
}
a {
text-decoration: underline;
color: blue;
}
a:hover {
cursor: pointer;
}
<h1>My Page</h1>
<a class="link1">link 1</a>
<a class="link2">link 2</a>
<div class="content">
</div>
And the click event callbacks could request content from your server instead of having the content hard-coded as in this example. Note that then the server should respond with just a snippet of HTML rather than an entire new page.
For example, you could use the following function to get content for the div.content element:
function fetchData() {
const response = fetch("/gallery");
return response;
}
If you're new to single page sites, you might checkout a framework like React, Vue, or Angular to get started and gain a better understanding or even use for this project.

How do you dynamically display images in Django

I'm trying to dynamically display Images in Django. this is my details page
{% extends 'base.html' %}
{% load staticfiles %}
{% block header %}
<!-- Set your background image for this header on the line below. -->
<header class="intro-header" style="background-image: url('{% static 'blog/img/about-bg.jpg' %}')">
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<div class="page-heading">
<h1>{{ post.title }}</h1>
<hr class="small">
<span class="subheading">blog detail</span>
</div>
</div>
</div>
</div>
</header>
{% endblock %}
{% block content %}
<h1>{{ post.title }}</h1>
<h4>{{ post.body }}</h4>
{% lorem 5 p random %}
<hr>
<div id="disqus_thread"></div>
<script>
/**
* RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
* LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables
*/
/*
var disqus_config = function () {
this.page.url = PAGE_URL; // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
*/
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = '//eights.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the
comments powered by Disqus.
</noscript>
<script id="dsq-count-scr" src="//eights.disqus.com/count.js" async></script>
{% endblock %}
So far I tried storing these approaches. I tried storing this in the database
{% static 'blog/img/about-bg.jpg' %}
and called it like this
style="background-image: url('{{ post.title }}')"
that didn't work. Then I tried storing it in the database like this
'blog/img/about-bg.jpg'
and calling it like this
style="background-image: url('{% static '{{ post.title }}' %}')
then I ried storing it like this in the database
static/blog/img/about-bg.jpg
and calling it like this
style="background-image: url('{{ post.title }}')"
I've also tried defining it in the views.py
pic = "path/pic.img"
context = {
"pic": pic
context and calling it
{{pic }}
none of these methods work. It's a little different from Laravel. In laravel
path/{{ post->title }}
would have worked. How can I do this in Django? any and all suggestions are welcome. To be clear I want all my articles to display an image on the index page, then when I click one of them, I am taken to the details page that image for that particular article is displayed
I've figured it out. It's supposed to be stored as
/static/blog/img/about-bg.jpg
not
static/blog/img/about-bg.jpg
the forward slash makes it work. in Laravel this does not matter
From you question I understand that by dynamically you mean that you want to upload an image to your site. So it's not just a static image that is always the same like a logo of your page or something.
You have to do this:
In models.py
from django.contrib.sites.models import Site
def generate_filename(filename): #it adds the image in a folder with the current year
ext = filename.split('.')[-1]
year = datetime.datetime.now().year
return str(year) + '/' + str(int(time())) + '.' + ext
class PageWithImage(models.Model):
image = models.ImageField(upload_to=generate_filename, blank=True, null=True)
site = models.ForeignKey(Site, blank=True, null=True)#this if you want the image linked with your site
Then in setting.py you have to add:
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
MEDIA_ROOT = os.path.join(BASE_DIR, 'img')
MEDIA_URL = '/img/'
Then in the template:
<img itemprop="image" src="http://{{ object.site }}{{ object.image.url }}">
And don't forget to add the image field to admin.py
For adding images dynamically to your webpage using Django :
As we mostly use Jinja for templates,
<img src="{%static 'images/beach.svg' %}" alt="A beach image">
We give this kind of commands to access static image files. But for dynamic, we have to change the 'beach.svg' to something like {{dest2.img}} in the above HTML image tag, if my "views.py" function is like :
def index(request):
dest2 = Destination() // Imported class 'Destination()' from models.py .
dest2.name = 'Beach'
dest2.img = 'beach.svg' // The image name we need.
dest2.desc = 'Some beach description'
dest2.price = 2000
return render(request, 'index.html', {'dest2' : dest2}) // Passing the object value to html page.
If we logically think, the code should be like :
<img src="{%static 'images/{{dest2.img}}' %}" alt="A beach image"> // we cannot do this here!!!
We cannot use a Jinja code inside another Jinja code. So we add :
{% static 'images' as base_url %}
at the top of our HTML page. 'images' is the default folder for images and we are calling it as 'base_url' in this HTML page. So we have to use 'base_url' as path and 'dest2.img' as the file name. so the image source will be like :
<img src="{{base_url}}/{{dest2.img}}" alt="A beach image">
So finally we're done making the dynamic images in Django.!!!😋
First, you cannot use {% static 'blablabla' %} in CSS files.
Second, use this code:
style="background: url(/static/blog/img/about-bg.jpg) no-repeat"
Third, if you will be working with images from models in the future then your code should be:
style="background: url(/{{ your_object.your_img_field }}) no-repeat"

Customize count list

I have this code I'm using to generate a list of records categorized into year, make, series, body style, and color for vehicles. I'd like to customize this further this way:
for the year, I want to have only up to 2004 being individual...the rest will fall under other i.e. 2009, 2008, 2007, 2006, 2005, 2004, Other.
for the make, I want to display the six makes with the highest popularity...there's a field in the model I'm using to assign the popularity of a make with a value of primary (highest), secondary or tertiary. The rest will fall under Other.
For the body style and color, I want to have the items having less than 3 records falling under Other.
My code is as below:
year_count = vehicle_query.order_by(
'-common_vehicle__year__year').values('common_vehicle__year__year').
annotate(count=Count('id'))
make_count = vehicle_query.order_by(
'common_vehicle__series__model__manufacturer__manufacturer').
values('common_vehicle__series__model__manufacturer__manufacturer').
annotate(count=Count('id'))
style_count = vehicle_query.order_by(
'common_vehicle__body_style__style').values
('common_vehicle__body_style__style').annotate(count=Count('id'))
colour_count = vehicle_query.order_by(
'exterior_colour__exterior_colour').values(
'exterior_colour__exterior_colour').annotate(count=Count('id'))
The bulk of what you're asking would probably better be handled outside of Django and instead by client-side javascript. To be clear, you could have portions handled by Django, but it would be cleaner not doing so. There are benefits to doing it this way:
Your Django template code stays cleaner
It will degrade nicely
You can later update the interface (change the javascript) and not have to worry about breaking the Django template
To handle this you could simply make a script that when given a <ul> tag (and maybe some arguments) will render that list in the format you're asking about.
Here's a simple example using jQuery. For this example, I'm going to wrap the functionality in a using a jQuery plugin pattern.
Say your django template outputs the following...
<ul>
<li>Chevy</li>
<li>Mazda</li>
<li>Honda</li>
<li>Ford</li>
<li>BMW</li>
</ul>
jquery.showmorelist.js
(function($) {
$.fn.ShowMoreList = function(visibleItemCount) {
// Wrap parent element
var parent = $(this).wrap('<div class="show-more-list"></div>').parent();
var ul = $(this);
// Enumerate children and hide extras
var counter = 0;
$(this).children().filter('li').each(function(){
counter += 1;
if (counter > visibleItemCount) {
$(this).hide();
$(this).addClass('hidden');
}
});
// Add link and bind click
var link = $('> Show More').click(function(){
$(ul).children().filter('.hidden').show();
});
$(parent).append(link);
}
})(jQuery);
page.html
<script type="text/javascript" src="jquery.showmorelist.js"></script>
<script type="text/javascript">
// On page load...
$(function() {
$('ul').ShowMoreList(4); // Shows only the first 4 items
});
</script>
This is a rather simple example, and it won't switch the "Show More" to "Hide More" but you should be able to figure that out from the context.
I managed to get a solution, so I thought it'd be good to update the answer here:
In the head section I have this:
<script type="text/javascript" src="{{ MEDIA_URL }}share/jquery/jquery.min.js"></SCRIPT>
<script type="text/javascript">
(function($) {
$(document).ready(function() {
//hide the additional content under "Display More"
$("div.additional_content").hide();
$("a.more").click(function () {
//show or hide the additional content
$(this).siblings("div.additional_content").toggle();
//change the attributes and text value of the link toggle
if($(this).text() == "Display Less"){
$(this).removeClass("less");
$(this).addClass("more");
$(this).html("Display More");
}else{
$(this).removeClass("more");
$(this).addClass("less");
$(this).html("Display Less");
}
return false;
});
});
})(jQuery);
Then wherever I want to reduce the number of available options I have this:
<div class="module_wrap">
<div class="module"> {% if year_count %} <strong>{% trans "Year" %}</strong> <br />
{% for item in year_count|slice:":6" %}
<ul>
<li> {{ item.common_vehicle__year__year }} ({{ item.count }}) {% if request.session.chosen_year %} <img src="{{ MEDIA_URL }}img/undo.gif" border="0" alt="Remove Year Filter" title="Remove Year Filter" /> {% endif %} </li>
</ul>
{% endfor %}
<div class="additional_content"> {% for item in year_count|slice:"6:" %}
<ul>
<li> {{ item.common_vehicle__year__year }} ({{ item.count }})</li>
</ul>
{% endfor %} </div>
{% if year_count|slice:"6:" %}Display More<br />
{% endif %} <br />
</div>
</div>
{% endif %}