Ignore missing variables in Django templates - django

How can I make Django (1.11) ignore missing variables in template during rendering? I need to render the same template with different data in multiple steps. I need to use the Django template engine for all the features that it includes and I can't modify the templates.
Instead of replacing them with an empty string:
>>> from django.template import Template, Context, TemplateSyntaxError
>>> c = Context({'foo': 'hello'})
>>> t = Template('{{foo}} {{bar}}')
>>> t.render(c)
'hello '
I would like it to just leave them as is
>>> t.render(c)
'hello {{bar}}'

I think string_if_invalid will work for you: https://docs.djangoproject.com/en/1.11/ref/templates/api/#how-invalid-variables-are-handled
Your settings should add something like this:
TEMPLATES = [
{
...
'OPTIONS': {
...
'string_if_invalid': '{{%s}}',
...
},
},
]
You may need to escape the curly braces, but I would be surprised if it didn't do that for you when replacing the string in.
Note that from the docs say:
If string_if_invalid contains a '%s', the format marker will be replaced with the name of the invalid variable.
ALSO note that the docs say:
For debug purposes only!
While string_if_invalid can be a useful debugging tool, it is a bad idea to turn it on as a ‘development default’.
Many templates, including those in the Admin site, rely upon the silence of the template system when a non-existent variable is encountered. If you assign a value other than '' to string_if_invalid, you will experience rendering problems with these templates and sites.
Generally, string_if_invalid should only be enabled in order to debug a specific template problem, then cleared once debugging is complete.
EDIT: The doc's warning is making me a bit wary about using this. Give the above a try for debugging, but I wouldn't rely on it for a production system.
You might want to look into writing a custom template tag: https://docs.djangoproject.com/en/2.1/howto/custom-template-tags/#django.template.Library.simple_tag
It would be defined like this:
#register.simple_tag(takes_context=True)
def preserve_invalid(context, var_name):
return context.get(var_name, '{{%s}}' % var_name)
and used like this:
{% preserve_invalid "some_var" %}

Related

String from Django template, but without returning a HTTPResponse

I have a query set whose objects I'd like to use to populate a template. One view I have ends with
return render_to_response('entry.json', {'entry_list':r}, mimetype="application/json; charset=utf-8")
However I'd like to be able to serialise to json with a template like this without having to return a HTTPResponse. In pseudocode, this might be:
render('entry.json', {'entry_list':r}) #returns a string with the template entry.json
Is this possible? If so, how?
What #HankGay said is correct, though you sometimes might want to get the template response with out returning a HttpResponse, even though you are using Django correctly.
read this: Rendering a context:
>>> from django.template import Context, Template
>>> t = Template("My name is {{ my_name }}.")
>>> c = Context({"my_name": "Adrian"})
>>> t.render(c)
"My name is Adrian."
>>> c = Context({"my_name": "Dolores"})
>>> t.render(c)
"My name is Dolores."
Is that what you're after?
Django provides a built in shortcut for this.
https://docs.djangoproject.com/en/dev/ref/templates/api/#the-render-to-string-shortcut
I don't quite understand what you are trying to accomplish, But you can just return JSON as your HTTPResponse. You can serialize objects to jason and return it without the use of any template.
If you aren't handling HTTP requests, it doesn't make much sense to use Django, honestly. Look into Jinja 2 for a simple template engine that has lots in common w/ Django's, and SQLAlchemy for an ORM that is equal or better than Django's.

Make django templates strict

