SlideShare a Scribd company logo
1 of 84
Download to read offline
Advanced Templates
2013-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?

More Related Content

What's hot

Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologyDaniel Knell
 
Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Konstantin Kudryashov
 
Workshop 8: Templating: Handlebars, DustJS
Workshop 8: Templating: Handlebars, DustJSWorkshop 8: Templating: Handlebars, DustJS
Workshop 8: Templating: Handlebars, DustJSVisual Engineering
 
Class-based views with Django
Class-based views with DjangoClass-based views with Django
Class-based views with DjangoSimon Willison
 
Jquery Complete Presentation along with Javascript Basics
Jquery Complete Presentation along with Javascript BasicsJquery Complete Presentation along with Javascript Basics
Jquery Complete Presentation along with Javascript BasicsEPAM Systems
 
jQuery Loves Developers - Oredev 2009
jQuery Loves Developers - Oredev 2009jQuery Loves Developers - Oredev 2009
jQuery Loves Developers - Oredev 2009Remy Sharp
 
Building High Performance Web Applications and Sites
Building High Performance Web Applications and SitesBuilding High Performance Web Applications and Sites
Building High Performance Web Applications and Sitesgoodfriday
 
Powerful Generic Patterns With Django
Powerful Generic Patterns With DjangoPowerful Generic Patterns With Django
Powerful Generic Patterns With DjangoEric Satterwhite
 
The Django Book, Chapter 16: django.contrib
The Django Book, Chapter 16: django.contribThe Django Book, Chapter 16: django.contrib
The Django Book, Chapter 16: django.contribTzu-ping Chung
 
Object Oriented JavaScript
Object Oriented JavaScriptObject Oriented JavaScript
Object Oriented JavaScriptDonald Sipe
 
Basics of Java Script (JS)
Basics of Java Script (JS)Basics of Java Script (JS)
Basics of Java Script (JS)Ajay Khatri
 

What's hot (20)

jQuery
jQueryjQuery
jQuery
 
Headless Js Testing
Headless Js TestingHeadless Js Testing
Headless Js Testing
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
 
Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015
 
Workshop 8: Templating: Handlebars, DustJS
Workshop 8: Templating: Handlebars, DustJSWorkshop 8: Templating: Handlebars, DustJS
Workshop 8: Templating: Handlebars, DustJS
 
Class-based views with Django
Class-based views with DjangoClass-based views with Django
Class-based views with Django
 
Frontin like-a-backer
Frontin like-a-backerFrontin like-a-backer
Frontin like-a-backer
 
jQuery introduction
jQuery introductionjQuery introduction
jQuery introduction
 
Jquery Complete Presentation along with Javascript Basics
Jquery Complete Presentation along with Javascript BasicsJquery Complete Presentation along with Javascript Basics
Jquery Complete Presentation along with Javascript Basics
 
Advanced Django
Advanced DjangoAdvanced Django
Advanced Django
 
jQuery Loves Developers - Oredev 2009
jQuery Loves Developers - Oredev 2009jQuery Loves Developers - Oredev 2009
jQuery Loves Developers - Oredev 2009
 
Building High Performance Web Applications and Sites
Building High Performance Web Applications and SitesBuilding High Performance Web Applications and Sites
Building High Performance Web Applications and Sites
 
Powerful Generic Patterns With Django
Powerful Generic Patterns With DjangoPowerful Generic Patterns With Django
Powerful Generic Patterns With Django
 
The Django Book, Chapter 16: django.contrib
The Django Book, Chapter 16: django.contribThe Django Book, Chapter 16: django.contrib
The Django Book, Chapter 16: django.contrib
 
jQuery in 15 minutes
jQuery in 15 minutesjQuery in 15 minutes
jQuery in 15 minutes
 
jQuery Essentials
jQuery EssentialsjQuery Essentials
jQuery Essentials
 
Min-Maxing Software Costs
Min-Maxing Software CostsMin-Maxing Software Costs
Min-Maxing Software Costs
 
Object Oriented JavaScript
Object Oriented JavaScriptObject Oriented JavaScript
Object Oriented JavaScript
 
