I'm stumbling with an error when trying to do something as simple as including a template that extends another one... I'm not sure if that's an unsupported case or I'm doing something wrong, because it seems like a very common scenario.
The smallest code that I've managed to write to reproduce the error is this:
test.py
import tornado.template
loader = tornado.template.Loader(".")
templ = loader.load("t1.html")
t1.html
{% include "t2.html" %}
t2.html
{% extends "t3.html" %}
t3.html
{# empty #}
when running test.py I get a NotImplementedError raised in tornado's template.py
Am I missing something or is this a bug?
Ah, sorry, I got too focused on the missing blocks.
What you've described doesn't work using {% include %}, but works for me using {% module Template('t2.html', **args) %}, which will render the template in it's own namespace. Module setup is done automatically by tornado.web.Application, but not with the minimal template loader in your example.
This limitation seems to be there because of the way the {% extends %} tag is implemented.
Related
I am creating a custom django template tag by using such a code :
#register.simple_tag(takes_context=True)
def render_listing(context, *args, **kwargs):
... my code ...
This works well, but in my template, it seems that all parameters must be on a single line, for example :
this works:
{% render_listing param1=val1 param2=val2 ... paramN=valN %}
but on multiple lines, it does not work :
{% render_listing param1=val1
param2=val2
...
paramN=valN %}
I tried multiple escape sequences but I did not succeeded,
Is there a way to specify a template tag on multiple lines ?
No, the Django template language does not support multiple line tags. See ticket 8652, which was closed as WONTFIX, or this thread from the django-developers mailing list.
Sometimes, if there is a repeated prefix, you can make it more readable by using the with tag. For example if you have,
{% render_listing param1=long.common.prefix.val1 param2=long.common.prefix.val2 param2=long.common.prefix.val3 %}
you could rewrite as
{% with prefix=long.common.prefix %}
{% render_listing param1=prefix.val1 param2=prefix.val2 param2=prefix.val3 %}
{% endwith %}
Often (but not always), a really long tag is an indication that you're putting too much logic in the template. See if you can move some of it into the view, model method, template tag or template filter.
It's pretty straightforward to enable, though hackish:
import re
from django.template import base
base.tag_re = re.compile(base.tag_re.pattern, re.DOTALL)
"Using" it is simple; one place I find it especially useful is {% include %} tags:
{% include 'my/sweet/modal-template.template' with
message="Hey, do you really want to frob the widget?"
yes="Heck yes I do!"
no="No frickin' way!"
icon="error"
%}
I haven't tested this in more recent versions of Django but I imagine it could be adapted; that worked at least back around 1.8. I should point out that in theory some tags that do custom parsing of their arguments could break; in practice I haven't had any trouble in the last ~10 years of Django programming.
I have two blocks that call the same method with same variables. I want to call the method only once, but the result is then outsite the scope of the block tags. I've tried calling this method in the parent template header.html and with a with tag, but nothing seems to work.
This is the layout:
{% extends "header.html" %}
{% load navigation_tags %}
{% block header %}
{% get_section site=site as section %}
{% include "foobar.html" with section=section %}
{% endblock header %}
{% block navigation %}
<nav>
<div class="container">
{% get_section site=site as section %}
{% navigation section.slug %}
</div>
</nav>
{% endblock navigation %}
navigation_tags.py
#register.assignment_tag
def get_parent_section(site):
if site.id == settings.FOOBAR_SITE_ID:
section = Section.objects.get(id=settings.FOOBAR_SECTION_ID)
else:
# This is also a section instance.
return site.default_section
As mentioned by 2pacho in another answer and Fernando Cezar in a comment, the easiest way to share values between different sections is to set it in the template context. If you are using the render shortcut function, you can pass a dict as the context parameter to add a value to the rendering context of the template. That would be a good place to add it and this would be the easiest place to put it.
return render(request, 'template.html', {'section': get_parent_section(site)})
However, if for some reason, you can't include it in the context, you can use a decorator to add memoization to your function, so that it will cache the computation results and return it immediately when called with the same parameters. You can use functools.lru_cache to do so, or it's Django backport at django.utils.lru_cache.lru_cache if you are using Python 2.x.
#register.assignment_tag
#functools.lru_cache()
def get_parent_section(site):
if site.id == settings.FOOBAR_SITE_ID:
section = Section.objects.get(id=settings.FOOBAR_SECTION_ID)
else:
# This is also a section instance.
return site.default_section
I wouldn't call a method outside .py . Think that this is using Jinja2 templates,
it's powerful but not in the way that the backend can be.
What I recommend you doing in this case is to generate a context for the template and use this variables there.
Would be as simple as adding it to your context where it's being generated.
context['site_parent'] = get_parent_section(site)
Think that Jinja2 (html) has to be as simple as possible and that can help you with basic coding and time saving (like loops to print the exact same information or show and hide code depending on the context) but I would keep it as simple you can when rendering.
If you would like you can read official django website about templates https://docs.djangoproject.com/en/2.0/topics/templates/
But from my expirience I would keep the method calls in the views.py
I am reading 《Flask web development》.
in Example 4-3,
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
I'd like to know:
What are the differences between extends and import?(I think they are quite similar in usage.)
In which situation,I will use extends or import?
When you extend another template the template controls you (the called controls the caller) - only named blocks in the "parent" template will be rendered:
{% extends "base.html" %}
{% block main_content %}
Only shows up if there is a block called main_content
in base.html.
{% endblock main_content%}
On the other hand an import simply binds the template to a name in your template's scope, and you control when and where to call it (the caller controls the called):
{% import "bootstrap/wtf.html" as wtf %}
Some of your own template code with {{ wtf.calls() }} where it makes sense.
There is a difference. {% extends parent.html %} allows you to render parent.html and also override {% block %}'s defined in it while {% import %} just allows you to access template variables.
So example template is extends base.html and imports variables from bootstrap/wtf.html. Think about it like python's class inheritance and import statement.
By default, included templates are passed the current context and imported templates are not.
Jinja documentation
By default will the included templates not be cached while the imported will.
The reason for this is that imports often are used as a module that holds macros.
The best practice will be to use import on templates that contain macro's while include is best to use when you just wan't some markup template.
I would like to include blocks from templates instead of macros, as many templates will be including content from many other templates, so extends isn't an option.
I've read many answers about this, including blocks in included files, but the use case always seems to be different. I suspect this cannot be done.
template1.html
{% block description %}
<p> Here is a description </p>
{% endblock %}
And in template2.html
{% from 'template1.html' import description %} <- doesnt work
You have two options here.
Use a macro, which sounds like something you don't want to do.
Use a template_filter.
Assuming you're using Flask, this is as easy as:
#app.template_filter('get_block')
def template_filter(block, file):
html = render_template(file)
# Then use regex or something to parse
# or even simple splits (better with named blocks), like...
content = html.split('{%% block %s %%}'%(block))[-1]
content = content.split('{%% endblock %%}')[0]
return content
And to use it:
{% 'description'|get_block('template1.html') %}
I have the following template in template/admin/change_form.html:
{% extends "admin/change_form.html" %}
{% block extrahead %}
{% include "dojango/base.html" %}
{% block dojango_content %}
{% endblock %}
{% endblock %}
However for some reason it throws a
TemplatesyntaxError: TemplateSyntaxError at /admin/cms/post/add/
Caught RuntimeError while rendering: maximum recursion depth exceeded while calling a Python object
I know it's late, but...
If extending - which is a far better option than duplicating - the key is to have it named anything except /admin/change_form.html.
(Although the OP referred to template/admin/change_form.html, this is simply because a path in his TEMPLATE_DIRS tuple ends in '/template' - mine generally end in '/templates' - but, these directories can be named anything and located anywhere.)
It will be used automatically on a per-app basis if named /admin/<MyAppName>/change_form.html
It will be used automatically on a per-model basis if named /admin/<MyAppName>/<MyModelName>/change_form.html
It can be named anything if specified explicitly in the ModelAdmin
class MyModelAdmin(admin.ModelAdmin):
change_form_template = 'subdir/my_change_form.html'
Finally, if insistent on naming it /admin/change_form.html, you can - provided that the extends tag contains the full path to your django installation instead of a relative one.
You are in admin/change_form.html and you extend admin/change_form.html. You cannot extend the same template which you are in.
You probably expected that if you override template from admin application, you can extend the one you override. But this is not how it works. When you override a template you cannot access it.
Solution to your problem is to copy original template and change things you don't like.
Also, you can point your AdminOptions class to another template using the change_form_template property.
Something like:
class MyOptions(AdminOptions):
change_form_template = 'myapp/my_change_form.html'
And myapp/my_change_form.html:
{% extends "admin/change_form.html" %}
I had the same problem. Solved by placing the overridden template under myapp/templates/admin/myapp instead of myapp/templates/admin.
The best way to do this that I have found is to use '..' to go up a couple of directories, then go back down into directories that should only be found in the Django code base.
As the Django templates are in something like "django/contrib/admin/templates/admin", I found that this worked me:
{% extends "../../admin/templates/admin/change_form.html" %}
If that still causes a clash with some other structure you have, you could go further:
{% extends "../../../contrib/admin/templates/admin/change_form.html" %}
or even:
{% extends "../../../../django/contrib/admin/templates/admin/change_form.html" %}
Although it is a little hacky, at least by doing the above you don't have to use some other technique that involves copying the django source or setting up a symlink.
With Django core it's impossible. But it is not impossible.
Copy and paste "the original template and change things you don't like" it's very ugly.
Don't make in the templates, whatever you don't make in python
This solution is to any template:
http://pypi.python.org/pypi/django-smart-extends