Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des...

51
Django Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016

Transcript of Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des...

Page 1: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Django đź’–

Jinja2 Aymeric Augustin DjangoCong 2016

Jardin des Plantes, Avranches, 9 avril 2016

Page 2: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

👋 I’m Aymeric

2

Core Developersince 2011

Chief Technical Officersince 2015

• Time zones• Python 3• Transactions• App loading• Jinja2

First peer-to-peerinsurance broker

in France

We’re hiring!

Amalfi

Page 3: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Why Jinja2?

3

Aren’t Django templates just fine?

Page 4: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

4

Jinja2 is fast.10-20x faster than Django templates.

Image from http://www.gentside.com/dragster/wallpaper

Page 5: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

5

Django templates are slow.10-20x slower than Jinja2.

Image from https://commons.wikimedia.org/wiki/File:Grand_bi_sur_la_terrasse_Dufferin_vers_1900.jpg

Page 6: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Let’s try it on the fractalideas.com home page!

6

>>> import statistics, timeit

>>> setup = \

... "from django.template.loader import render_to_string"

>>> round(statistics.median(timeit.repeat(

... "render_to_string('home.html', using='django')",

... setup, repeat=10, number=1000)), 2)

1.18 # ms

>>> round(statistics.median(timeit.repeat(

... "render_to_string('home.html', using='jinja2')",

... setup, repeat=10, number=1000)), 2)

0.35 # ms

Page 7: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Let’s try it on the fractalideas.com home page!

• More than three times faster!

• Not even a millisecond faster.

• Page loads 0.004% faster with a cold cache.

7

Page 8: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Let’s try it on the fractalideas.com home page!

• More than three times faster!

• Not even a millisecond faster.

• Page loads 0.02% faster with a warm cache.

8

Page 9: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Template rendering speed (often) doesn’t matter

• Light content? Anything is fast enough

• Heavy static content? Pre-render & cache it

• Heavy dynamic content? Render in the browser

• If you’re thinking about switching template languages, consider both server-side and client-side options (React)

• Until you’ve optimized everything else, templates are unlikely to be a bottleneck

9

Page 10: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Why Jinja2?

10

Aren’t Django templates just fine?

Page 11: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

– http://jinja.pocoo.org/docs/

“Jinja2 is a modern and designer-friendly templating language for Python,

modelled after Django’s templates.”

11

Page 12: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Advantages of Jinja2

• More comfortable for developers

• Django expects templates to be written by “designers”

• Easier to customize

• Try writing a Django template tag without the docs

• Meaningfully faster in some use cases