Basics of Java Script (JS)
Basics of Java Script (JS)Basics of Java Script (JS)
Basics of Java Script (JS)
 
Jquery 2
Jquery 2Jquery 2
Jquery 2
 

Viewers also liked

Django - The Web framework for perfectionists with deadlines
Django - The Web framework for perfectionists with deadlinesDjango - The Web framework for perfectionists with deadlines
Django - The Web framework for perfectionists with deadlinesMarkus Zapke-Gründemann
 
Rabbitmq & Postgresql
Rabbitmq & PostgresqlRabbitmq & Postgresql
Rabbitmq & PostgresqlLucio Grenzi
 
Django e il Rap Elia Contini
Django e il Rap Elia ContiniDjango e il Rap Elia Contini
Django e il Rap Elia ContiniWEBdeBS
 
라이트닝 토크 2015 파이콘
라이트닝 토크 2015 파이콘라이트닝 토크 2015 파이콘
라이트닝 토크 2015 파이콘Jiho Lee
 
2016 py con2016_lightingtalk_php to python
2016 py con2016_lightingtalk_php to python2016 py con2016_lightingtalk_php to python
2016 py con2016_lightingtalk_php to pythonJiho Lee
 
Django - The Web framework for perfectionists with deadlines
Django - The Web framework  for perfectionists with deadlinesDjango - The Web framework  for perfectionists with deadlines
Django - The Web framework for perfectionists with deadlinesMarkus Zapke-Gründemann
 
NoSql Day - Apertura
NoSql Day - AperturaNoSql Day - Apertura
NoSql Day - AperturaWEBdeBS
 
NoSql Day - Chiusura
NoSql Day - ChiusuraNoSql Day - Chiusura
NoSql Day - ChiusuraWEBdeBS
 
Authentication & Authorization in ASPdotNet MVC
Authentication & Authorization in ASPdotNet MVCAuthentication & Authorization in ASPdotNet MVC
Authentication & Authorization in ASPdotNet MVCMindfire Solutions
 
2007 - 应用系统脆弱性概论
2007 - 应用系统脆弱性概论 2007 - 应用系统脆弱性概论
2007 - 应用系统脆弱性概论 Na Lee
 

Viewers also liked (20)

Django - The Web framework for perfectionists with deadlines
Django - The Web framework for perfectionists with deadlinesDjango - The Web framework for perfectionists with deadlines
Django - The Web framework for perfectionists with deadlines
 
Website optimization
Website optimizationWebsite optimization
Website optimization
 
Html5 History-API
Html5 History-APIHtml5 History-API
Html5 History-API
 
2 × 3 = 6
2 × 3 = 62 × 3 = 6
2 × 3 = 6
 
Rabbitmq & Postgresql
Rabbitmq & PostgresqlRabbitmq & Postgresql
Rabbitmq & Postgresql
 
User-centered open source
User-centered open sourceUser-centered open source
User-centered open source
 
PythonBrasil[8] closing
PythonBrasil[8] closingPythonBrasil[8] closing
PythonBrasil[8] closing
 
Django e il Rap Elia Contini
Django e il Rap Elia ContiniDjango e il Rap Elia Contini
Django e il Rap Elia Contini
 
라이트닝 토크 2015 파이콘
라이트닝 토크 2015 파이콘라이트닝 토크 2015 파이콘
라이트닝 토크 2015 파이콘
 
2016 py con2016_lightingtalk_php to python
2016 py con2016_lightingtalk_php to python2016 py con2016_lightingtalk_php to python
2016 py con2016_lightingtalk_php to python
 
Django - The Web framework for perfectionists with deadlines
Django - The Web framework  for perfectionists with deadlinesDjango - The Web framework  for perfectionists with deadlines
Django - The Web framework for perfectionists with deadlines
 
NoSql Day - Apertura
NoSql Day - AperturaNoSql Day - Apertura
NoSql Day - Apertura
 
PyClab.__init__(self)
PyClab.__init__(self)PyClab.__init__(self)
PyClab.__init__(self)
 
