Post on 18-Dec-2014
description
Advanced Templates2013-07-23. Django Workshop.
About me
• TP (@uranusjr)
• RTFD
• Find me anywhere
Language Review
{% if is_logged_in %}!
Thanks for logging in!!
{% else %}!
Welcome to {{ website_name }}.!
{% endif %}
Template tags
{% if is_logged_in %}!
Thanks for logging in!!
{% else %}!
Welcome to {{ website_name }}.!
{% endif %}
Variables
{% if is_logged_in %}!
Thanks for logging in!!
{% else %}!
Welcome to {{ website_name }}.!
{% endif %}
Language Review
• Variables come from a context
• Dictionary-like name-value mapping
• A context is rendered by the template
• Variable replacement
• Template tag execution
Template
__init__(self, string)
render(self, context)
format string
context
rendered value
text file
dict object
from django.shortcuts import render_to_response!!def view_1(request):! context_dict = {! 'app': 'My app',! 'user': request.user,! 'ip_address': request.META['REMOTE_ADDR'],! 'message': 'I am view 1.'! }! return render_to_response('template1.html',! context_dict)!!def view_2(request):! context_dict = {! 'app': 'My app',! 'user': request.user,! 'ip_address': request.META['REMOTE_ADDR'],! 'message': 'I am the second view.'! }! return render_to_response('template2.html',! context_dict)
from django.shortcuts import render_to_response!!def view_1(request):! context_dict = {! 'app': 'My app',! 'user': request.user,! 'ip_address': request.META['REMOTE_ADDR'],! 'message': 'I am view 1.'! }! return render_to_response('template1.html',! context_dict)!!def view_2(request):! context_dict = {! 'app': 'My app',! 'user': request.user,! 'ip_address': request.META['REMOTE_ADDR'],! 'message': 'I am the second view.'! }! return render_to_response('template2.html',! context_dict)
from django.template import loader, Context!!def view_1(request):! context_dict = {! 'app': 'My app',! 'user': request.user,! 'ip_address': request.META['REMOTE_ADDR'],! 'message': 'I am view 1.'! }! t = loader.get_template('template1.html')! context = Context(context_dict)! return t.render(context)!!def view_2(request):! context_dict = {! 'app': 'My app',! 'user': request.user,! 'ip_address': request.META['REMOTE_ADDR'],! 'message': 'I am the second view.'! }! t = loader.get_template('template2.html')! context = Context(context_dict)! return t.render(context)
from django.template import loader, Context!!def view_1(request):! context_dict = {! 'app': 'My app',! 'user': request.user,! 'ip_address': request.META['REMOTE_ADDR'],! 'message': 'I am view 1.'! }! t = loader.get_template('template1.html')! context = Context(context_dict)! return t.render(context)!!def view_2(request):! context_dict = {! 'app': 'My app',! 'user': request.user,! 'ip_address': request.META['REMOTE_ADDR'],! 'message': 'I am the second view.'! }! t = loader.get_template('template2.html')! context = Context(context_dict)! return t.render(context)
from django.template import loader, Context!!def render_it(template, context_dict):! context_dict.update({! 'app': 'My app',! 'user': request.user,! 'ip_address': request.META['REMOTE_ADDR']! })! return template.render(Context(context_dict))!!def view_1(request):! t = loader.get_template('template1.html')! context_dict = {'message': 'I am view 1.'}! return render_it(t, context_dict)!!def view_2(request):! t = loader.get_template('template2.html')! context_dict = {! 'message': 'I am the second view.'! }! return render_it(t, context_dict)
from django.template import loader, Context!!def render_it(template, context_dict):! context_dict.update({! 'app': 'My app',! 'user': request.user,! 'ip_address': request.META['REMOTE_ADDR']! })! return template.render(Context(context_dict))!!def view_1(request):! t = loader.get_template('template1.html')! context_dict = {'message': 'I am view 1.'}! return render_it(t, context_dict)!!def view_2(request):! t = loader.get_template('template2.html')! context_dict = {! 'message': 'I am the second view.'! }! return render_it(t, context_dict)
from django.template import loader, RequestContext!!def custom_proc(request):! return {! 'app': 'My app',! 'user': request.user,! 'ip_address': request.META['REMOTE_ADDR']! }!!def view_1(request):! t = loader.get_template('template1.html')! context = RequestContext(! request, {'message': 'I am view 1.'},! processors=[custom_proc]! )! return t.render(context)!!def view_2(request):! t = loader.get_template('template2.html')! context = RequestContext(! request, {'message': 'I am the second view.'},! processors=[custom_proc]! )! return t.render(context)
from django.template import loader, RequestContext!!def custom_proc(request):! return {! 'app': 'My app',! 'user': request.user,! 'ip_address': request.META['REMOTE_ADDR']! }!!def view_1(request):! t = loader.get_template('template1.html')! context = RequestContext(! request, {'message': 'I am view 1.'},! processors=[custom_proc]! )! return t.render(context)!!def view_2(request):! t = loader.get_template('template2.html')! context = RequestContext(! request, {'message': 'I am the second view.'},! processors=[custom_proc]! )! return t.render(context)
from django.template import loader, RequestContext!!def custom_proc(request):! return {! 'app': 'My app',! 'user': request.user,! 'ip_address': request.META['REMOTE_ADDR']! }!!def view_1(request):! t = loader.get_template('template1.html')! context = RequestContext(! request, {'message': 'I am view 1.'},! processors=[custom_proc]! )! return t.render(context)!!def view_2(request):! t = loader.get_template('template2.html')! context = RequestContext(! request, {'message': 'I am the second view.'},! processors=[custom_proc]! )! return t.render(context)
from django.shortcuts import render_to_response!from django.template import RequestContext!!def custom_proc(request):! return {! 'app': 'My app',! 'user': request.user,! 'ip_address': request.META['REMOTE_ADDR']! }!!def view_1(request):! context = RequestContext(request, processors=[custom_proc])! return render_to_response(! 'template1.html',! {'message': 'I am view 1.'},! context_instance=context! )!!def view_2(request):! context = RequestContext(request, processors=[custom_proc])! return render_to_response(! 'template2.html',! {'message': 'I am the second view.'},! context_instance=context! )
from django.shortcuts import render_to_response!from django.template import RequestContext!!def custom_proc(request):! return {! 'app': 'My app',! 'user': request.user,! 'ip_address': request.META['REMOTE_ADDR']! }!!def view_1(request):! context = RequestContext(request, processors=[custom_proc])! return render_to_response(! 'template1.html',! {'message': 'I am view 1.'},! context_instance=context! )!!def view_2(request):! context = RequestContext(request, processors=[custom_proc])! return render_to_response(! 'template2.html',! {'message': 'I am the second view.'},! context_instance=context! )
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.core.context_processors.tz',! 'django.contrib.messages.context_processors.messages',! 'myapp.views.custom_proc'!)
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',! 'django.core.context_processors.static',! 'django.core.context_processors.tz',! 'django.contrib.messages.context_processors.messages',! 'myapp.views.custom_proc'!)
settings.py
from django.shortcuts import render_to_response!from django.template import RequestContext!!def custom_proc(request):! return {! 'app': 'My app',! 'user': request.user,! 'ip_address': request.META['REMOTE_ADDR']! }!!def view_1(request):! context = RequestContext(request)! return render_to_response(! 'template1.html',! {'message': 'I am view 1.'},! context_instance=context! )!!def view_2(request):! context = RequestContext(request)! return render_to_response(! 'template2.html',! {'message': 'I am the second view.'},! context_instance=context! )
from django.shortcuts import render_to_response!from django.template import RequestContext!!def custom_proc(request):! return {! 'app': 'My app',! 'user': request.user,! 'ip_address': request.META['REMOTE_ADDR']! }!!def view_1(request):! context = RequestContext(request)! return render_to_response(! 'template1.html',! {'message': 'I am view 1.'},! context_instance=context! )!!def view_2(request):! context = RequestContext(request)! return render_to_response(! 'template2.html',! {'message': 'I am the second view.'},! context_instance=context! )
from django.shortcuts import render!!def custom_proc(request):! return {! 'app': 'My app',! 'user': request.user,! 'ip_address': request.META['REMOTE_ADDR']! }!!def view_1(request):! return render(! 'template1.html',! {'message': 'I am view 1.'}! )!!def view_2(request):! return render(! 'template2.html',! {'message': 'I am the second view.'}! )
Custom Processors
• Do one thing and do it well
• Pick your variable names
• Context keys are global
• General import conventions apply
• Breaking down render_to_response
• RequestContext and context processors
• TEMPLATE_CONTEXT_PROCESSORS
• django.shortcuts.render
Summary
HTML Escaping
• Variable values are escaped by default
• {{ something|safe }}
• {% autoescape off %} Not escaped: {{ something }} {% endautoescape %}!
• Literals in tags/filters are not escaped
• {{ escaped|default:'1 < 2' }}
Template Loading
• django.template.loader
• get_template(template_name)
• select_template(template_names)!
• TemplateDoesNotExist!
• TEMPLATE_DIRS!
• TEMPLATE_LOADERS
PROJECT_ROOT = …!!!TEMPLATE_DIRS = (! os.path.join(PROJECT_ROOT, 'templates'),! os.path.join(PROJECT_ROOT, 'other_templates')!)!!TEMPLATE_LOADERS = (! 'django.template.loaders.filesystem.Loader',! 'django.template.loaders.app_directories.Loader',! # 'django.template.loaders.eggs.Loader',!)
1
2
3
• Loader order
• Path order
• Third-party apps will be searched
• App directory search order is undefined
Questions?
Template Tags
Before We Start...
• This is a HUGE topic
• Read the documentation
• Learn how others do it
• Template processing is string manipulation
• Slow, period
• No fancy things unless necessary
Template Library
Template Library
Template Library
Name matters
Template Library
Don’t forget this
Template Library
Naming is important
Template Filter
• Takes one or zero arguments
• Always returns something
• Fails silently
{{ value|upper }}!!{{ value|add:arg }}
from django import template!!register = template.Library()!!!def upper(value):! """Converts a string into all uppercase."""! return value.upper()!!!def add(value, arg):! """Adds the arg to the value."""! return int(value) + int(arg)
from django import template!!register = template.Library()!!!def upper(value):! """Converts a string into all uppercase."""! return value.upper()!!!def add(value, arg):! """Adds the arg to the value."""! return int(value) + int(arg)
from django import template!!register = template.Library()!!!def upper(value):! """Converts a string into all uppercase."""! return value.upper()!!!def add(value, arg):! """Adds the arg to the value."""! return int(value) + int(arg)!!!register.filter('upper', upper)!register.filter('add', add)
from django import template!!register = template.Library()!!!@register.filter(name='upper')!def upper(value):! """Converts a string into all uppercase."""! return value.upper()!!!@register.filter(name='add')!def add(value, arg):! """Adds the arg to the value."""! return int(value) + int(arg)!
from django import template!!register = template.Library()!!!@register.filter!def upper(value):! """Converts a string into all uppercase."""! return value.upper()!!!@register.filter!def add(value, arg):! """Adds the arg to the value."""! return int(value) + int(arg)!
@register.filter!def upper(value):! """Converts a string into all uppercase."""! return value.upper()!!!@register.filter!def add(value, arg):! """Adds the arg to the value."""! try:! return int(value) + int(arg)! except (ValueError, TypeError):! try:! return value + arg! except Exception:! return '' # Return something
@register.filter!def upper(value):! """Converts a string into all uppercase."""! return value.upper() # Let it fail!!!@register.filter!def add(value, arg):! """Adds the arg to the value."""! try:! return int(value) + int(arg)! except (ValueError, TypeError):! try:! return value + arg! except Exception:! return ''
{{ value|upper }}!!{{ value|add:arg }}
{% load myapp_tags %}!!{{ value|upper }}!!{{ value|add:arg }}
{{ value|date:arg }}
from django.conf import settings!from django.utils import formats!!@register.filter!def date(value, arg=None):! """Formats a date according to the given format."""! if value in (None, ''):! return ''! if arg is None:! arg = settings.DATE_FORMAT! try:! return formats.date_format(value, arg)! except AttributeError:! try:! return format(value, arg)! except AttributeError:! return ''
from django.conf import settings!from django.utils import formats!!@register.filter!def date(value, arg=None):! """Formats a date according to the given format."""! if value in (None, ''):! return ''! if arg is None:! arg = settings.DATE_FORMAT! try:! return formats.date_format(value, arg)! except AttributeError:! try:! return format(value, arg)! except AttributeError:! return ''
from django.conf import settings!from django.utils import formats!!@register.filter!def date(value, arg=None):! """Formats a date according to the given format."""! if value in (None, ''):! return ''! if arg is None:! arg = settings.DATE_FORMAT! try:! return formats.date_format(value, arg)! except AttributeError:! try:! return format(value, arg)! except AttributeError:! return ''
from django.conf import settings!from django.utils import formats!!@register.filter!def date(value, arg=None):! """Formats a date according to the given format."""! if value in (None, ''):! return ''! if arg is None:! arg = settings.DATE_FORMAT! try:! return formats.date_format(value, arg)! except AttributeError:! try:! return format(value, arg)! except AttributeError:! return ''
Return Something
• Not necessarily a string
• Only need to be convertible to a string
• None is converted to 'None', not ''
• I always return strings
Template Tags
Compilation function
Template tag node
__init__(self, string)
render(self, context)
template
context
rendered value
{% now format_string %}
from django import template!from django.template import TemplateSyntaxError!!register = template.Library()!!!@register.tag!def now(parser, token):! bits = token.split_contents()! if len(bits) != 2:! raise TemplateSyntaxError(! "'now' statement takes one argument"! )! format_string = bits[1][1:-1]! return NowNode(format_string)!
from django import template!from django.template import TemplateSyntaxError!!register = template.Library()!!!@register.tag!def now(parser, token):! bits = token.split_contents()! if len(bits) != 2:! raise TemplateSyntaxError(! "'now' statement takes one argument"! )! format_string = bits[1][1:-1]! return NowNode(format_string)!
from django import template!from django.template import TemplateSyntaxError!!register = template.Library()!!!@register.tag!def now(parser, token):! bits = token.split_contents()! if len(bits) != 2:! raise TemplateSyntaxError(! "'now' statement takes one argument"! )! format_string = bits[1][1:-1]! return NowNode(format_string)!
from django import template!from django.template import TemplateSyntaxError!!register = template.Library()!!!@register.tag!def now(parser, token):! bits = token.split_contents()! if len(bits) != 2:! raise TemplateSyntaxError(! "'now' statement takes one argument"! )! format_string = bits[1][1:-1]! return NowNode(format_string)!
from django import template!from django.template import TemplateSyntaxError!!register = template.Library()!!!@register.tag!def now(parser, token):! bits = token.split_contents()! if len(bits) != 2:! raise TemplateSyntaxError(! "'now' statement takes one argument"! )! format_string = bits[1][1:-1]! return NowNode(format_string)!
from django import template!from django.template import TemplateSyntaxError!!register = template.Library()!!!@register.tag!def now(parser, token):! bits = token.split_contents()! if len(bits) != 2:! raise TemplateSyntaxError(! "'now' statement takes one argument"! )! format_string = bits[1][1:-1]! return NowNode(format_string)!
from datetime import datetime!!!class NowNode(Node):! def __init__(self, format_string):! self.format_string = format_string!! def render(self, context):! now = datetime.now()! return now.strftime(self.format_string)
from datetime import datetime!!!class NowNode(Node):! def __init__(self, format_string):! self.format_string = format_string!! def render(self, context):! now = datetime.now()! return now.strftime(self.format_string)
from datetime import datetime!from django.conf import settings!from django.template.defaultfilters import date!from django.utils import timezone!!!class NowNode(Node):! def __init__(self, format_string):! self.format_string = format_string!! def render(self, context):! if settings.USE_TZ:! tzinfo = timezone.get_current_timezone()! else:! tzinfo = None! return date(! datetime.now(tz=tzinfo),! self.format_string! )
from datetime import datetime!from django.conf import settings!from django.template.defaultfilters import date!from django.utils import timezone!!!class NowNode(Node):! def __init__(self, format_string):! self.format_string = format_string!! def render(self, context):! if settings.USE_TZ:! tzinfo = timezone.get_current_timezone()! else:! tzinfo = None! return date(! datetime.now(tz=tzinfo),! self.format_string! )
{% inject_now format_string %}!!<p>The time is {{ now }}</p>
from django import template!from django.template import TemplateSyntaxError!!register = template.Library()!!!@register.tag!def inject_now(parser, token):! bits = token.split_contents()! if len(bits) != 2:! raise TemplateSyntaxError(! "'inject_now' requires one argument"! )! format_string = bits[1][1:-1]! return InjectNowNode(format_string)!
class InjectNowNode(Node):! def __init__(self, format_string):! self.format_string = format_string!! def render(self, context):! if settings.USE_TZ:! tzinfo = timezone.get_current_timezone()! else:! tzinfo = None! context['now'] = date(! datetime.now(tz=tzinfo),! self.format_string! )! return ''
class InjectNowNode(Node):! def __init__(self, format_string):! self.format_string = format_string!! def render(self, context):! if settings.USE_TZ:! tzinfo = timezone.get_current_timezone()! else:! tzinfo = None! context['now'] = date(! datetime.now(tz=tzinfo),! self.format_string! )! return ''
{% inject_now format_string as var_name %}!!<p>The time now is {{ var_name }}</p>
@register.tag!def inject_now(parser, token):! bits = token.split_contents()! if len(bits) != 4:! raise TemplateSyntaxError(! "'inject_now' statement requires form "! "{% inject_now format as var_name %}."! )! format_string = bits[1][1:-1]! var_name = bits[3]! return InjectNowNode(format_string, var_name)
@register.tag!def inject_now(parser, token):! error = TemplateSyntaxError(! "'inject_now' statement requires form "! "{% inject_now format as var_name %}."! )! try:! tag_name, arg = token.contents.split(None, 1)! except ValueError:! raise error!! m = re.search(r'(.*?) as (\w+)', arg)! if m:! fmt, var_name = m.groups()! else:! raise error! if not (fmt[0] == fmt[-1] and fmt[0] in ('"', "'")):! raise error!! return InjectNowNode(fmt[1:-1], var_name)
class InjectNowNode(Node):! def __init__(self, format_string, var_name):! self.format_string = format_string! self.var_name = var_name!! def render(self, context):! if settings.USE_TZ:! tzinfo = timezone.get_current_timezone()! else:! tzinfo = None! context[self.var_name] = date(! datetime.now(tz=tzinfo),! self.format_string! )! return ''
Start-end tags
• parser.parse(end_tags_tuple)!
• Returns a NodeList instance (iterable)
• parser.delete_first_token()
• Deletes next token (i.e. the end token)
def do_comment(parser, token):! nodelist = parser.parse(('endcomment',))! parser.delete_first_token()! return CommentNode()!!!class CommentNode(template.Node):! def render(self, context):! return ''
def do_comment(parser, token):! nodelist = parser.parse(('endcomment',))! parser.delete_first_token()! return CommentNode()!!!class CommentNode(template.Node):! def render(self, context):! return ''
def do_comment(parser, token):! nodelist = parser.parse(('endcomment',))! parser.delete_first_token()! return CommentNode()!!!class CommentNode(template.Node):! def render(self, context):! return ''
Shortcuts
• register.simple_tag
• register.inclusion_tag(template)!
• takes_context=True!
• stringfilter
Custom Loaders
• implement load_template_source
• Raise TemplateDoesNotExist when appropriate
• Add it to TEMPLATE_LOADERS in settings
Standalone Mode
• django.conf.settings.configure()
• I’d just use another template engine
• Jinja2 + Coffin
• Read Appendix D or the docs if you really know what you’re doing
Again...
• This is a HUGE topic
• Read the documentation
• Learn how others do it
• Template processing is string manipulation
• Slow, period
• No fancy things unless necessary
Questions?