Django: Is there a better way to bold the current page link - django

I have a base.html template that contains a list of links.
Example:
<div id="sidebar1">
<ul>
<li>Index</li>
<li>Stuff</li>
<li>About Me</li>
<li>Contact Me</li>
</div>
Then I have in my views.py a definition for each of index.html, stuff.html, about.html and contact.html. Each of those templates simply derive from a base.html template and set their own respective titles and contents.
My question is about the above /stuff I have a class="current".
I'd like to make the current page that I'm on have that class attribute.
I could set a different variable in each view like current_page="about" and then do a compare in the template with {% ifequal %} in each class element of each link , but that seems like duplicating work (because of the extra view variable).
Is there a better way? Maybe if there is a way to get the view function name that the template was filled from automatically I would not need to set the extra variable? Also it does seem like a lot of ifequals.

Here's an elegant way to do this, which I copied from somewhere and I only wish I could remember where, so I could give them the credit. 8-)
I assign an id to each of my pages (or all the pages within a section) like this:
In index.html: <body id='section-intro'>...
In faq.html: <body id='section-faq'>...
In download.html: <body id='section-download'>...
And then an id for the corresponding links:
<li id='nav-intro'>Introduction</li>
<li id='nav-faq'>FAQ</li>
<li id='nav-download'>Download</li>
And the in the CSS I set a rule like this:
#section-intro #nav-intro,
#section-faq #nav-faq,
#section-download #nav-download {
font-weight: bold;
/* And whatever other styles the current link should have. */
}
So this works in a mostly declarative way to control the style of the link that the current page belongs in. You can see it in action here: http://entrian.com/source-search/
It's a very clean and simple system once you've set it up, because:
You don't need to mess about with template markup in your links
You don't end up using big ugly switch statements or if / else / else statements
Adding pages to a section Just Works [TM]
Changing the way things look only ever means changing the CSS, not the markup.
I'm not using Django, but this system works anywhere. In your case, where you "set their own respective titles and contents" you also need to set the body id, and there's no other Django markup required.
This idea extends easily to other situations as well, eg. "I want a download link in the sidebar on every page except the download pages themselves." You can do that in CSS like this:
#section-download #sidebar #download-link {
display: none;
}
rather than having to put conditional template markup in the sidebar HTML.

Haven't used Django, but I've dealt with the same issue in Kohana (PHP) and Rails.
What I do in Kohana:
<li><a href="/admin/dashboard" <?= (get_class($this) == 'Dashboard_Controller') ? "class=\"active\"" : NULL ?>>Dashboard</a></li>
<li><a href="/admin/campaigns" <?= (get_class($this) == 'Campaigns_Controller') ? "class=\"active\"" : NULL ?>>Campaigns</a></li>
<li><a href="/admin/lists" <?= (get_class($this) == 'Lists_Controller') ? "class=\"active\"" : NULL ?>>Lists</a></li>
What I do in Rails:
<li><a href="/main" <%= 'class="active"' if (controller.controller_name == 'main') %>>Overview</a></li>
<li><a href="/notifications" <%= 'class="active"' if (controller.controller_name == 'notifications') %>>Notifications</a></li>
<li><a href="/reports" <%= 'class="active"' if (controller.controller_name == 'reports') %>>Reports</a></li>

I see only a couple of ways of doing it, while avoiding repeated ifequals:
Javascript. Something along the lines of (jQuery):
var parts = window.location.pathname.split('/');
var page = parts[parts.length-1];
$('#sidebar1 a[href*=' + page + ']').addClass('current');
Change your views to contain a list of pages with their associated titles and URLs and create a {% for %} loop in your template, which will go through that list, and add a single {% ifequal %}.
Option 2 being the favorite from where I stand. If the logic for all of your pages is the same, and only the templates differ, you might consider using the FlatPages model for each of your pages. If the logic is different, and you need different models, you might consider using a menuing app of some sort. A shameless plug: I have a menuing app of my own