NoSql Day - Chiusura
NoSql Day - ChiusuraNoSql Day - Chiusura
NoSql Day - Chiusura
 
Authentication & Authorization in ASPdotNet MVC
Authentication & Authorization in ASPdotNet MVCAuthentication & Authorization in ASPdotNet MVC
Authentication & Authorization in ASPdotNet MVC
 
EuroDjangoCon 2009 - Ein Rückblick
EuroDjangoCon 2009 - Ein RückblickEuroDjangoCon 2009 - Ein Rückblick
EuroDjangoCon 2009 - Ein Rückblick
 
Load testing
Load testingLoad testing
Load testing
 
2007 - 应用系统脆弱性概论
2007 - 应用系统脆弱性概论 2007 - 应用系统脆弱性概论
2007 - 应用系统脆弱性概论
 
Bottle - Python Web Microframework
Bottle - Python Web MicroframeworkBottle - Python Web Microframework
Bottle - Python Web Microframework
 
Django-Queryset
Django-QuerysetDjango-Queryset
Django-Queryset
 

Similar to The Django Book Chapter 9 - Django Workshop - Taipei.py

Refactor Dance - Puppet Labs 'Best Practices'
Refactor Dance - Puppet Labs 'Best Practices'Refactor Dance - Puppet Labs 'Best Practices'
Refactor Dance - Puppet Labs 'Best Practices'Gary Larizza
 
Doing the Refactor Dance - Making Your Puppet Modules More Modular - PuppetCo...
Doing the Refactor Dance - Making Your Puppet Modules More Modular - PuppetCo...Doing the Refactor Dance - Making Your Puppet Modules More Modular - PuppetCo...
Doing the Refactor Dance - Making Your Puppet Modules More Modular - PuppetCo...Puppet
 
The go-start webframework (GTUG Vienna 27.03.2012)
The go-start webframework (GTUG Vienna 27.03.2012)The go-start webframework (GTUG Vienna 27.03.2012)
The go-start webframework (GTUG Vienna 27.03.2012)ungerik
 
Beyond MVC: from Model to Domain
Beyond MVC: from Model to DomainBeyond MVC: from Model to Domain
Beyond MVC: from Model to DomainJeremy Cook
 
Python RESTful webservices with Python: Flask and Django solutions
Python RESTful webservices with Python: Flask and Django solutionsPython RESTful webservices with Python: Flask and Django solutions
Python RESTful webservices with Python: Flask and Django solutionsSolution4Future
 
Javascript MVC & Backbone Tips & Tricks
Javascript MVC & Backbone Tips & TricksJavascript MVC & Backbone Tips & Tricks
Javascript MVC & Backbone Tips & TricksHjörtur Hilmarsson
 
PyCon US 2012 - State of WSGI 2
PyCon US 2012 - State of WSGI 2PyCon US 2012 - State of WSGI 2
PyCon US 2012 - State of WSGI 2Graham Dumpleton
 
Django class based views
Django class based viewsDjango class based views
Django class based viewsjustinvoss
 
From polling to real time: Scala, Akka, and Websockets from scratch
From polling to real time: Scala, Akka, and Websockets from scratchFrom polling to real time: Scala, Akka, and Websockets from scratch
From polling to real time: Scala, Akka, and Websockets from scratchSergi González Pérez
 
High Performance web apps in Om, React and ClojureScript
High Performance web apps in Om, React and ClojureScriptHigh Performance web apps in Om, React and ClojureScript
High Performance web apps in Om, React and ClojureScriptLeonardo Borges
 
N Things You Don't Want to Repeat in React Native
N Things You Don't Want to Repeat in React NativeN Things You Don't Want to Repeat in React Native
N Things You Don't Want to Repeat in React NativeAnton Kulyk
 
Flask intro - ROSEdu web workshops
Flask intro - ROSEdu web workshopsFlask intro - ROSEdu web workshops
Flask intro - ROSEdu web workshopsAlex Eftimie
 
