views:

365

answers:

3

I'm planning to create a website using django that will have a common header throughout the entire website. I've read django's documentation on templating inheritance, but I can't seem to find an elegant solution for the "dynamic" elements in my header.

For example, the header in the website will include tabs, say similar to http://www.google.com/ (where it has "Web", "Images", etc), where the selected tab will describe your current location in the website.

Using the django template inheritance, it would seem like you would create a base template like this:

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <link rel="stylesheet" href="style.css" />
    <title>{% block title %}My Amazing Site{% endblock %}</title>
</head>
<body>
<div id="header">
    {% block header %}
     .... html to create tabs ...
    {% endblock header %}
</div>

and then in all of my other pages, i would do this:

{% extends "base.html" %}
{% block header % }
 .... html to create tabs with one tab "selected" ...
{% endblock header %}

which seems annoying as every single one of my pages would have to have duplicated HTML with the header information, but slightly different. So when its time to add a new tab, i have to modify every single HTML file.

Upon further reading, it seems like some other possible solutions are:

1 - Create a custom template tag that takes in which tab is currently selected, that way in each HTML page i just call: {% block header %} {% mycustomtag abc %} {% endblock header %}

I don't like this solution because it would requiring placing HTML into the python code for creating this custom tag.

2 - Create X number of sub-templates of base.html, all with the appropriate tab selected. Then each page would inherit from the appropriate sub-template based on which tab they want selected.

This solution seems fine, except for the fact that it will require X number of almost exactly the same HTML, and still runs into the issue of having to modify all the files when a tab is added or removed.

3 - Use javascript (like jquery) to modify the header on page load to "select" the correct tab.

This solution is fine but then would require one to remember to add this functionality to every page's javascript. the good part is that the header HTML would only live in a single HTML file.

Any other suggestions?

Thanks!

A: 

A version of #1 will do the trick — with a separate template file for the tag.

Lets say you have the models "Category" and "Article".

class Category(models.Model):
    title           = models.CharField(_("Name"), max_length=200)
    introduction    = models.TextField(blank=True, null=True)
    slug            = models.SlugField(help_text=_("Used for URLs"))
    sort_order      = models.IntegerField(_("Sortierung"))

class Article(models.Model):
    title = models.CharField(_("Full Name"), max_length=255)
    slug = models.SlugField(_("Slug Name"), unique=True, help_text=_("This is a short, descriptive name of article that will be used in the URL link to this item"))
    text = models.TextField(_("Text of Article"), blank=True, null=True)
    category = models.ForeignKey(Category)

in your views you would pass the category you are viewing to the template:

@render_to('cat_index.html')
def category_view(request,string):

    cat = Category.objects.get(slug=string)
    articles = Article.objects.filter(category = cat).order_by('date')
    return {
             'articles':articles,
             'category':cat,
             }

(Note: using the annoying render_to-decorator – same as render_to_response)

and in your template you call a inclusion_tag like this:

@register.inclusion_tag('snippets/navigation.html')
def navigation(cat=None):
    return {'cats':Category.objects.order_by('sort_order'),
            'cat':cat
            }

by using this in your base-template (often called base.html)

{% navigation category %}

Now in the inclusions_tags's template (snippets/navigation.html) you would for-loop over cats and if one of it equals cat you can assign other styles

 <ul> 
   {% for c in cats %}
       <li{% ifequal c cat %} class="active"{% endifequal %}>
           <a href="{{c|url}}">{{ c }}</a>
       </li>
   {% endfor %}
 </ul>
vikingosegundo
+6  A: 

I'm assuming each tab is a list item in your template base.html.

<ul>
    <li>Tab 1</li>
    <li>Tab 2</li>
    ...
</ul>

Add an extra block to each li.

<ul>
    <li class="{% block class_tab1 %}inactive{% endblock %}">Tab 1</li>
    <li class="{% block class_tab2 %}inactive{% endblock %}">Tab 2</li>
    <li class="{% block class_tab3 %}inactive{% endblock %}">Tab 3</li>
    ...
</ul>

Then in your template if tab 1 is to be selected:

{% extends "base.html" %}

{% block class_tab1 %}active{% endblock %}
...

So the html rendered for Tab 1 is:

<ul>
    <li class="active">Tab 1</li>
    <li class="inactive">Tab 2</li>
    <li class="inactive">Tab 3</li>
    ...
</ul>

and you can write CSS to target the li .active as you wish.

Alasdair
+1  A: 

This is a rather common problems and I've come up with some various ways to solve it.

Since you're asking for options, here's 3 other alternative ways achieve this effect. The options you mentioned and these listed below all have thier positives and negatives. It's really up to you to decide which is a best fit.

Alternate 1 - Use Regular Expressions and a Hash Table

This could be performed either client-side (less advantageous) or server-side (a better pick). To do this you could have a tag that had 1 input: a regular expression. In use it would look like this...

// In base.html...

<li class="tab {% is_tab_active r'^/cars/' %}"><a>Cars</a></li>
<li class="tab {% is_tab_active r'^/trucks/' %}"><a>Trucks</a></li>

The custom tag applies the regular expression against the current page being viewed. If successfull, it adds a css class "active" if not "inactive" (or whatever your CSS classes are).

I've been pondering this method for a while. I feel as if there should be some good way to come up with a way to tie it into urls.py, but I haven't seen it yet.

Alternate 2 - Use CSS

If you were to identify each [body] tag, or at least have a common template for the sections of your site, CSS could be used to assign which was active. Consider the following:

body.cars_section .navigation #cars_tab { color: #00000; }
body.truck_section .navigation #trucks_tab { color: #00000; }

For your base template...

<body class="{% block category %}{% endblock %}">

...

<ul class="navigation">
    <li id="cars_tab"><a>Cars</a></li>
    <li id="trucks_tab"><a>Trucks</a></li>

Then for any page you simply put the category it's a part of (matching the CSS rule)...

{% extends "base.html" %}

...

{% block category %}cars_section{% endblock %}

Alternate 3 - Have Some Bloated Middleware

Django lets you write Middleware to affect the behavior of just about whatever you want. This seems like a bloated and complex route with potential negative performance impact, but I figured I'd at least mention it as an option.

T. Stone
I'm using "Alternate 2 - Use CSS" and it works great. I think this is the best way to go about it. Instead of having several blocks of logic, you only have one!Thanks!
Idris