Two Scoops of Django (1.6)

Templates and Tags

Date: 2014.12.16
Speaker: Apua
Last Update: 2014.12.16

Concept

  • Explicit is better than Implicit
  • Flat is better than Nested
  • Minimalist Approach
  • Limit Processing

Templates

Settings

TEMPLATE_DIRS = (os.path.join(BASE_DIR, "template"),)
INSTALLED_APPS = ('myproject.polls', 'myproject.music')

TEMPLATE_LOADERS = (
    ('django.template.loaders.cached.Loader', (
        'django.template.loaders.filesystem.Loader',
        'django.template.loaders.app_directories.Loader',
    )),
)

Basic

{% extend "base.html" %}

{% block body %}
{{ block.super }}
{{ form_subtitle }}
<form method="post">
    {% include form_template %}
</form>
<a href="{% url 'home' %}">Home</a>
{% endblock %}

{# Those are `context` objects #}

Basic Control Flow

{% if display_inbox %}
    {% with emails=user.emails.all %}
    <p>You have {{ emails|lenght }} email(s)</p>
    {% for email in emails %}
        <p>{{ email.body }}</p>
    {% endfor %}
{% endif %}

Filters and Tags

{# A filter `lower` #}
{% "ABC"|lower %}

{# A tag `autoescape` #}
{% autoescape %}
{{ obj }}
{% endautoescape %}

Notices

  • Use named Context objects.
  • Don't bother (merged) HTML pretty.
  • Recommend 2-tier or 3-tier architecture.
  • Avoid unnecessary aggregation and
    condition filtering in Templates.
  • Use CSS for styling.

Customize Tags and Filters

Location

myapp/
|-- __init__.py
|-- admin.py
|-- models.py
|-- templatetags
|   `-- myapp_tags.py
|-- tests.py
`-- views.py

Naming Tag Libraries

Recommand:
 

Warning:

    Don't name template tag libraries with the       same name as app .

    Don't rely on IDE to determine the name.

<app_name>_tags.py

Load

{% extends "base.html" %}

{% load myapp_tags %}

Customize Filters

# myapp/templatetags/myapp_tags.py
from django import template
register = template.Library()

@register.filter
def myfilter(val, arg): # limit one or two arguments
    pass
    return "altered_string"
{% extends "base.html" %}
{% load myapp_tags %}

{% val|myfilter:arg %}

Customize Tags

# myapp/templatetags/myapp_tags.py
from django import template
register = template.Library()

@register.tag
def mytag(parser, token, *args, **kwargs):
    pass
    return template.Node()
{% extends "base.html" %}
{% load myapp_tags %}

{% mytag arg0 arg1 kwarg0="val0"|lower kwarg1=var %}
This part would be token.
{% endmytag %}

When to Write
Filters and Tags

Filters:
    Modifying the presentation of data.
    It can be readily reused in REST APIs
    and other output formats

Tags:

    Only responsible for rendering of HTML.

Bottleneck

Correct complex implied queries
in templates.

{% comment %}
`user_list` generated via
User.object.all() or User.selet_related("flavors")
{% endcomment %}
{% for user in user_list %}
   {{ user.name }} <br />
   {{ user.flavor.title }} <br />
   {{ user.flavor.scoops_remaining }} <br />
{% endfor %}

Avoid hidden CPU load and
REST API calls in templates.

Consider caching the loaded templates if custom tags load lots of templates.

Ends and Odds

Don't replace Django template engine.

See chapter 16: Tradeoffs of Replacing Core Components

Debugging complex templates.

# setttings/local.py
TEMPLATE_STRING_IF_INVALID = "INVALID EXPRESSION: %s"

Serve error pages
entirely self-contained static.

Filter are easy to test.
Tags are harder to debug.

Liberal use of log statements and tests are helpful.

See chapter 20: Testing Stinks and Is a Waste of Money!

Filters can be reusable.

# from django.template.defaultfilters import (slugify,
#                                             remove_tags)
from django.utils.text import slugify
from django.utils.html.remove_tags

Tags make code reuse harder.
But don't create anti-pattern for DRY.

# Anti-pattern
from django import template
template.add_to_builtins("myapp.templatetags.myapp_tags")