XebiConFr 15 - Brace yourselves Angular 2 is coming
XebiConFr 15 - Brace yourselves Angular 2 is comingXebiConFr 15 - Brace yourselves Angular 2 is coming
XebiConFr 15 - Brace yourselves Angular 2 is comingPublicis Sapient Engineering
 
Interoperable Component Patterns
Interoperable Component PatternsInteroperable Component Patterns
Interoperable Component PatternsMatthew Beale
 

Similar to The Django Book Chapter 9 - Django Workshop - Taipei.py (20)

ParisJS #10 : RequireJS
ParisJS #10 : RequireJSParisJS #10 : RequireJS
ParisJS #10 : RequireJS
 
Refactor Dance - Puppet Labs 'Best Practices'
Refactor Dance - Puppet Labs 'Best Practices'Refactor Dance - Puppet Labs 'Best Practices'
Refactor Dance - Puppet Labs 'Best Practices'
 
Doing the Refactor Dance - Making Your Puppet Modules More Modular - PuppetCo...
Doing the Refactor Dance - Making Your Puppet Modules More Modular - PuppetCo...Doing the Refactor Dance - Making Your Puppet Modules More Modular - PuppetCo...
Doing the Refactor Dance - Making Your Puppet Modules More Modular - PuppetCo...
 
Iktomi lightning talk
Iktomi lightning talkIktomi lightning talk
Iktomi lightning talk
 
The go-start webframework (GTUG Vienna 27.03.2012)
The go-start webframework (GTUG Vienna 27.03.2012)The go-start webframework (GTUG Vienna 27.03.2012)
The go-start webframework (GTUG Vienna 27.03.2012)
 
Beyond MVC: from Model to Domain
Beyond MVC: from Model to DomainBeyond MVC: from Model to Domain
Beyond MVC: from Model to Domain
 
Broadleaf Presents Thymeleaf
Broadleaf Presents ThymeleafBroadleaf Presents Thymeleaf
Broadleaf Presents Thymeleaf
 
Refactoring
RefactoringRefactoring
Refactoring
 
Python RESTful webservices with Python: Flask and Django solutions
Python RESTful webservices with Python: Flask and Django solutionsPython RESTful webservices with Python: Flask and Django solutions
Python RESTful webservices with Python: Flask and Django solutions
 
Javascript MVC & Backbone Tips & Tricks
Javascript MVC & Backbone Tips & TricksJavascript MVC & Backbone Tips & Tricks
Javascript MVC & Backbone Tips & Tricks
 
PyCon US 2012 - State of WSGI 2
PyCon US 2012 - State of WSGI 2PyCon US 2012 - State of WSGI 2
PyCon US 2012 - State of WSGI 2
 
Django class based views
Django class based viewsDjango class based views
Django class based views
 
From polling to real time: Scala, Akka, and Websockets from scratch
From polling to real time: Scala, Akka, and Websockets from scratchFrom polling to real time: Scala, Akka, and Websockets from scratch
From polling to real time: Scala, Akka, and Websockets from scratch
 
High Performance web apps in Om, React and ClojureScript
High Performance web apps in Om, React and ClojureScriptHigh Performance web apps in Om, React and ClojureScript
High Performance web apps in Om, React and ClojureScript
 
Django - sql alchemy - jquery
Django - sql alchemy - jqueryDjango - sql alchemy - jquery
Django - sql alchemy - jquery
 
N Things You Don't Want to Repeat in React Native
N Things You Don't Want to Repeat in React NativeN Things You Don't Want to Repeat in React Native
N Things You Don't Want to Repeat in React Native
 
Try AngularJS
Try AngularJSTry AngularJS
Try AngularJS
 
Flask intro - ROSEdu web workshops
Flask intro - ROSEdu web workshopsFlask intro - ROSEdu web workshops
Flask intro - ROSEdu web workshops
 
XebiConFr 15 - Brace yourselves Angular 2 is coming
XebiConFr 15 - Brace yourselves Angular 2 is comingXebiConFr 15 - Brace yourselves Angular 2 is coming
XebiConFr 15 - Brace yourselves Angular 2 is coming
 
Interoperable Component Patterns
Interoperable Component PatternsInteroperable Component Patterns
Interoperable Component Patterns
 

