Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

671 views

Published on

Reviewing Chapter 9: Advanced Templates in The Django Book.

  • Be the first to comment

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

  1. 1. Advanced Templates 2013-07-23. Django Workshop.
  2. 2. About me • TP (@uranusjr) • RTFD • Find me anywhere
  3. 3. Language Review {% if is_logged_in %}! Thanks for logging in!! {% else %}! Welcome to {{ website_name }}.! {% endif %}
  4. 4. Template tags {% if is_logged_in %}! Thanks for logging in!! {% else %}! Welcome to {{ website_name }}.! {% endif %}
  5. 5. Variables {% if is_logged_in %}! Thanks for logging in!! {% else %}! Welcome to {{ website_name }}.! {% endif %}
  6. 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
  7. 7. Template __init__(self, string) render(self, context) format string context rendered value text file dict object
  8. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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! )
  19. 19. 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
  20. 20. 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
  21. 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. 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. 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. 24. Custom Processors • Do one thing and do it well • Pick your variable names •Context keys are global • General import conventions apply
  25. 25. • Breaking down render_to_response • RequestContext and context processors • TEMPLATE_CONTEXT_PROCESSORS • django.shortcuts.render Summary
  26. 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 < 2' }}
  27. 27. Template Loading • django.template.loader • get_template(template_name) • select_template(template_names)! • TemplateDoesNotExist! • TEMPLATE_DIRS! • TEMPLATE_LOADERS
  28. 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. 29. 1 2 3 • Loader order • Path order • Third-party apps will be searched • App directory search order is undefined
  30. 30. Questions?
  31. 31. Template Tags
  32. 32. 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
  33. 33. Template Library
  34. 34. Template Library
  35. 35. Template Library Name matters
  36. 36. Template Library Don’t forget this
  37. 37. Template Library Naming is important
  38. 38. Template Filter • Takes one or zero arguments • Always returns something • Fails silently
  39. 39. {{ value|upper }}! ! {{ value|add:arg }}
  40. 40. 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)
  41. 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. 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)! ! ! register.filter('upper', upper)! register.filter('add', add)
  43. 43. 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)!
  44. 44. 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)!
  45. 45. @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
  46. 46. @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 ''
  47. 47. {{ value|upper }}! ! {{ value|add:arg }}
  48. 48. {% load myapp_tags %}! ! {{ value|upper }}! ! {{ value|add:arg }}
  49. 49. {{ value|date:arg }}
  50. 50. 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 ''
  51. 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. 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. 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. 54. Return Something •Not necessarily a string •Only need to be convertible to a string • None is converted to 'None', not '' •I always return strings
  55. 55. Template Tags Compilation function Template tag node __init__(self, string) render(self, context) template context rendered value
  56. 56. {% now format_string %}
  57. 57. 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)!
  58. 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. 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. 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. 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. 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. 63. 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)
  64. 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. 65. 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! )
  66. 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. 67. {% inject_now format_string %}! ! <p>The time is {{ now }}</p>
  68. 68. 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)!
  69. 69. 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 ''
  70. 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. 71. {% inject_now format_string as var_name %}! ! <p>The time now is {{ var_name }}</p>
  72. 72. @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)
  73. 73. @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)
  74. 74. 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 ''
  75. 75. 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)
  76. 76. def do_comment(parser, token):! nodelist = parser.parse(('endcomment',))! parser.delete_first_token()! return CommentNode()! ! ! class CommentNode(template.Node):! def render(self, context):! return ''
  77. 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. 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. 79. Shortcuts • register.simple_tag • register.inclusion_tag(template)! • takes_context=True! • stringfilter
  80. 80. Custom Loaders • implement load_template_source • Raise TemplateDoesNotExist when appropriate • Add it to TEMPLATE_LOADERS in settings
  81. 81. 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
  82. 82. 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
  83. 83. Questions?

×