I would like to be able to render some or all of the views in my project with a different base template. In other words, for url /some/view I would like to be able to have /inline/some/view and have it render the same content, but using a different base template.
Modifying each view to accept a different template is not an option, because I would like to apply this behaviour across all apps in the project, including things like django.contrib.auth.
So far, I have, in urls.py:
url("^inline/(?P<uri>.*)", format.inline, name='inline'),
And the view, format.py:
from django.core.urlresolvers import resolve
def inline(request, uri=''):
# get the view that would normally handle this request
view, args, kwargs = resolve('/' + uri)
# call the view
kwargs['request'] = request
template_response = view(*args, **kwargs)
# ...now what?
I'm not sure where to go from here. Can I modify the entire template chain before I call view(), so that template_response.render() does the right thing?
Perhaps I am entirely off-base with this approach and should be looking at a middleware solution, but I am attached to the idea of this behaviour keying off URLs, because it will be easy to explain to the content people later on.
UPDATE
I was able to achieve the effect I desired, but the implementation is severely lacking. Here's what I did:
copied the templates for the views I wished to inline into templates/inline/
replaced {% extends base.html %} with {% extends inline/base.html %}
modified the view thus:
from django.core.urlresolvers import resolve
def inline(request, uri=''):
# get the view that would normally handle this request
view, args, kwargs = resolve('/' + uri)
# call the view
kwargs['request'] = request
template_response = view(*args, **kwargs)
response.template_name = os.path.join('inline', response.template_name)
return response
I don't like this solution because it will require those inline templates to be managed, being replaced/updated whenever apps in the project change, and so on. I would still dearly love a cleaner solution.
Update 2: Solution
chris-wesseling was 100% correct; a custom template loader was exactly what I needed. For posterity, here is my implementation.
app/loaders.py:
from django.conf import settings
from django.template.loader import BaseLoader
from django.template.base import TemplateDoesNotExist
import os
class BaseTemplateOverrideLoader(BaseLoader):
"""
Load templates from a specified subdirectory in the current app's directory.
"""
subdir = 'templates'
def load_template_source(self, template_name, template_dirs=None):
template_dir = os.path.join(
os.path.dirname(os.path.realpath(__file__)),
self.subdir
)
try:
t = os.path.join(template_dir, template_name)
with open(t, 'rb') as fp:
return (fp.read().decode(settings.FILE_CHARSET), template_dir)
except IOError:
pass
raise TemplateDoesNotExist(template_name)
class InlineTemplateLoader(BaseTemplateOverrideLoader):
"""
Override the location of base.html for inline views.
"""
is_usable = True
subdir = 'templates/inline'
# ... other custom override classes here ....
app/views/inline.py:
from django.conf import settings
from django.core.urlresolvers import resolve
from django.template import loader
def default(request, slug=None):
view, args, kwargs = resolve('/' + slug)
old_loaders = settings.TEMPLATE_LOADERS
# Temporarily insert the inline template loader into TEMPLATE_LOADERS;
# we must also force BaseLoader to reload all templates loaders since
# they are cached at compile time.
settings.TEMPLATE_LOADERS = ('app.loaders.InlineTemplateLoader', ) + \
settings.TEMPLATE_LOADERS
loader.template_source_loaders = None
# now call the original view that handles this request
kwargs['request'] = request
response = view(*args, **kwargs)
response_string = response.render()
# restore the template loaders to their original condition
settings.TEMPLATE_LOADERS = old_loaders
loader.template_source_loaders = None
return response_string
app/templates/inline/base.html:
{% comment %}
inline/base.html
-- render just the main content without any styles etc,
for loading as inline content via ajax or whatever.
{% endcomment %}
{% block main %}{% endblock %}
You can implement your own TemplateLoader and set it in your settings.TEMPLATE_LOADERS. You can have a look at this similar question for an approach of what you're trying to do.
Basically what you're looking for is a way to load base.html from a different location.
I have some stuff in settings.py that I'd like to be able to access from a template, but I can't figure out how to do it. I already tried
{{CONSTANT_NAME}}
but that doesn't seem to work. Is this possible?
If it's a value you'd like to have for every request & template, using a context processor is more appropriate.
Here's how:
Make a context_processors.py file in your app directory. Let's say I want to have the ADMIN_PREFIX_VALUE value in every context:
from django.conf import settings # import the settings file
def admin_media(request):
# return the value you want as a dictionnary. you may add multiple values in there.
return {'ADMIN_MEDIA_URL': settings.ADMIN_MEDIA_PREFIX}
add your context processor to your settings.py file:
TEMPLATES = [{
# whatever comes before
'OPTIONS': {
'context_processors': [
# whatever comes before
"your_app.context_processors.admin_media",
],
}
}]
Use RequestContext in your view to add your context processors in your template. The render shortcut does this automatically:
from django.shortcuts import render
def my_view(request):
return render(request, "index.html")
and finally, in your template:
...
path to admin media
...
I find the simplest approach being a single custom template tag:
from django import template
from django.conf import settings
register = template.Library()
# settings value
#register.simple_tag
def settings_value(name):
return getattr(settings, name, "")
Usage:
{% settings_value "LANGUAGE_CODE" %}
Django provides access to certain, frequently-used settings constants to the template such as settings.MEDIA_URL and some of the language settings if you use django's built in generic views or pass in a context instance keyword argument in the render_to_response shortcut function. Here's an example of each case:
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.views.generic.simple import direct_to_template
def my_generic_view(request, template='my_template.html'):
return direct_to_template(request, template)
def more_custom_view(request, template='my_template.html'):
return render_to_response(template, {}, context_instance=RequestContext(request))
These views will both have several frequently used settings like settings.MEDIA_URL available to the template as {{ MEDIA_URL }}, etc.
If you're looking for access to other constants in the settings, then simply unpack the constants you want and add them to the context dictionary you're using in your view function, like so:
from django.conf import settings
from django.shortcuts import render_to_response
def my_view_function(request, template='my_template.html'):
context = {'favorite_color': settings.FAVORITE_COLOR}
return render_to_response(template, context)
Now you can access settings.FAVORITE_COLOR on your template as {{ favorite_color }}.
Check out django-settings-export (disclaimer: I'm the author of this project).
For example...
$ pip install django-settings-export
settings.py
TEMPLATES = [
{
'OPTIONS': {
'context_processors': [
'django_settings_export.settings_export',
],
},
},
]
MY_CHEESE = 'Camembert';
SETTINGS_EXPORT = [
'MY_CHEESE',
]
template.html
<script>var MY_CHEESE = '{{ settings.MY_CHEESE }}';</script>
Another way to do this is to create a custom template tag which can let you fish values out of the settings.
#register.tag
def value_from_settings(parser, token):
try:
# split_contents() knows not to split quoted strings.
tag_name, var = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError, "%r tag requires a single argument" % token.contents.split()[0]
return ValueFromSettings(var)
class ValueFromSettings(template.Node):
def __init__(self, var):
self.arg = template.Variable(var)
def render(self, context):
return settings.__getattr__(str(self.arg))
You can then use:
{% value_from_settings "FQDN" %}
to print it on any page, without jumping through context-processor hoops.
I like Berislav's solution, because on simple sites, it is clean and effective. What I do NOT like is exposing all the settings constants willy-nilly. So what I ended up doing was this:
from django import template
from django.conf import settings
register = template.Library()
ALLOWABLE_VALUES = ("CONSTANT_NAME_1", "CONSTANT_NAME_2",)
# settings value
#register.simple_tag
def settings_value(name):
if name in ALLOWABLE_VALUES:
return getattr(settings, name, '')
return ''
Usage:
{% settings_value "CONSTANT_NAME_1" %}
This protects any constants that you have not named from use in the template, and if you wanted to get really fancy, you could set a tuple in the settings, and create more than one template tag for different pages, apps or areas, and simply combine a local tuple with the settings tuple as needed, then do the list comprehension to see if the value is acceptable.
I agree, on a complex site, this is a bit simplistic, but there are values that would be nice to have universally in templates, and this seems to work nicely.
Thanks to Berislav for the original idea!
Adding an answer with complete instructions for creating a custom template tag that solves this, with Django 2.0+
In your app-folder, create a folder called templatetags. In it, create __init__.py and custom_tags.py:
In the custom_tags.py create a custom tag function that provides access to an arbitrary key in the settings constant:
from django import template
from django.conf import settings
register = template.Library()
#register.simple_tag
def get_setting(name):
return getattr(settings, name, "")
To understand this code I recommend reading the section on simple tags in the Django docs.
Then, you need to make Django aware of this (and any additional) custom tag by loading this file in any template where you will use it. Just like you need to load the built in static tag:
{% load custom_tags %}
With it loaded it can be used just like any other tag, just supply the specific setting you need returned. So if you have a BUILD_VERSION variable in your settings:
{% get_setting "BUILD_VERSION" %}
This solution will not work with arrays, but if you need that you might be putting to much logic in your templates.
Note: A more clean and failsafe solution would probably be to make a custom context processor where you add the settings you need to a context available to all templates. This way you reduce the risk of outputting sensitive settings in your templates by mistake.
Add this code to a file called context_processors.py:
from django.conf import settings as django_settings
def settings(request):
return {
'settings': django_settings,
}
And then, in your settings file, include a path such as 'speedy.core.base.context_processors.settings' (with your app name and path) in the 'context_processors' settings in TEMPLATES.
(You can see for example settings/base.py and context_processors.py).
Then you can use the specific setting in any template code. For example:
{% if settings.SITE_ID == settings.SPEEDY_MATCH_SITE_ID %}
Update: The code above exposes all the settings to templates, including sensitive information such as your SECRET_KEY. A hacker might abuse this feature to display such information in the templates. If you want to expose only specific settings to the templates, use this code instead:
def settings(request):
settings_in_templates = {}
for attr in ["SITE_ID", ...]: # Write here the settings you want to expose to the templates.
if (hasattr(django_settings, attr)):
settings_in_templates[attr] = getattr(django_settings, attr)
return {
'settings': settings_in_templates,
}
I improved chrisdew's answer (to create your own tag) a little bit.
First, create the file yourapp/templatetags/value_from_settings.py in which you define your own new tag value_from_settings:
from django.template import TemplateSyntaxError, Variable, Node, Variable, Library
from yourapp import settings
register = Library()
# I found some tricks in URLNode and url from defaulttags.py:
# https://code.djangoproject.com/browser/django/trunk/django/template/defaulttags.py
#register.tag
def value_from_settings(parser, token):
bits = token.split_contents()
if len(bits) < 2:
raise TemplateSyntaxError("'%s' takes at least one " \
"argument (settings constant to retrieve)" % bits[0])
settingsvar = bits[1]
settingsvar = settingsvar[1:-1] if settingsvar[0] == '"' else settingsvar
asvar = None
bits = bits[2:]
if len(bits) >= 2 and bits[-2] == 'as':
asvar = bits[-1]
bits = bits[:-2]
if len(bits):
raise TemplateSyntaxError("'value_from_settings' didn't recognise " \
"the arguments '%s'" % ", ".join(bits))
return ValueFromSettings(settingsvar, asvar)
class ValueFromSettings(Node):
def __init__(self, settingsvar, asvar):
self.arg = Variable(settingsvar)
self.asvar = asvar
def render(self, context):
ret_val = getattr(settings,str(self.arg))
if self.asvar:
context[self.asvar] = ret_val
return ''
else:
return ret_val
You can use this tag in your Template via:
{% load value_from_settings %}
[...]
{% value_from_settings "FQDN" %}
or via
{% load value_from_settings %}
[...]
{% value_from_settings "FQDN" as my_fqdn %}
The advantage of the as ... notation is that this makes it easy to use in blocktrans blocks via a simple {{my_fqdn}}.
If using a class-based view:
#
# in settings.py
#
YOUR_CUSTOM_SETTING = 'some value'
#
# in views.py
#
from django.conf import settings #for getting settings vars
class YourView(DetailView): #assuming DetailView; whatever though
# ...
def get_context_data(self, **kwargs):
context = super(YourView, self).get_context_data(**kwargs)
context['YOUR_CUSTOM_SETTING'] = settings.YOUR_CUSTOM_SETTING
return context
#
# in your_template.html, reference the setting like any other context variable
#
{{ YOUR_CUSTOM_SETTING }}
The example above from bchhun is nice except that you need to explicitly build your context dictionary from settings.py. Below is an UNTESTED example of how you could auto-build the context dictionary from all upper-case attributes of settings.py (re: "^[A-Z0-9_]+$").
At the end of settings.py:
_context = {}
local_context = locals()
for (k,v) in local_context.items():
if re.search('^[A-Z0-9_]+$',k):
_context[k] = str(v)
def settings_context(context):
return _context
TEMPLATE_CONTEXT_PROCESSORS = (
...
'myproject.settings.settings_context',
...
)
If someone finds this question like I did, then I'll post my solution which works on Django 2.0:
This tag assigns some settings.py variable value to template's variable:
Usage: {% get_settings_value template_var "SETTINGS_VAR" %}
app/templatetags/my_custom_tags.py:
from django import template
from django.conf import settings
register = template.Library()
class AssignNode(template.Node):
def __init__(self, name, value):
self.name = name
self.value = value
def render(self, context):
context[self.name] = getattr(settings, self.value.resolve(context, True), "")
return ''
#register.tag('get_settings_value')
def do_assign(parser, token):
bits = token.split_contents()
if len(bits) != 3:
raise template.TemplateSyntaxError("'%s' tag takes two arguments" % bits[0])
value = parser.compile_filter(bits[2])
return AssignNode(bits[1], value)
Your template:
{% load my_custom_tags %}
# Set local template variable:
{% get_settings_value settings_debug "DEBUG" %}
# Output settings_debug variable:
{{ settings_debug }}
# Use variable in if statement:
{% if settings_debug %}
... do something ...
{% else %}
... do other stuff ...
{% endif %}
See Django's documentation how to create custom template tags here: https://docs.djangoproject.com/en/2.0/howto/custom-template-tags/
I found this to be the simplest approach for Django 1.3:
views.py
from local_settings import BASE_URL
def root(request):
return render_to_response('hero.html', {'BASE_URL': BASE_URL})
hero.html
var BASE_URL = '{{ JS_BASE_URL }}';
For those who want to use #Berislav's approach (custom template tag) with if tag:
/app/templatetags/my_settings.py:
from django import template
from django.conf import settings
register = template.Library()
#register.simple_tag
def settings_value(name):
return getattr(settings, name, "")
Template file:
<!-- Load your tags -->
{% load my_settings %}
{% settings_value 'ENABLE_FEATURE_A' as ENABLE_FEATURE_A %}
{% if ENABLE_FEATURE_A %}
<!-- Feature A stuffs -->
{% endif %}
Both IanSR and bchhun suggested overriding TEMPLATE_CONTEXT_PROCESSORS in the settings. Be aware that this setting has a default that can cause some screwy things if you override it without re-setting the defaults. The defaults have also changed in recent versions of Django.
https://docs.djangoproject.com/en/1.3/ref/settings/#template-context-processors
The default TEMPLATE_CONTEXT_PROCESSORS :
TEMPLATE_CONTEXT_PROCESSORS = ("django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.static",
"django.contrib.messages.context_processors.messages")
If we were to compare context vs. template tags on a single variable, then knowing the more efficient option could be benificial. However, you might be better off to dip into the settings only from templates that need that variable. In that case it doesn't make sense to pass the variable into all templates. But if you are sending the variable into a common template such as the base.html template, Then it would not matter as the base.html template is rendered on every request, so you can use either methods.
If you decide to go with the template tags option, then use the following code as it allows you to pass a default value in, just in case the variable in-question was undefined.
Example: get_from_settings my_variable as my_context_value
Example: get_from_settings my_variable my_default as my_context_value
class SettingsAttrNode(Node):
def __init__(self, variable, default, as_value):
self.variable = getattr(settings, variable, default)
self.cxtname = as_value
def render(self, context):
context[self.cxtname] = self.variable
return ''
def get_from_setting(parser, token):
as_value = variable = default = ''
bits = token.contents.split()
if len(bits) == 4 and bits[2] == 'as':
variable = bits[1]
as_value = bits[3]
elif len(bits) == 5 and bits[3] == 'as':
variable = bits[1]
default = bits[2]
as_value = bits[4]
else:
raise TemplateSyntaxError, "usage: get_from_settings variable default as value " \
"OR: get_from_settings variable as value"
return SettingsAttrNode(variable=variable, default=default, as_value=as_value)
get_from_setting = register.tag(get_from_setting)
A more complete implementation.
/project/settings.py
APP_NAME = 'APP'
/app/templatetags/settings_value.py
from django import template
from django.conf import settings
register = template.Library()
#register.simple_tag
def settings_value(name):
return getattr(settings, name, "")
/app/templates/index.html
<!DOCTYPE html>
{% load static %}
{% load settings_value %}
<head>
<title>{% settings_value "APP_NAME" %}</title>
...
I am trying to add a feature to my app that would allow me to enable/disable the "Call Me" button based on whether or not I am at [home|the office]. I created a model in the database called setting, it looks like this:
class setting(models.Model):
key = models.CharField(max_length=200)
value = models.CharField(max_length=200)
Pretty simple. There is currently one row, available, the value of it is the string True. I want to be able to transparently pass variables to the templates like this:
{% if available %}
<!-- Display button -->
{% else %}
<!-- Display grayed out button -->
{% endif %}
Now, I could add logic to every view that would check the database, and pass the variable to the template, but I am trying to stay DRY.
What is the best way to do this?
UPDATE
I created a context processor, and added it's path to the TEMPLATE_CONTEXT_PROCESSORS, but it is not being passed to the template
def available(request):
available = Setting.objects.get(key="available")
if open.value == "True":
return {"available":True}
else:
return {}
UPDATE TWO
If you are using the shortcut render_to_response, you need to pass an instance of RequestContext to the function.
from the django documentation:
If you're using Django's render_to_response() shortcut to populate a template with the contents of a dictionary, your template will be passed a Context instance by default (not a RequestContext). To use a RequestContext in your template rendering, pass an optional third argument to render_to_response(): a RequestContext instance. Your code might look like this:
def some_view(request):
# ...
return render_to_response('my_template.html',
my_data_dictionary,
context_instance=RequestContext(request))
Many thanks for all the help!
Write a custom context processor.
I am working on a small intranet site for a small company, where user should be able to post. I have imagined a very simple authentication mechanism where people just enter their email address, and gets sent a unique login url, that sets a cookie that will always identify them for future requests.
In my template setup, I have base.html, and the other pages extend this. I want to show logged in or register button in the base.html, but how can I ensure that the necessary variables are always a part of the context? It seems that each view just sets up the context as they like, and there is no global context population. Is there a way of doing this without including the user in each context creation?
Or will I have to make my own custom shortcuts to setup the context properly?
There is no need to write a context processor for the user object if you already have the "django.core.context_processors.auth" in TEMPLATE_CONTEXT_PROCESSORS and if you're using RequestContext in your views.
if you are using django 1.4 or latest the module has been moved to django.contrib.auth.context_processors.auth
#Ryan: Documentation about preprocessors is a bit small
#Staale: Adding user to the Context every time one is calling the template in view, DRY
Solution is to use a preprocessor
A: In your settings add
TEMPLATE_CONTEXT_PROCESSORS = (
'myapp.processor_file_name.user',
)
B: In myapp/processor_file_name.py insert
def user(request):
if hasattr(request, 'user'):
return {'user':request.user }
return {}
From now on you're able to use user object functionalities in your templates.
{{ user.get_full_name }}
In a more general sense of not having to explicitly set variables in each view, it sounds like you want to look at writing your own context processor.
From the docs:
A context processor has a very simple interface: It's just a Python function that takes one argument, an HttpRequest object, and returns a dictionary that gets added to the template context. Each context processor must return a dictionary.
The hints are in every answer, but once again, from "scratch", for newbies:
authentication data is in templates (almost) by default -- with a small trick:
in views.py:
from django.template import RequestContext
...
def index(request):
return render_to_response('index.html',
{'var': 'value'},
context_instance=RequestContext(request))
in index.html:
...
Hi, {{ user.username }}
var: {{ value }}
...
From here: https://docs.djangoproject.com/en/1.4/topics/auth/#authentication-data-in-templates
This template context variable is not available if a RequestContext is
not being used.
#Dave
To use {{user.username}} in my templates, I will then have to use
requestcontext rather than just a normal map/hash: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-requestcontext
So I guess there are no globals that the template engine checks.
But the RequestContext has some prepopulate classes that I can look into to solve my problems. Thanks.
If you can hook your authentication into the Django authentication scheme you'll be able to use request.user.
I think this should just be a case of calling authenticate() and login() based on the contents of your Cookie.
Edit: #Staale - I always use the locals() trick for my context so all my templates can see request and so request.user. If you're not then I guess it wouldn't be so straightforward.
its possible by default, by doing the following steps, ensure you have added the context 'django.contrib.auth.context_processors.auth' in your settings. By default its added in settings.py, so its looks like this
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.core.context_processors.auth',)
And you can access user object like this,
{% if user.is_authenticated %}
<p>Welcome, {{ user.username }}. Thanks for logging in.</p>
{% else %}
<p>Welcome, new user. Please log in.</p>
{% endif %}
For more information, refer here http://docs.djangoproject.com/en/1.2/topics/auth/#authentication-data-in-templates
Use context_processors. https://docs.djangoproject.com/en/2.2/ref/settings/#std:setting-TEMPLATES-OPTIONS
settings.py
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'myapp.functions.test'
],
},
myapp/functions.py
def test(request):
return {'misc': 'misc'}