The Django Book Chapter 9 - Django Workshop - Taipei.py

  • 2. About me • TP (@uranusjr) • RTFD • Find me anywhere
  • 3. Language Review {% if is_logged_in %}! Thanks for logging in!! {% else %}! Welcome to {{ website_name }}.! {% endif %}
  • 4. Template tags {% if is_logged_in %}! Thanks for logging in!! {% else %}! Welcome to {{ website_name }}.! {% endif %}
  • 5. Variables {% if is_logged_in %}! Thanks for logging in!! {% else %}! Welcome to {{ website_name }}.! {% endif %}
  • 6. Language Review • Variables come from a context • Dictionary-like name-value mapping • A context is rendered by the template • Variable replacement • Template tag execution
  • 8. 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)
  • 9. 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)
  • 10. 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)
  • 11. 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)
  • 12. 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)
  • 13. 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)
  • 14. 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)
  • 15. 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)
  • 16. 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)
  • 17. 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! )
  • 18. 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! )
  • 21. 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! )
  • 22. 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! )
  • 23. 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.'}! )
  • 24. Custom Processors • Do one thing and do it well • Pick your variable names •Context keys are global • General import conventions apply
  • 25. • Breaking down render_to_response • RequestContext and context processors • TEMPLATE_CONTEXT_PROCESSORS • django.shortcuts.render Summary
  • 26. 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 &lt; 2' }}
  • 27. Template Loading • django.template.loader • get_template(template_name) • select_template(template_names)! • TemplateDoesNotExist! • TEMPLATE_DIRS! • TEMPLATE_LOADERS
  • 28. 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',! )
  • 29. 1 2 3 • Loader order • Path order • Third-party apps will be searched • App directory search order is undefined
  • 31.
  • 33. 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
  • 39. Template Filter • Takes one or zero arguments • Always returns something • Fails silently
  • 40. {{ value|upper }}! ! {{ value|add:arg }}
  • 41. 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)
  • 42. 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)
  • 43. 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)
  • 44. 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)!
  • 45. 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)!
  • 46. @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
  • 47. @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 ''
  • 48. {{ value|upper }}! ! {{ value|add:arg }}
  • 49. {% load myapp_tags %}! ! {{ value|upper }}! ! {{ value|add:arg }}
  • 51. 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 ''
  • 52. 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 ''
  • 53. 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 ''
  • 54. 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 ''
  • 55. Return Something •Not necessarily a string •Only need to be convertible to a string • None is converted to 'None', not '' •I always return strings
  • 56. Template Tags Compilation function Template tag node __init__(self, string) render(self, context) template context rendered value
  • 58. 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)!
  • 59. 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)!
  • 60. 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)!
  • 61. 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)!
  • 62. 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)!
  • 63. 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)!
  • 64. 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)
  • 65. 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)
  • 66. 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! )
  • 67. 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! )
  • 68. {% inject_now format_string %}! ! <p>The time is {{ now }}</p>
  • 69. 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)!
  • 70. 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 ''
  • 71. 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 ''
  • 72. {% inject_now format_string as var_name %}! ! <p>The time now is {{ var_name }}</p>
  • 73. @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)
  • 74. @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)
  • 75. 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 ''
  • 76. 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)
  • 77. def do_comment(parser, token):! nodelist = parser.parse(('endcomment',))! parser.delete_first_token()! return CommentNode()! ! ! class CommentNode(template.Node):! def render(self, context):! return ''
  • 78. def do_comment(parser, token):! nodelist = parser.parse(('endcomment',))! parser.delete_first_token()! return CommentNode()! ! ! class CommentNode(template.Node):! def render(self, context):! return ''
  • 79. def do_comment(parser, token):! nodelist = parser.parse(('endcomment',))! parser.delete_first_token()! return CommentNode()! ! ! class CommentNode(template.Node):! def render(self, context):! return ''
  • 81. Custom Loaders • implement load_template_source • Raise TemplateDoesNotExist when appropriate • Add it to TEMPLATE_LOADERS in settings
  • 82. 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
  • 83. 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