In a django template, a call to {{ var }} will silently fail if var is undefined. That makes templates hard to debug. Is there a setting I can switch so django will throw an exception in this case?
The only hint at a solution I've found online is http://groups.google.com/group/google-appengine/browse_thread/thread/86a5b12ff868038d and that sounds awfully hacky.
Django<=1.9
Set TEMPLATE_STRING_IF_INVALID = 'DEBUG WARNING: undefined template variable [%s] not found' in your settings.py.
See docs:
https://docs.djangoproject.com/en/1.9/ref/settings/#template-string-if-invalid
Django>=1.10
Set string_if_invalid = 'DEBUG WARNING: undefined template variable [%s] not found' template option in your settings.py.
See docs: https://docs.djangoproject.com/en/2.0/topics/templates/#module-django.template.backends.django
Also read:
http://docs.djangoproject.com/en/dev/ref/templates/api/#invalid-template-variables
This hack from djangosnippets will raise an exception when an undefined variable is encountered in a template.
# settings.py
class InvalidVarException(object):
def __mod__(self, missing):
try:
missing_str = unicode(missing)
except:
missing_str = 'Failed to create string representation'
raise Exception('Unknown template variable %r %s' % (missing, missing_str))
def __contains__(self, search):
if search == '%s':
return True
return False
TEMPLATE_DEBUG = True
TEMPLATE_STRING_IF_INVALID = InvalidVarException()
Consider using the django-shouty-templates app: https://pypi.org/project/django-shouty-templates/
This app applies a monkeypatch which forces Django’s template language to error far more loudly about invalid assumptions. Specifically:
chef would raise an exception if the variable were called sous_chef.
chef.can_add_cakes would raise an exception if can_add_cakes was not a valid attribute/property/method of chef
It ain’t compile time safety, but it’s better than silently swallowing errors because you forgot something!
I use this pytest-django config:
[pytest]
FAIL_INVALID_TEMPLATE_VARS = True
This way I get an exception if I run the tests.
That's part of the design. It allows you to provide defaults and switch based on whether or not a variable exists in the context. It also allows templates to be very flexible and promotes re-usability of templates instead of a strict "each view must have it's own template" approach.
More to the point, templates are not really supposed to be "debugged". The idea is to put as much of your logic as possible outside the template, in the views or models. If you want to figure out why a variable that's supposed to be passed to the context isn't, the place to debug that is in your view. Just drop import pdb;pdb.set_trace() somewhere before your view returns and poke around.

django settings variables get lost in while being passed to templates

i have a weird problem.
Basically, in my settings.py file i have 4 variables
URL_MAIN = 'http://www.mysite'
URL_JOBS = 'http://jobs.mysite'
URL_CARS = 'http://cars.mysite'
URL_HOMES = 'http://homes.mysite'
In my views.py i have the usual:
from settings import *
I have 6 views calling them and just returning them to templates inside the context:
class CarsHp(TemplateView):
...
class JobsHp(TemplateView):
...
class HomesHp(TemplateView):
...
class CarsList(TemplateView):
...
class JobsList(TemplateView):
...
class HomesList(TemplateView):
...
which are being called in urls by
CarsList.as_view()
...
All of those views have the same statement:
context['URL_MAIN'] = URL_MAIN
...
for all 4 variables.
In templates i'm correctly getting all 4 of them, except for URL_MAIN, which "gets lost" in 2 of those 6 views. I'm accessing them with classical {{ URL_MAIN }} and i've been trying everything, from moving to renaming, but still that URL_MAIN doesn't show up (i get empty string, no errors of sort) after being served from 2 of those views. All the functions basically share the same code (except for the querying and data processing part) and those settings' variables are just being assigned and returned off. Not any sort of check nor modification. I've been trying with django's shell, and i could always retrieve them.
We're being served by apache, with some proxypassing configurations for the robots.txt file and static files. Nothing "serious".
I'm not posting all the 6 views source codes just because they're long and the relevant parts are all described above. But i can post them if you want,i just don't know if it is actually useful since i've been triple checking all the sources for clashing on names or double declarations or incorrect use.
Thanks all in advance, this is really stunning my brain
Ideally, you should use template context processors for this. It will cut down your code and allow you to see exactly where the problem is.
Make a file in your projects called urls_context_processor.py (or similar) and put your variables in there:
def common_urls(request):
return {
'URL_MAIN': "http://...",
'URL_JOBS': "http://...",
'URL_CARS': "http://...",
'URL_HOME': "http://...",
}
and in your settings.py
TEMPLATE_CONTEXT_PROCESSORS = = (
....
'my_project.urls_context_processor.common_urls',)
now the urls variables will be automatically available in all your template, and you won't need to hard code them into every view.

Show undefined variable errors in Django templates?