If you add the request context processor, it's pretty straightforward:
settings.py:
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.request',
'django.contrib.auth.context_processors.auth' # admin app wants this too
)
Now you have access to the HttpRequest, which contains the request path. Highlighting the current page is a simple matter of checking if the path matches the link's destination, i.e., you're already there:
<li><a class="{% if request.path == '/' %}current{% endif %}" href="/">Index</a></li>
<li><a class="{% if request.path == '/stuff/' %}current{% endif %}" href="/stuff/">Stuff</a></li>
<li><a class="{% if request.path == '/about/' %}current{% endif %}" href="/about/">About Me</a></li>
<li><a class="{% if request.path == '/contact/' %}current{% endif %}" href="/contact/">Contact Me</a></li>

Related

Django along with bootstrap navbar - distiinguishing link to current page

I use bootstrap for my django project.
I have a navbar like this:
[home][gallery][user]
How can I change the CSS properties of a specific navbar button to correspond to a currently opened page?
For example, if I am on the home page, the home button would be highlighted.
Solution 1
I usually have the navbar template gets included in all templates, each template should define what the page is this.
For example
# nav.html
<div class="..">
<div class="..">My Nav</div>
<ul class="..">
Home
Settings
</ul>
</div>
Then in each template you specify which should be active. something like this
# home.html
{% include "yourtemplatedir/nav.html" with active='home' %}
# settings.html
{% include "yourtemplatedir/nav.html" with active='settings' %}
Solution 2
Using context processor will make it sometimes easy
def get_current_path(request):
return {
'current_path': request.get_full_path()
}
In your template you can use {{ current_path }} to determine which nav item should be active.
You can also enhance the context processor code in order to check the prefix of the url and set the active_page variable automatically. so you don't need to set it with each include (In case you include the nav.html in your base always). However it really hard to have exceptions in here.

django context to built-in pages

I have a template that looks something like:
Main Template, home.html:
{% extends "framed.html" %}
<h2> stuff <h2>
framed.html looks something like
{% block header %}
<h1>{{ sitename }}</h1>
{% endblock %}
Normally when I call these views I give it a context with a context containing a key "sitename" assigned get_current_site().name, which works fine.
I also, however, would like to use framed.html at the top of a bunch of templates that are also called by django default views. For example:
return HttpResponseRedirect(reverse('django.contrib.auth.views.login'))
The top of that page never gets the {{sitename}} to show up, so I end up with some blank space at the top of my page. The same goes for flat pages, logouts, etc. Is there a way I can get the relevant context added to all of these "built-in" pages?
You can write your own template context processor that will add required variable in the parameters provided to each template.
More details at Writing your own context processors

Have separate template for each tab without having separate URL - Django

I'm trying to develop a reporting system using Django. I have to display reports about various categories of data.I have put each category as a tab-tab1,tab2, etc. Is it possible to have different template for each tab without having to change the url.
I have tried template inheritance but that requires have separate url for each tab.
My concern is that if the number of tabs grow, then the number of urls will also increase.
Any suggestions please?
Thanks in Advance.
Why is it a problem for the number of URLs to increase?
Presumably you don't need separate URLconf entries for each tab, you can just capture the tab name in the URL and send it on to the view:
url(r'^reports/(?P<tab_name>\w+)/$', views.reports, name='reports')
...
def reports(request, tab_name):
... do something depending on tab_name ...
You can just use {% include %} tag and include different templates.
And I think it's better to have unique URL for each tab, it least with hashtag.
You can use a library like jquery tabs to create the tabs, then load each template individually either through include as suggested by #DrTyrsa or by a custom template tag (which would be my personal preference).
Here is an example (from the excellent bootstrap framework from twitter):
<ul class="tabs">
<li class="active">Home</li>
<li>Profile</li>
<li>Messages</li>
<li>Settings</li>
</ul>
<div class="pill-content">
<div class="active" id="home">...</div>
<div id="profile">...</div>
<div id="messages">...</div>
<div id="settings">...</div>
</div>
<script>
$(function () {
$('.tabs').tabs()
})
</script>

Handlebars.js in Django templates

