In a Django project, I have a mini navbar that is common in ~30% of my templates. Instead of including it in my global base.html, I decided to take a different route.
I first wrote a separate view for this:
from django.template.loader import render_to_string
def navbar(origin=None):
if origin == '1':
locations = get_approved_loc(withscores=True)
else:
locations = get_approved_loc()
obj_count = get_all_obj_count()
return render_to_string("mini_navbar.html",{'top_3_locs':locations[:3],\
'other_cities':len(locations[3:]),'obj_count':obj_count})
I next added it in the templates it needed to be in via:
{% include "mini_navbar.html" with origin='1' %}
When I run this code, I get a NoReverseMatch error. It seems the view function navbar never runs. So the context variables it was sending in (e.g. top_3_locs or other_cities etc) are never populated. Hence NoReverseMatch.
What's wrong with this pattern, and what's the fix for it? An illustrative example would do the trick.
Rather than including a template directly, you should write a custom template tag - specifically, an inclusion tag that renders the template with the custom context. The code that you have put in that separate view goes in that template tag instead.
Here's an illustrative example of Daniel's suggestion:
I created an 'inclusion' template tag like so:
from django import template
from redis_modules import get_approved_loc, get_all_obj_coun
register = template.Library()
#register.inclusion_tag(file_name='mini_navbar.html')
def mini_navbar(origin=None):
locations = get_approved_loc(withscores=True)
obj_count = get_all_obj_count()
return {'top_3_locs':locations[:3],'origin':origin,'other_cities':len(locations[3:]),'obj_count':obj_count}
Next I included this in the relevants templates like so:
{% load get_mini_navbar %} <!-- name of the module -->
And finally, I called it within the template like so:
{% mini_navbar origin %}
Where origin is the parameter passed into the tag.
Related
I'm using a custom Django (v1.5.5) model Widget used to contain HTML code (using a TextField) to be placed in certain places of different pages and allow staff users change the contents easily through the Django admin backend.
This approach works perfectly with HTML, CSS and Javascript code. Using the following syntax at the templates:
{{ widget.content|safe }}
However, I'd also like to be able to put Django code (using templatetags, calling methods...) within it, but this Django code does NOT get executed and instead it's directly printed as if it was plain text.
Is there any way to execute Django code using the safe filter? Or am I missing another filter? Maybe I'd have to use a custom filter or templatetag... Any ideas?
Thank you!
Django doesn't have a template tag/filter like that by default. I'm using this in one of my projects:
class IncludeString(ttag.Tag):
template_src = ttag.Arg()
def render(self, context):
data = self.resolve(context)
try:
t = loader.get_template_from_string(data['template_src'])
except Exception, e:
return u"error rendering template: %s" % unicode(e)
return t.render(context)
class Meta:
name = "include_string"
register.tag(IncludeString)
And use it like {% include_string variable_name_with_template_content %}. This implementation uses django-ttag, but I think something like the following should work, too (untested):
#register.simple_tag(takes_context=True)
def include_string(context, template_src):
try:
t = loader.get_template_from_string(template_src)
return t.render(context)
except Exception, e:
return u"error rendering template: %s" % unicode(e)
Based on #sk1p's answer, I've created a Django simple_tag that takes an HTML string (which can contain "Django code") and the context (which is absolutely necessary here). Then I just render it and return. That's it!
I created a file called smart_html.py that contains:
from django import template
from django.template import Template
register = template.Library()
#register.simple_tag(takes_context=True)
def render_html(context, html):
return Template(html).render(context)
Then, from the templates I just have to do:
{% load smart_html %}
{% render_html widget.content %}
So I often have to use something like the following in my Django templates:
{% include "conname.html" %}
I'd like to be able to instead just have my own tag and only have to type something like
{% conname %}
Is there an easy way to setup some sort of alias so that when ever the template engine sees the conname tag it knows that should actually be a specific include tag?
Pretty simple, in a module just add the following code (mine are usually called custom_tags.py:
from django import template
register = template.Library()
#register.simple_tag
def conname():
return '''{% include "conname.html" %}'''
#to make it available everywhere (i generally add this to my urls.py
from django.template.loader import add_to_builtins
#this will be aded to all templates
add_to_builtins('custom_tags')
In your template you would just use {% conname %}
Or you could make it more complicated and programatically call IncludeNode but that is overkill.
Hi I need to refresh my custom template tag --right_side.py-- via Ajax. Is there a way to import the template tag in the view and return it as HttpResponse because I don't want to give up my custom template tag (it works great in other pages) nor code a new view action which is really similar to it.
Having a link to call with Ajax or loading it in the view inside
if request.isAjax():
Are both fine for me.
I find it really useful when refreshing an area with ajax. So thought it would be good to share it:
First you import the custom template tag you coded in your view file.
from your_app_name.templatetags import your_tag_name
And then you use it like this:
return HttpResponse(your_tag_name.your_method(context))
That worked for me and I got the template tag as response from server and refreshed the div with that result.
I had this same question awhile ago, I was loading HTML snippets with AJAX which I had already written as template tags. And I was trying to avoid duplicating the code in two places.
This is what I came up with to render a template tag from a view (called via ajax):
from django.template import RequestContext, Template
def myview(req):
context = RequestContext({'somearg':"FooBarBaz"})
template_string = """
{% load my_tag from tagsandfilters %}
{% my_tag somearg %}
"""
t = Template(template_string)
return HttpResponse(t.render(context))
You could create a template just containing your templatetag and nothing else. Then you would have in right_side.html:
{%load cems_templatetags%}
{%right_side%}
and in the view something like:
if request.isAjax():
return render_to_response('right_side.html',RequestContext(request))
I found this solution :
from templatetags_file_name import my_templatetag
return render (request,'path/to/template.html', my_templatetag(parameter) )
And, in my "templatetags_file_name", "my_templatetag" is like that :
#register.inclusion_tag('path/to/template.html')
def my_templatetag(parameter):
#my operations
return locals()
I have a site where different projects/programs are listed, the list is dynamic so once a user clicks on a specific program e.g. /program/health the template should load one that has a "health" specific header (mast head). How can I load a base template based on the program selected?
Whenever you pass the path to a template to a function, you may substitute the hard-coded string for some variable of your choosing. If you do this it is extremele important to make sure it is a sensible value. To be fair, the template system will escape dangerous stuff and will not go outside the specified template dir, but trying to load a non-existing template will cause the view to crash and you don't want that.
If you have a model for the program, then get the model and use the slug as a template name, like this:
p = get_object_or_404(Program, slug = slug_from_url_or_whatever)
template = "program_%s.html" % p.slug
It is also possible to pass a list of templates to the loaders. If it doesn't find the first one, it will try the next one, etc. For example:
render_to_response([template, "default_program_template.html"], ...)
You could certainly use a custom Context Processor and some CSS to get the job done.
settings.py:
TEMPLATE_CONTEXT_PROCESSORS = ("django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"yourproject.context_processors.header_context")
context_processors.py:
def header_context(request):
values = {'body_class': "default",}
if request.path == "/program/health/":
## Don't really use an if block, it's just nasty.
values['body_class'] = "health"
return values
views.py:
from django.shortcuts import render_to_response
from django.template import RequestContext
def view(request):
## Do magic stuff!
return render_to_response("template.html",
{},
context_instance=RequestContext(request))
template.html:
<body class="{{ body_class }}">
<!-- Whatever -->
</body>
Have you considered using a combination of CSS class names from a template variable and include template tags to accomplish this?
Something like:
<body class="{{ program }}">
<div>{% include program_specific_template %}</div>
<div>Common stuff...</div>
</body>
So we're writing a super simple game-app (answer questions, get points) and we want to render a high-score in the side-bar of every screen on the page.
So I understand that Django automatically (kinda) passes the authenticated user as argument (if we use render() ) but I'm not sure how to pass what basically will be:
top_list = User.objects.filter().order_by('-score')[:10]
so that we can fix a nice little top list in our base template :)
We've looked around at different questions here, like this one for example and thought about whether the solution would be to write our own context processor?
EDIT:
Might be a duplicate of this so should I perhaps write a custom template tag instead?
You can use django template tags to do it..
Under myapp/templatetags/ create a myapp_tags.py and __init__.py
In myapp_tags.py do the following:
from django import template
register = template.Library()
#register.assignment_tag
def get_top_list():
top_list = User.objects.filter().order_by('-score')[:10]
return top_list
And then use it in your templates like this:
{% load myapp_tags %}
{% get_top_list as top_list %}
{% for top_user in top_list %}
...