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
Related
I am trying to solve a problem. During the process I got this question.
{% if request.path == '/{{sub_cat.sub_cat_parent.cat_slug}}/{{sub_cat.sub_cat_slug}}/' %}
<div class="alert-secondary">Active</div>
{% else %}
How do I properly include slashes in here '/{{sub_cat.sub_cat_parent.cat_slug}}/{{sub_cat.sub_cat_slug}}/'? Is it okay to use {{ within {% template tags? what am I doing wrong in here?
"variable substitution" ({{ somevar }}) syntax is indeed not supported in templatetags arguments - which makes sense if you consider that in the context of template rendering, this syntax would be the equivalent of a "print" statement.
This doesn't mean you can't pass context variables to templatetags - else templatetags would be of very limited use - but you do so by just mentionning them ie assuming you have a context variable named "path", you could use it here as
{% if request.path==path %}
...
You can also use filter expressions here, ie this is valid too:
{% if request.path==path|lower %}
(assuming the templatetag's author correctly handled the tags arguments - I let you refer to the documentation for more on this).
Now wrt/ your problem here, you could of course use a custom filter to join the different parts but that would actually be a very bad idea. Django's url handing is based on the idea that you should actually never hardcode an url anywhere - you define and name urls in your urls.py files, and then use the builtin functions (django.core.urlresolvers.reverse()) / templatetags ({% url %}) to generate them.
Here, the proper solution would be to define a get_absolute_url() method on your sub_cat object that returns the correct url (actually the path portion of it) for this object, and test against it in the template.
As far as I'm concerned, I'd even use a second indirection level as a template filter so 1/ your model (or whatever sub_cat is) knows it's own url, and 2/ the template filter can tell if a given sub_cat is "active" for the current request.path so if the spec defining when a sub_cat is "active" is to change you'll only have the template filter function to change instead of having to browse thru all your templates (possibly missing something on the way).
use with clause
{% with "/"|add:sub_cat.sub_cat_parent.cat_slug|add:"/"|add:sub_cat.sub_cat_slug|add:"/" as url_path %}
{% if request.path == url_path %}
<div class="alert-secondary">Active</div>
{% else %}
{% endwith %}
this will work
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'm trying to figure out what is the best way to have mixes member and guest templates.
The main difference would be the menu of the page. In some languages i've worked with you can add prefixes to templates to get it to switch out the whole templates for the other version.
ex:
base.guest.html
base.member.html
In Django the only way i've seen any thing related to this is this code i found in the documents:
if request.user.is_authenticated():
# Do something for authenticated users.
else:
# Do something for anonymous users.
Is this the base way to do this in Django? or is there something else that i'm missing.
For most of my pages this would work out ok but wasn't sure if there was a better way to switch content based on authenticated state.
You don't extend in this case, instead you include. The base.html should authentication-agnostic.
{% if user.is_authenticated %}
{% include 'member.html' %}
{% else %}
{% include 'guest.html' %}
{% endif %}
If you want to, you can do have the if-statement also in your view, and pass the name of the template-to-be-included to the main template. For more info see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#include
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.
To add some content to a Django admin view of a model, I want to override the change_form.html template. According to the documentation I would need to create a file change_form.html in a the folder /project-path/templates/admin/appname/modelname/. Of course I need to make sure, that this path is also available in TEMPLATE_DIRS. Such a file could look like this:
{% extends "admin/change_form.html" %}
{% load i18n %}
{% block after_field_sets %}
SOME CONTENT
{% endblock %}
However, I make use of django-guardian to have object permissions. This Django app overrides change_form.html as well (which works fine -- relevant source seems to be here), but Django doesn't pick up my template extension file (i.e. "SOME CONTENT" from the sample above isn't displayed). The blocks/parts I want to override are not the same ones that django-guardian overrides and eventually I want to have the additions to change_form.html of django-guardion and of my template.
What am I doing wrong here? And is possible at all to have multiple applications overriding an admin template?
If it is of interest, this is my TEMPLATE_LOADERS setting:
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader'
)
Also, django-guardian is the last app in the INSTALLED_APPS array.
One possible solution seems to be to explicitly define the inheritance chain by referring to and overriding django-guardian's template (defined here) and not Django's general change_form.html. So instead of using
{% extends "admin/change_form.html" %}
in the beginning of my custom template, I would need to use
{% extends "admin/guardian/model/change_form.html" %}
Also I would need to extend my GuardedModelAdmin sub-class model to explicitly use my own template file as the change form template:
class MyModel(GuardedModelAdmin):
change_form_template = 'admin/appname/mymodel/change_form.html'
This works, but it adds a clear dependency to the template and the model. Of course, the model has this dependency anyway, but I would be interested if there is also a solution that refers only to the default change_form.html -- however, I suspect that this is not really possible.
If a path to your template is in TEMPLATE_DIRS, then you can reverse order of your TEMPLATE_LOADERS.
If your template in in your app, then you need to add your app after django-guardian in INSTALLED_APPS array.
Basically, if there are name collisions, then last loaded template will be in use.