I need a javascript templating system and i think handlebars.js does an excellent job in this case.
I'm having syntax conflicts with handlebars templates inside a django template because django tries to render handlebars variables.
Is there a tag in django templates to stop rendering a block with curly braces?
Something like:
{{ django_context_varable }} #works
{{% raw %}}
<script id="restaurants-tpl" type="text/x-handlebars-template">
<ul>
{{#restaurants}} #not rendered by django, plain text
<li>{{name}}</li>
{{/restaurants}}
</ul>
</script>
{{% endraw %}}
Edit
Likely i found this. It works fine.
Update
Django 1.5 supports verbatim tag natively.
I use a custom template tag for another js templating system, here:
https://gist.github.com/629508
Use in template:
{% load mytags %}
{% verbatim %}
{{ This won't be touched by {% django's %} template system }}
{% endverbatim %}
Edit: This custom template tag is no longer necessary, as Django's template language now supports the {% verbatim %} template tag.
Is there a tag in django templates to stop rendering a block with curly braces?
OLD Answer for Django 1.0-1.4: No, though you could though you could put the block in a separate file and include it without rendering or use a different templating engine.
New Answer: The answer above was correct in August 2011 when the question was asked and answered. Starting in Django 1.5 (released Feb 2013, though alpha/beta versions in late 2012), they introduced the {% verbatim %} and {% endverbatim %} which will prevent the django template engine from processing the content in the block.
So for the question asked the following will work in django 1.5+ out of the box:
{{ django_context_varable }} #works
{% verbatim %}
<script id="restaurants-tpl" type="text/x-handlebars-template">
<ul>
{{#restaurants}} #not rendered by django, plain text
<li>{{name}}</li>
{{/restaurants}}
</ul>
</script>
{% endverbatim %}
The documentation on verbatim is here. Yes, this was noted by others earlier, but as this is the accepted answer I should list the easiest solution.
I wrote a very small django application : django-templatetag-handlebars exactly for that purpose.
{% load templatetag_handlebars %}
{% tplhandlebars "tpl-infos" %}
{{total}} {% trans "result(s)." %}
<p>{% trans "Min" %}: {{min}}</p>
<p>{% trans "Max" %}: {{max}}</p>
{% endtplhandlebars %}
Render your block as usual using Handlebars.js API :
var properties = {
total: 10,
min: 5,
max: 4
};
var template = Handlebars.compile($('#tpl-infos').html()),
rendered = template(properties);
I wrote it the day #chrisv published its package, with a KISS approach in mind. It is mainly based on Miguel Araujo's gist : https://gist.github.com/893408.
for a deeper integration between handlebars and Django (including optional on-the-fly precompilation) check out my project at
https://bitbucket.org/chrisv/django-handlebars/
It basically works like this:
create HB template under
appdirectory/hbtemplates/myapp/template.html
(just like Django template)
in your app, use
{% handlebars myapp %}
template tag and render template like so:
Handlebars.templates["myapp.template.html"]({context:"value"});
Compile your handlebars first!
From handlebars precompiling documentation:
In addition to reducing the download size, eliminating client-side compilation will significantly speed up boot time, as compilation is the most expensive part of Handlebars.
You can compile templates in your build environment using handlebars npm module, or integrate it with a build tool like gulp with gulp-handlebars.
After compiling, your handlebars templates can be served as static resources and bypass server side rendering altogether. Makes it easier on caching too :)
Typical usage would look like this:
<div id="restaurants-tpl">
Waiting for content...
</div>
<script src="{% static 'js/handlebars.runtime.js' %}"></script>
<script src="{% static 'js/templates.js' %}"></script>
<script>
// Let django render this as a json string
properties = {{ properties }};
// Use Handlebars compiled template imported above
rendered_html = Handlebars.templates["restaurants-tpl"](properties);
// Set element content
document.getElementById("restaurants-tpl").innerHTLM = rendered_html;
</script>
Django's templating system doesn't support escaping blocks at a time. It would be easy to work around were it not for the fact that when templates are processed the tokenizer doesn't keep exact information on what the tokens looked like before they got tokenized.
I have used the following work-around which is ugly, but (sort of) works. Use different tag delimiters in your templates and a django template tag that translates those back to what you actually want:
#register.tag(name="jstemplate")
def do_jstemplate(parser, token):
while self.tokens:
token = self.next_token()
if token.token_type == TOKEN_BLOCK and token.contents == endtag:
return
self.unclosed_block_tag([endtag])
nodelist = parser.parse( ('endjstemplate',) )
parser.delete_first_token()
s = token.split_contents()
tmpl_id = Variable( s[1] ) if (len(s) == 2 and s[1]) else ''
return JsTemplateNode( nodelist, tmpl_id )
class JsTemplateNode(template.Node):
def __init__(self, nodelist, tmpl_id=''):
self.tmpl_id = tmpl_id
self.nodelist = nodelist
def render(self, context):
content = self.nodelist.render(context)
return u'<script id="%s" type="text/x-handlebars-template">%s</script>' % (
self.tmpl_id.resolve(context),
re.sub( ur'{\$(.*?)\$}', u'{{\\1}}', content ), )
For bonus points you can leverage Django's templates within your templates ...
which will probably cook your brain trying to untangle later:
{% jstemplate "restaurants-tpl" %}
{$#restaurants$}
<div id="<$name$<" class="{$type$}">
<ul class="info">
{$#if info/price_range$}<li><em>{{ trans "Price Range" }}:</em> {$info/price_range$}</li>{$/if$}
{$#if info/awards$}<li><em>{{ trans "Awards" }}:</em> {$info/awards$}{$/if$}
</ul>
<div class="options">
<button>{% trans "Reservation" %}</button>
</div>
</div>
{$/restaurants$}
{% jstemplate %}
Actually I wrote a custom template filter which goes like this:
from django import template
register = template.Library()
def handlebars(value):
return '{{%s}}' % value
register.filter('handlebars', handlebars)
and use it in a template like so:
{{"this.is.a.handlebars.variable"|handlebars}}
It's the simplest thing I could think of. You just have to put your handlebars variable name in quotes. I regret I hadn't got this idea before I struggled with ssi. It works also with keywords:
{{"#each items"|handlebars}}
Why not use jinja2 instead? IMO, they're both elegant to use. Here's an excellent article about it: Using Jinja2 with Django

Django: Passing argument to parent template

I have templates of this style
project
- main_templates (including nav bar)
-- app1
--- app1_base_template
--- app1_templates
-- app2
--- app2_base_template
--- app2_templates
So when rendering, app2_templates extends app2_base_template which extends main_template.
What I need to do, is have the corresponding nav item be bold when app2's template is being rendered (to show the user where he is).
The easiest would if I could pass a variable in the {% block xxx %} part.
Is this possible ?
What other generic ways are there ?
Have you tried the {% with %} template tag?
{% block content %}
{% with 'myvar' as expectedVarName %}
{{block.super}}
{% endwith %}
{% endblock content %}
There's no direct way to pass a variable up the template inheritance tree the way you describe. The way that people implement navigation bars that highlight the current page is often tweaked to the nature of the site itself. That said, I've seen two common approaches:
The low-tech approach
Pass a variable in the template context that indicates which tab is active:
# in views.py
def my_view(request):
return render_to_response('app2_template.html', {"active_tab": "bar"},
<!-- Parent template -->
<div id="navigation">
<a href="/foo" {% ifequal active_tab "foo" %}class="active"{% endifequal %}>Foo</a>
<a href="/bar" {% ifequal active_tab "bar" %}class="active"{% endifequal %}>Bar</a>
</div>
The higher-tech approach
Implement a custom template tag to render your navigation bar. Have the tag take a variable that indicates which section is active:
<!-- Parent template -->
<div id="navigation">{% block mainnav %}{% endblock %}</div>
<!-- any child template -->
{% load my_custom_nav_tag %}
{% block mainnav %}{% my_custom_nav_tag "tab_that_is_active" %}{% endblock %}
You can pretty much go crazy from there. You may find that someone has already implemented something that will work for you on djangosnippets.org.
Inability to pass args on template inclusion is one of many failings of the Django template system.
I had to deal with an almost identical problem: Deeply nested templates needed to cause parent templates to format/highlight differently.
Possible solutions:
Use a "super context" routine that sets a number of values based on where in the hierarchy you are. I.e. super_context = MySuperContext(request, other, values, etc.), where super_context is a dict you pass to the view (or to RequestContext). This is the most Django-thnonic(?) approach, but it means that presentation logic has been pushed back into the views, which feels wrong to me.
Use the expr template tag to set values in the lower level templates. Note: this only works when you {% include %} a template because it must be evaluated before the inclusion occurs. You can't do that with an {% extends %} because that must be the first thing in the child template.
Switch to Jinja2 templating, at least for the views where you need to do this.
Once you have these values set you can do things like this:
<div class="foo{% if foo_active%}_active{%endif%}"> stuff </div>
This makes the div class "foo" when it's not active and "foo_active" when it is. Style to taste, but don't add too much cinnamon. :-)
I have taken Jarret Hardie's "low tech" approach in a similar, err... context (yes, it's a pun... which won't make perfect sense to you unless I tell you that I was not doing navs but setting the border color of buttons in order to show which one had been pressed).
But my version is a bit more compact I think. Instead of defining just one simple context variable activebar in the view, I return a dictionary, but always with only one key-value pair: e.g. activebar = {'foo': 'active'}.
Then in the template I simply write class="{{activebar.foo}}" in the foo anchor, and correspondingly in the other anchors. If only activebar.foo is defined to have the value "active" then activebar.bar in the bar anchor will do nothing. Maybe "fail silently" is the proper Django talk. And Bob's your uncle.
EDIT: Oops... a couple of days have passed, and while what I had written above did work for me a problem appeared when I put into the navbar an anchor with a new window as target. That seemed to be the cause of a strange glitch: after clicking on the new window (tab in Firefox) and then returning to the one from which the new window was launched, portions of the display below the navbar became blank whenever I quickly moved the cursor over the items on the navbar--- without clicking on anything. I had to force a screen redraw by moving the scroll bar (not a page reload, though that too worked because it involves a screen redraw).
I'm much too much of a noob to figure out why that might happen. And it's possible that I did something else that caused the problem that somehow went away. But... I found a simpler approach that's working perfectly for me. My circumstances are that every child template that is launched from a view should cause an associated navbar item to be shown as "active". Indeed, that navbar item is the one that launched the view that launched the child template--- the usual deal.
My solution--- let's take a "login" navbar item as an example--- is to put this in the child template that contains the login form.
{% block login %}active{% endblock %}
I put it in below the title block but I don't suppose the placement to matter. Then in the parent template that contains the navbar definition, for the li tag that surrounds the anchor for the login navbar item I put... well, here's the code:
<li class="{% block login %}{% endblock %}">Login</li>
Thus when the child template is rendered the parent will show the login navbar item as active, and Bob's still your uncle.
The dictionary approach that I described above was to show which of a line of buttons had been pressed, when they were all on the same child template. That's still working for me and since only one child template is involved I don't see how my new method for navbars would work in that circumstance. Note that with the new method for navbars views aren't even involved. Simpler!
Variables from the parent template are automatically included in the child's scope. Your title says you want to pass a variable TO the parent, which doesn't make sense, as you can't define variables in templates. If you need it a variable in the both the parent and the child, just declare it in the view.
Sadly I can't find a clean way.
Ended up putting a tag in each app's base.html:
<span class="main_nav_bar_hint" id="2"></span>
(Actually I use two. One set by app's base for the main nav. One set by app pages for app's nav bar)
And a bit of JQuery magic in the project base.html
$(document).ready(function() { $("#nav_menu_" + $(".main_nav_bar_hint").attr("id")).removeClass("normal").addClass("selected"); })
Its a bit of a hack, but this way its easy to understand and I only need to make logical changes once more apps are added.
You can use HttpRequest.resolver_match.
In the parent html template:
<li class="nav-item {% if request.resolver_match.view_name == 'my_simple_blog:homepage' %}active{% endif %}">Home</li>
<li class="nav-item {% if request.resolver_match.view_name == 'my_simple_blog:about' %}active{% endif %}">About</li>
It checks for the current namespace and compare it, if they are the same, add active in the class of the list.