How can I ask Django to tell me when it encounters, for example, an undefined variable error while it's rendering templates?
I've tried the obvious DEBUG = True and TEMPLATE_DEBUG = True, but they don't help.
Put this in your debug settings:
class InvalidString(str):
def __mod__(self, other):
from django.template.base import TemplateSyntaxError
raise TemplateSyntaxError(
"Undefined variable or unknown value for: \"%s\"" % other)
TEMPLATE_STRING_IF_INVALID = InvalidString("%s")
This should raise an error when the template engine sees or finds an undefined value.
According to the django documentation,
undefined variables are treated as ''(empty string) by default. While in if for regroup, it's None.
If you are going to identify the variable undefined, change TEMPLATE_STRING_IF_INVALID in settings.
'%s' makes the invalid variable to be rendered as its variable name, in this way, u can identify easily.
how-invalid-variables-are-handled
Finding template variables that didn't exist in the context was important to me as several times bugs made it into production because views had changed but templates had not.
I used this technique, implemented in manage.py, to achieve the effect of breaking tests when template variables not found in the context were used. Note that this technique works with for loops and if statements and not just {{ variables }}.
import sys
# sometimes it's OK if a variable is undefined:
allowed_undefined_variables = [
'variable_1',
'variable_2',
]
if 'test' in sys.argv:
import django.template.base as template_base
old_resolve = template_base.Variable.resolve
def new_resolve(self, context):
try:
value = old_resolve(self, context)
except template_base.VariableDoesNotExist as e:
# if it's not a variable that's allowed to not exist then raise a
# base Exception so Nodes can't catch it (which will make the test
# fail)
if self.var not in allowed_undefined_variables:
raise Exception(e)
# re-raise the original and let the individual Nodes deal with it
# however they'd like
raise e
return value
template_base.Variable.resolve = new_resolve
How to log a warning on undefined variable in a template
It seems that Django relies on undefined variables being a simple empty string. So instead of changing this behaviour or making it throw an exception, let's keep it the same but have it log a warning instead!
In your settings.py file:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
# ...
'OPTIONS': {
# ...
'string_if_invalid': InvalidStringShowWarning("%s"),
},
}
]
(string_if_invalid replaces TEMPLATE_STRING_IF_INVALID in newer Django versions.)
And further up, you'll need to define the InvalidStringShowWarning class, making it behave while logging a warning:
class InvalidStringShowWarning(str):
def __mod__(self, other):
import logging
logger = logging.getLogger(__name__)
logger.warning("In template, undefined variable or unknown value for: '%s'" % (other,))
return ""
def __bool__(self): # if using Python 2, use __nonzero__ instead
# make the template tag `default` use its fallback value
return False
You should be able to see the warning in the output of python manage.py runserver.
I believe that's a major oversight on Django's part and the primary reason I prefer not to use their default template engine. The sad truth is that, at least for now (Django 1.9), you can't achieve this effect reliably.
You can make Django raise an exception when {{ undefined_variable }} is encountered - by using "the hack" described in slacy's answer.
You can't make Django raise the same exception on {% if undefined_variable %} or {% for x in undefined_variable %} etc. "The hack" doesn't work in such cases.
Even in cases in which you can, it is strongly discouraged by Django authors to use this technique in production environment. Unless you're sure you don't use Django's built-in templates in your app, you should use "the hack" only in DEBUG mode.
However, if you're stuck with Django's templates for now, I would recommend to use slacy's answer, just make sure you're in DEBUG mode.
Read up on how invalid variable are handled in templates. Basically, just set TEMPLATE_STRING_IF_INVALID to something in your settings.py.
TEMPLATE_STRING_IF_INVALID = "He's dead Jim! [%s]"
I am use next:
import logging
from django.utils.html import format_html
from django.utils.safestring import mark_safe
class InvalidTemplateVariable(str):
"""
Class for override output that the Django template system
determinated as invalid (e.g. misspelled) variables.
"""
# styles for display message in HTML`s pages
styles = mark_safe('style="color: red; font-weight: bold;"')
def __mod__(self, variable):
"""Overide a standart output here."""
# access to current settings
from django.conf import settings
# display the message on page in make log it only on stage development
if settings.DEBUG is True:
# format message with captured variable
msg = 'Attention! A variable "{}" does not exists.'.format(variable)
# get logger and make
logger = self.get_logger()
logger.warning(msg)
# mark text as non-escaped in HTML
return format_html('<i {}>{}</i>', self.styles, msg)
# on production it will be not displayed
return ''
def get_logger(self):
"""Create own logger with advanced error`s details."""
logger = logging.getLogger(self.__class__.__name__)
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
Usage in settings file (by default it settings.py):
TEMPLATES = [
{
......
'OPTIONS': {
.....................
'string_if_invalid': InvalidTemplateVariable('%s'),
.....................
},
},
]
or directly
TEMPLATES[0]['OPTIONS']['string_if_invalid'] = InvalidTemplateVariable('%s')
A result if DEBUG = True:
On page
In console
> System check identified 1 issue (0 silenced). October 03, 2016 -
> 12:21:40 Django version 1.10.1, using settings 'settings.development'
> Starting development server at http://127.0.0.1:8000/ Quit the server
> with CONTROL-C. 2016-10-03 12:21:44,472 - InvalidTemplateVariable -
> WARNING - Attention! A variable "form.media" does not exists.
You can use the pytest-django setting FAIL_INVALID_TEMPLATE_VARS
The invaild vars get checked if pytest executes the code.
[pytest]
DJANGO_SETTINGS_MODULE = mysite.settings
FAIL_INVALID_TEMPLATE_VARS = True
If there is a undefined variable in templates, django won't tell you.
You can print this variable in view.