• Template-based widget rendering (#15667)

12

Page 13: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Drawbacks of Jinja2

• Less common than Django templates

• Many authors of pluggable apps don’t know where to start (yet)

• Targets experienced developers

• Docs suggest handling escaping manually for performance

• Scoping behavior for included blocks is declarative

• etc.

13

Page 14: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Features of Jinja2 — from its docs’ homepage

• sandboxed execution

• powerful automatic HTML escaping system for XSS prevention

• template inheritance

• compiles down to the optimal python code just in time

• optional ahead-of-time template compilation

• easy to debug (…)

• configurable syntax14

sounds important (to me)

Page 15: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

– paraphrased from http://jinja.pocoo.org/docs/

“Jinja2 is a templating language for Python designed for experienced developers, modelled after Django’s templates.”

15

Page 16: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Third-party solutions

• Coffin (2008 - reborn in 2015)

• Alternative APIs such as render()

• Jingo (2010)

• Template loader (class-based API since Django 1.2)

• Django-Jinja (2012)

• More opinionated and full-featured template loader

16

Page 17: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Templates in Django < 1.8

17

Discussed for historical purposes only — these versions are unsupported!

Page 18: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Django < 1.8 - Rendering a template

18

# django/template/loaders.py (simplified)

def render_to_string(template_name, dictionary):

template = get_template(template_name, dirs)

context = Context(dictionary)

return template.render(context)

Page 19: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Django < 1.8 - Rendering a template

19

# django/template/loaders.py (simplified)

def get_template(template_name):

template, origin = find_template(template_name)

return Template(template, origin, template_name)

Page 20: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Django < 1.8 - Rendering a template

20

# django/template/loaders.py (simplified)

def find_template(name, dirs=None):

global template_source_loaders

if template_source_loaders is None:

... # init from settings.TEMPLATE_LOADERS

for loader in template_source_loaders:

... # try loading with loader(name)

raise TemplateDoesNotExist(name)

global configuration

Page 21: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Django < 1.8 - Actually rendering a template

21

# django/shortcuts.py (drastically simplified)

def render(request, template_name, dictionary):

template = get_template(template_name, dirs)

context = RequestContext(request, dictionary)

return HttpResponse(template.render(context))

strong coupling

Page 22: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Context processors are a poor API

• How many context processors depend on request?

• Aside from the request context processor, that is.

• How hard is it to make a context processor lazy?

• In order to avoid overhead in templates that don’t need it.

• Just an API for {{ global_func(request) }}?

• Django templates cannot call functions with arguments.

22

Page 23: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Multiple templates engines in Django ≥ 1.8

23

If templates weren’t boring, they’d have been fixed earlier.

Page 24: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

24

Page 25: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

25

Page 26: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

26

Page 27: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Plan

1. Write a Django Enhancement Proposal (DEP)

• Lots of reading; some writing

2. Refactor Django templates as a library (optional)

• Actually not optional

3. Implement the DEP

• Sounds easy

27

Page 28: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Concept

28

django.template.*

django.template.loader

d.t.backends.django d.t.backends.jinja2

django.template.* jinja2

Page 29: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

– DEP 182

“this project avoids encoding the legacy of the Django template language in APIs”

29

Page 30: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Jinja2 for Djangonauts

30

Wake up — this is the “just do this” section.

Page 31: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Install Jinja2

31

$ pip install jinja2

Page 32: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Define a Jinja2 environment

32

# project/jinja2.py

from jinja2 import Environment

def environment(**options):

options.setdefault('extensions', []).extend([...])

env = Environment(**options)

env.filters.update({ ... })

env.globals.update({ ... })

env.tests.update({ ... })

return env

Page 33: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Install translations (optional)

33

# project/jinja2.py

from django.utils import translation

def environment(**options):

# ...

env.install_gettext_translations(translation,

newstyle=True)

# ...

return env

Page 34: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Point the TEMPLATES setting to the environment

34

TEMPLATES = [{

'BACKEND': 'django.template.backends'

'.jinja2.Jinja2',

'DIRS': [os.path.join(BASE_DIR, 'templates')],

'OPTIONS': {

'environment': 'project.jinja2.environment',

},

}, {

'BACKEND': 'django.template.backends'

'.django.DjangoTemplates',

'APP_DIRS': True,

}]

Page 35: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Globals

35

# project/jinja2.py

from django.core.urlresolvers import reverse

env.globals.update({

'url': reverse,

})

# templates/header.html

{{ url('account_login') }}

Page 36: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Filters

36

# project/jinja2.py

from urllib.parse import quote

env.filters.update({

'urlquote': quote,

})

# templates/header.html

?next={{ request.get_full_path() | urlquote }}

Page 37: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Tests

37

# project/jinja2.py

from django.utils.timezone import is_aware

env.tests.update({

'aware': is_aware,

})

# templates/datetime.html

{% if at is aware %}{{ at.tzname() }}{% endif %}

Page 38: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Extensions

38

# project/jinja2.py

options.setdefault('extensions', []).extend([

'jinja2.ext.i18n',

])

# or, alternatively:

env.add_extension('jinja2.ext.i18n')

# templates/header.html

{{ gettext("Log in") }}

Page 39: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Jinja2 tips

39

You can go back to sleep — the slides will be online.

Page 40: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Calling methods

40

# Django templates

{{ qotd }}

# Jinja2

{{ qotd() }}

Page 41: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Inserting the CSRF token in a form

41

# Django templates

{% csrf_token %}

# Jinja2

{{ csrf_input }}

# Both

<input type="hidden"

name="csrfmiddlewaretoken"

value="{{ csrf_token }}" />

Page 42: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Replacing a context processor

42

# project/jinja2.py

from django.contrib.messages import get_messages

env.globals.update({

'get_messages': get_messages,

})

Page 43: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Replacing a context processor

43

# templates/header.html

{% for message in get_messages(request) %}

<div>{{ message }}</div>

{% endfor %}

Page 44: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Replacing a context processor - alternative

44

# project/jinja2.py

from django.contrib.messages import get_messages

@jinja2.contextfunction

def _get_messages(context):

return get_messages(context['request'])

env.globals.update({

'get_messages': _get_messages,

})

Page 45: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Replacing a context processor - alternative

45

# templates/header.html

{% for message in get_messages() %}

<div>{{ message }}</div>

{% endfor %}

Page 46: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Replacing a template tag

46

# spam/templatetags.py

from django import template

register = template.Library()

@register.simple_tag(takes_context=True)

def eat_spam(context, arg1, arg2):

# ...

return html

Page 47: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Replacing a template tag

47

# templates/spam.html

{% load spam %}

{% eat_spam arg1 arg2 %}

Page 48: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Replacing a template tag

48

# spam/jinja2.py

@jinja2.contextfunction

def eat_spam(context, arg1, arg2):

# ...

return html

# and register it in env.globals as shown above.

Page 49: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Replacing a template tag

49

# templates/spam.html

{{ eat_spam(arg1, arg2) }}

Page 50: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Most extensions only add globals

50

# eat/jinja2.py

class EatExtension(Extension):

def __init__(self, env):

env.globals['eat'] = {'spam': eat_spam}

# templates/eat.html

{{ eat.spam() }}

Page 51: Django - Amazon S3 · PDF fileDjango ! Jinja2 Aymeric Augustin DjangoCong 2016 Jardin des Plantes, Avranches, 9 avril 2016 "

Questions?

Thank you!

Estuaire de la SĂ©e, Avranches, 9 avril 2016