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

Post on 18-Dec-2014

316 views 3 download

Tags:

description

Reviewing Chapter 9: Advanced Templates in The Django Book.

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

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?