Adding more CoC to Django

I come from a Cake background, and I'm just starting to learn Django now. I'm liking it quite a bit, but I kinda wish it used convention over configuration like cake does. So,
How can I get Cake-style URLs automatically? For example, if I went to mysite.com/posts/view/5 it would load up mysite.posts.views.view and pass an argument 5 to it? I was thinking I could add something like (r'^(.*)/(.*)', 'mysite.$1.$2'), to urls.py, but of course, that won't work.
How can I automatically load up a template? Each view function should automatically load a template like templates/posts/view.html.
Is this even possible, or do I have to hack the core of Django?
Here's my solution, based on what Carl suggested:
urlpatterns = patterns('',
# url pats here
url(r'^(?P<app>\w+)/(?P<view>\w+)/(?P<args>.*)$', 'urls.dispatch')
)
def dispatch(req, app, view, args): # FIXME: ignores decorators on view func!
func = get_callable(app+'.views.'+view)
if args:
ret = func(req, *args.split('/'))
else:
ret = func(req)
if type(ret) is dict:
return render_to_response(app+'/'+view+'.html', ret)
else:
return ret
Seems to be working pretty well with initial tests. Solves both problems with a single function. Probably won't support GET-style arguments tho.
Those points are both implementable without hacking Django core, but either one will require a non-trivial level of familiarity with advanced Python techniques.
You can do the generic URL pattern with a pattern like this:
url(r'^(?P<appname>\w+)/(?P<viewfunc>\w+)/(?P<args>.*)$', 'myresolverfunc')
Then define a 'myresolverfunc' "view" function that takes "appname", "viewfunc", and "args" parameters, and implement whatever logic you want, splitting args on "/" and dynamically importing and dispatching to whatever view function is referenced. The trickiest part is the dynamic import, you can search Django's source for "importlib" to see how dynamic imports are done internally various places.
The automatic template loader can be implemented as a view function decorator similar to the various "render_to" decorators out there, except you'll generate the template name rather than passing it in to the decorator. You'll have to introspect the function object to get its name. Getting the app name will be trickier; you'll probably just want to hardcode it as a module-level global in each views.py file, or else work in conjunction with the above URL dispatcher, and have it annotate the request object with the app name or some such.
I don't you'll need to hack the core of Django for this. It sounds like you might be in need of generic views. Also check out the Generic Views topic guide.
The first example given in the generic views documentation sounds like your first bullet point:
Example:
Given the following URL patterns:
urlpatterns = patterns('django.views.generic.simple',
(r'^foo/$', 'direct_to_template', {'template':'foo_index.html'}),
(r'^foo/(?P<id>\d+)/$', 'direct_to_template', {'template':'foo_detail.html'}),
)
... a request to /foo/ would render the template foo_index.html, and a request to /foo/15/ would render the foo_detail.html with a context variable {{ params.id }} that is set to 15.