Moving Targets
@brandon_rhodes
Django Weekend 2014
Cardiff, Wales
Wolf
Coyote
Fox
The biggest rivalries
will be between the most
similar technologies
Corollary:
The biggest arguments
will tend to be over the
smallest differences

A moving target

Fixes problems.
Adds missing features.
Invalidates my complaints.

Django

Has done a great job
of being a moving target
“There are better URL mappers!”
“There are better template systems!”
“There are better ORMs!”
1.2
Multiple databases; usernames with '@'
1.4
Bulk insert; pre-fetching; WSGI;
fixed double import; timezones
1.5
Caching related models; custom User
1.6
No SELECT on Model.save()

Three Features

TemplateResponse

1.3

request → data → document

request → data → document
HTTP   list, dict, …   HTML
request → data → document
Let us consider these
two transforms separately

request → data

http://example.com/news/recent/
{'today': '2014 February 8',
 'top_stories': [
  ['World', [
   {'headline': 'Django Weekend Starting',
    'blurb': 'World leaders are happy that…',
    'byline': 'Cardiff, Wales'},
   {'headline': 'Olympics Celebrates Opening',
    'blurb': 'With the lighting of the…',
    'byline': 'Sochi, Russia'},
  
 ]}

request → data

data → document

{'today': '2014 February 8',
 'top_stories': [['World', []], ]}
World news on 2014 February 8

Django Weekend Starting───────────────────┐
  Cardiff, Wales  World leaders are happy
  that students, programmers, and other  
└──────────────────────────────────────────┘
Olympics Celebrates Opening───────────────┐
  Sochi, Russia  With the lighting of the
  Olympic torch, the opening ceremony has…│
└──────────────────────────────────────────┘
{'today': '2014 February 8',
 'top_stories': [['World', []], ]}
<h2>World news on 2014 February 8</h2>
<article>
<h3>Django Weekend Starting</h3>
<span class="byline">Cardiff, Wales</span>
<p>World leaders are happy
   that students, programmers, and other</p>
</article>
<article>
<h3>Olympics Celebrates Opening</h3>

data → document

{'today': '2014 February 8',
 'top_stories': [['World', []], ]}
{% for topic, stories in top_stories %}
<h2>{{ topic }} news on {{ today }}</h2>
{% for story in stories %}
<article>
<h3>{{ story.headline }}</h3>
<span class="byline">{{ story.byline }}</span>
<p>{{ story.blurb }}</p>
</article>
{% endfor %}
{% endfor %}
Old state of the art
in Django applications:
integration test
run
view
+
render
template
=
output
text
def test_recent_news_page(self):
    url = '/news/recent/'
    html = self.client.get(url).content
    self.assertIn(u'<h2>World ', html)
self.assertIn(u'<h2>World ', html)
Compound
Fragile
Late

request → (data) → document

Another approach

request → data   Programmer
data → document   Web designer
request → data   Python test
data → document   Browser test
This became possible when Django
added TemplateResponse in 1.3
A TemplateResponse remembers
both the template and the context
def recent_news_page(request):
    context = {}
    context['today'] = 
    context['top_stories'] = 
    return TemplateResponse(
        request, 'recent_news.html', context)
def test_person_page(self):
    url = '/person/brandon'
    c = self.client.get(url).context
    title, stories = c['top_stories'][0]
    self.assertEqual(title, u'World')
So your view data becomes
a first-class result available
during debugging and tests
This lets you write
tests on day one once you
specify how the context looks
The context structure becomes
an explicit contract between your
code and your template

View tests

db1 → context1
db2 → context2
db3 → context3

Design tests

contexta → pagea
contextb → pageb
Also, a TemplateResponse can
be examined and modified by —
Decorators
Middleware

Overly simple example

Eliminating a common source
of duplicated code
def front_page(request):
    
    context['user'] = request.user
    

def recent_news_page(request):
    
    context['user'] = request.user
    

def settings_page(request):
    
    context['user'] = request.user
    
# Simple middleware example, for views
# returning TemplateResponses

class DesignMiddleware(object):

    def process_template_response(
          request, response):

        context = response.context_data
        context['user'] = request.user

alternative

Your view:
request{…}
Template context processors:
request{…}
request{…}
request{…}
Dictionaries are merged
then given to the template
request
Your view
TemplateResponse
request → Template response middleware
request → Template response middleware
request → Template response middleware

TemplateResponse

1.3

Persistent Connections

1.6

By default, Django opens a
new connection to your database
for every view that it renders!
       connect() 
                  Please authenticate
Identity, secret 
                  Success
1.         Query 
                  Data
         close() 

       connect() 
                  Please authenticate
Identity, secret 
                  Success
2.         Query 
                  Data
         close() 

settings.py

CONN_MAX_AGE = 600   # ten minutes
       connect() 
                  Please authenticate
Identity, secret 
                  Success
1.         Query 
                  Data

2.         Query 
                  Data

3.         Query 
                  Data

                 

Persistent Connections

1.6

Transaction Management

1.6

Transactions are funny things

# Once upon a time

db> SELECT COUNT(*) FROM fast_changing_table;
1035 rows

# Then I waited several seconds

db> SELECT COUNT(*) FROM fast_changing_table;
1035 rows

# Then I waited several minutes

db> SELECT COUNT(*) FROM fast_changing_table;
1035 rows
I disconnected
and reconnected
db> SELECT COUNT(*) FROM fast_changing_table;
1102 rows

# Great! The table is updating now.

db> SELECT COUNT(*) FROM fast_changing_table;
1102 rows

# Hmm. But now it has stopped again

db> SELECT COUNT(*) FROM fast_changing_table;
1102 rows

# Drat.

Q:

What was happening?

A:

The database was configured with
a consistency level of Serializable

Serializable

Database admin:
“Look at this consistency
knob! This goes to 11!”

Serializable

  1. My first statement automatically
    started a new transaction
  2. Inside of that transaction,
    I could see no new data
Executing one statement caused
a frozen copy of the database to
be created for my benefit!
db> SELECT COUNT(*) FROM fast_changing_table;
1102 rows

db> COMMIT;
OK

db> SELECT COUNT(*) FROM fast_changing_table;
1129 rows

Consistency levels

In Django 1.6

with transaction.atomic():
@transaction.atomic()
def person_view(request):
>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.

Transaction Management

1.6

I hear rumors: built-in
database migration support!

1.7

Moving targets

If I am not careful, my
opinions tend to fossilize
An active, living project like
Django keeps you plugged in to a
community that is always thinking about
doing something better than before

Thank you very much!

@brandon_rhodes