Django: Passing argument to parent template - django

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.

Related

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

DJangoCMS - Outputting number of menu children to menu template

Within DjangoCMS I'd like to display the number of children a page has in the navigation.
I have a menu item called "Careers" and I'd like it to say "Careers (2)" if there are 2 sub-pages within that page in the site tree.
Is this possible?
I can target the right menu item in menu.html but need to dynamically generate the number somehow.
{% if child.get_menu_title|slugify == "careers" %}<span>1</span>{% endif %}
I more or less determined that without faffing with models (which I can't do) I solved the problem with some nifty CSS and JavaScript. Not a wonderful result, but it's sufficient.
Just tried it myself and you can do:
{% if child.children %} <span>{{ child.children|length }}</span>{% endif %}

What's the best way to allow an infinite amount of foreign keys to the same model?

I'm making a forum with django right now, and I want it so that anyone can comment on anyone else's comment. Right now I have a foreign key in my 'Comment' model that points back to itself so I can get the parent comment easily from any comment.
In my theory this worked great, because from any comment I could get all of its child comments, and then just keep branching down from there to get every single child comment. However I'm having trouble actually implementing this when it comes to getting the data from the view to the template.
I want it to be possible to have an infinite number of child comments, because who knows how long a discussion will last and I don't want to arbitrarily limit it. The problem I'm having is would you get all of those comments from the view to the template without losing their relationship to their parent comment?
Currently this is what the psuedocode for my code looks like:
#the view
def comment_page(request, forum, comment_id):
#this is the main comment that all others will stem from
main_comment = Comment.objects.get(id=comment_id)
children_comments = main_comment.comment_set.all()
#the template
{% for comment in children_comments %}
<p class='comment'>{{comment}}</p>
{% endfor %}
Obviously I'm not even trying to get all the child comments here, it just gets child comments of the very first post. What I don't understand is how can I then go through each of these child comments and then get all of theirs, and keep doing that for each new comment?
It makes the most sense to do it in the view since I am able to use Django's QuerySet API in there, but I don't see how I would be able to pass all of the comments to the template without losing their relationship to their parent. The only idea I can think of is to go through all of the comments in the view and build up a string of html that I just pass and simply display in the template, but that seems like a horrible idea because it'd be dealing with template related stuff in the view.
You might want to look into using a MPTT such as django-mptt
this can be implemented by a custom filter with an inclusion_tag that includes itself but causes a lots of queries to your db:
#register.inclusion_tag('children.html')
def comments_tree(comment):
children = comment.comment_set.all()
return {'children': children}
# children.html
<ul>
{% for child in children %}
<li> {{ child }}</li>
{% if child.comment_set.count > 0 %}
{% comments_tree child %}
{% endif %}
{% endfor %}
</ul>
# your template
{% comments_tree comment %}
This older question is probably of interest:
How can I render a tree structure (recursive) using a django template?
Edit: to future readers, don't do this as the inner for loop's comment variable does not substitute the outer comment variable during the loop execution, leading to infinite recursion. /Edit
If you need a recursive tree structure in your HTML page (i.e. a bunch of nested <div> tags), you can write a recursive "comment" template.
Sample: (untested)
{# comment.html #}
<p class='comment'>{{ comment.text }}</p>
{% if comment.children %}
{% for comment in comment.children %}
{% include "comment.html" %}
{% endfor %}
{% endfor %}
The for loop binds the comment template variable to each child before including itself.
Performance note: Unless your comment sets are often short, this will probably be very slow. I recommend that you make your comments non-editable and cache the result!
Alternate solution: If you don't need the recursive HTML <div> tags, you can write a generator that performs a pre-order traversal of the structure and yields (depth, comment) pairs. This would likely be far more efficient in rendering speed.

Django: cannot pass variable to included template?

I got a problem where I want to use template including in Django.
Here is the real example:
I got 3 files:
home.html (will get the context variable passed from Views)
base.html (the skeleton template file)
and the header.html (included by base.html).
If I put the code below directly in base.html without including header.html, the {{title}} variable passed from home is correctly called. But if I include the header.html in base.html, the {{title}} variable's value cannot be called.
<title>{% block title %}{% endblock %} | {{ SITE_INFO_TITLE }}</title>
Is there any solution to this problem? Thanks.
Could you just pass in a variable within the {% include %} tag? It's documented here: https://docs.djangoproject.com/en/1.5/ref/templates/builtins/#include
{% include "name_snippet.html" with person="Jane" greeting="Hello" %}
As far as I know blocks and variable are distinct in django.
If you want to pass title as a context variable you have to set it using a declaration in base.html such as :
{% include "header.html"%}
Which in turn contains :
{% block title %} {{title}} {%endblock%}
You can also set it in home like this.
{% block title %} Home page {%endblock%}
But I also try to set in the template context.
Without the title block.
def test_view(ctx):
xa = { "title":"Sommaire"}
return render_to_response("test.html",xa)
I think you can also see the with template tag I think it is possible to set a context variable using this tag.
You can use Inclusion Tags to render an additional template from within a Django template. You can additionally pass the 'child' template context from the 'parent' template.
It's a little involved for your use case but it solves your problem. I tend to use it when I'm looping a list to render each item with a custom template. I can then reuse that template elsewhere without duplicating the markup if I need to render another item of the same type.

Django conditional template inheritance

I have template that displays object elements with hyperlinks to other parts of my site. I have another function that displays past versions of the same object. In this display, I don't want the hyperlinks.
I'm under the assumption that I can't dynamically switch off the hyperlinks, so I've included both versions in the same template. I use an if statement to either display the hyperlinked version or the plain text version. I prefer to keep them in the same template because if I need to change the format of one, it will be easy to apply it to the other right there.
The template extends framework.html. Framework has a breadcrumb system and it extends base.html. Base has a simple top menu system.
So here's my dilemma. When viewing the standard hyperlink data, I want to see the top menu and the breadcrumbs. But when viewing the past version plain text data, I only want the data, no menu, no breadcrumbs. I'm unsure if this is possible given my current design. I tried having framework inherit the primary template so that I could choose to call either framework (and display the breadcrumbs), or the template itself, thus skipping the breadcrumbs, but I want framework.html available for other templates as well. If framework.html extends a specific template, I lose the ability to display it in other templates.
I tried writing an if statement that would display a the top_menu block and the nav_menu block from base.html and framework.html respectively. This would overwrite their blocks and allow me to turn off those elements conditional on the if. Unfortunately, it doesn't appear to be conditional; if the block elements are in the template at all, surrounded by an if or not, I lose the menus.
I thought about using {% include %} to pick up the breadcrumbs and a split out top menu. In that case though, I'll have to include it all the time. No more inheritance. Is this the best option given my requirement?
You can put your hyperlinks inside a block that is overridden by the loading templates.
Let's say you have your framework.html like this:
{% extends "base.html" %}
<html>...<body>...
{% block hyperlinks %}
your hyperlinks here
{% endblock %}
rest of the code
</body></html>
You can then create something of a nolinks.html template and use it
{% extends "framework.html" %}
{# here you'll have everything from framework
but now we disable the breadcrumbs #}
{% block hyperlinks %}{% endblock %}
If you're getting the past data you can then use nolinks to render instead of framework.
I hope this helps.
From here: Any way to make {% extends '...' %} conditional? - Django
It can be done like this :
{% extends ajax|yesno:"ajax_base.html,main_base.html" %}
Or:
{% extends a_variable_containing_base_template_name %}
Which ever best suited for you.
Regards;