Django Templating: More Than Just Blocks by Christine Cheung¶
Presenter: Christine Cheung (http://www.xtine.net/) (@plaidxtine)
PyCon 2012 presentation page: https://us.pycon.org/2012/schedule/presentation/80/
Slides: http://speakerdeck.com/u/xtine/p/django-templating-more-than-just-blocks
Video: http://pyvideo.org/video/697/django-templating-more-than-just-blocks
Outline
- Intro to Templating
- Effective Use of Built-In Tags
- Extending Templates
- Template Loading
- What’s New
Intro to Templating¶
Django Templating 101¶
- This is the End User Experience
- Balance between power and ease
- Design
Helpful tools¶
- Django template theme for your IDE
- syntax highlighting, autocompletion
- django-debug-toolbar
- template paths, session variables
- Print out tag/filter reference guide
Folder and file structure¶
- Keep templates in one place
Effective Use of Built-In Tags¶
The Basics¶
Start with base.html
<!doctype html> <head> <title>{% block title %}demo{% endblock title %}</title> </head> <body> {% block content %}{% endblock content %} </body> </html>
Then have pages inherit from it
{% extends "base.html" %} {% block title %}the foo page{% endblock title %} {% block content %} <div id="foo"> this is a bar. </div> {% endblock content %}
Common blocks¶
I use these in practically every project:
- title
- meta_tags, robots,
- extra_head (CSS, etc.),
- content
- extra_js (so that JavaScript can go at bottom of page which improves page-load time)
Block practices¶
- End your block structures
- {% block title %}foo{% endblock title %}
- instead of {%block title %}foo{% endblock %}
- Can’t repeat blocks
- however: context processor, include, custom template tag
- Don’t “over block”
Including templates¶
- {% include "snippet.html" %}
- great for repeating template segments
- try not to include in an include – gets confusing
Variables¶
- Tend to be objects passed from a view
- Modify objects with filters
- {{ variable | lower }}
- Loop through etc. using tags
- {% if variable %}foo{% else %}bar{% endif %}
- {% for entry in blog_entries %}<h2>{{ entry.title }}</h2><p>{{ entry.body }}</p>{% endfor %}
- You can also create your own filters and tags (see Django docs on custom template tags and filters)
- Modify objects with filters
Security¶
By default, Django’s security is rather solid on the template side of things...
- but if you use safe or {% autoescape %}
- * make sure you sanitize the data! *
URLs¶
Name {% url %} tags as much as possible
- define URL patterns in
urls.py
- url(r'^foo/$', foo, name="foo"),
- <a href="{% url "foo" %}">foo</a>
{{ STATIC_URL }}css/style.css
- Not /static/css/style.css
Forms¶
For heavy form action, take a look at:
- django-floppyforms (HTML 5)
- django-crispy-forms (used to be django-uni-form)
{% include form.html %}
- as_ul (docs) makes more sense than as_p or as_table
More than one way¶
There are multiple ways to accomplish the same task.
No ultimately right or wrong way
- use what suits you or your team
An example
The long way:
{% if foo.bar %}
{{ foo.bar }}
{% else %}
{{ foo.baz }}
{% endif %}
or the shorter way:
{% firstof foo.bar foo.baz %}
Extending templates¶
Custom Tags and Filters¶
demo/ models.py templatetags/ __init__.py demo_utils.py view.py
Given that we have template tags in demo/templatetags/demo_utils.py
{% load demo_utils %}
Making a Custom Filter¶
from django import template
register = template.Library()
@register.filter(name='remove')
def cut(value, argument):
# remove passed arguments from value
return value.replace(argument, '')
{{ foo|remove:'bar' }}
@register.filter
def lower(value):
# lowercased value with no passed arguments
return value.lower()
{{ foo|lower }}
Making a Custom Tag¶
Tags are a bit more complex
- two steps: compiling and rendering
Decide its purpose
- but start simple
better to have many tags that do many things rather than one tag that does many things
A Simple Example¶
<p>
It is now
{% current_time "%Y-%m-%d %I:%M %p" %}
</p>
Simple Tag¶
from django import template
register = template.Library()
@register.simple_tag
def current_time(format_string):
try:
return datetime.datetime.now().strftime(str(format_string))
except UnicodeEncodeError:
return 'oh noes current time borked'
Nodes and Stuff¶
import datetime
from django import template
register = template.Library()
@register.tag(name="current_time")
def do_current_time(parser, token):
try:
tag_name, format_string = token.split_contents()
except ValueError:
msg = '%r tag requires a single argument' % token.split_contents()[0]
raise template.TemplateSyntaxError(msg)
class CurrentTimeNode(template.Node):
def __init__(self, format_string):
self.format_string = str(format_string)
def render(self, context):
now = datetime.datetime.now()
return now.strftime(self.format_string)
Easier Template Tag Creation¶
- makes it simple to define syntax for a tag
- class-based template tags
- extensible argument parse for less boilerplate
DO NOT!!!¶
Do not write a template tag that runs logic or at worst, even run Python from a custom tag
- it defeats purpose of a templating language
- dangerous
- difficult to support
Loading templates¶
Template loading logic¶
Use cases
TEMPLATE_LOADERS setting (Django docs)
from django.conf import settings
from django.template import TemplateDoesNotExist
def load_template_source(template_name, template_dirs=None):
for filepath in get_template_sources(template_name, template_dirs):
try:
# load in some templates yo
except IOError:
pass
raise TemplateDoesNotExist(template_name)
Replacing the templating engine¶
You can replace the built in templating engine
But why?
- More familiar with another templating language
- Performance boost
- Different logic control and handling
but you risk “frankensteining” your project.
Jinja2 and Django and You¶
Pros
- functions callable from templates - don’t have to write tags and filters
- loop controls - more powerful flow control
- multiple filter arguments
- slight performance increase
Cons
- more dependencies and overhead
- extra time spent on development and support
- risk putting too much logic in templates
- minimal speed increase
Speeding Up Templates¶
Cache template loader
- compiles template files
- from Adrian Holovaty (one of the creators of Django)
- flattens template files
but also remember other bottlenecks...
New in Django 1.4¶
Custom project and app templates
- startapp/startproject – template (docs on Django 1.4 custom project and app templates)
- combine with your favorite boilerplate
Else if
- {% elif %} (docs on minor features)
Questions¶
Questions????