Django - Tips, Tricks and a little Python Magic

About Me

Follow me on twitter @simeonfranklin

Or on my blog at http://simeonfranklin.com/blog/

Or come and find me organizing the Python community at http://sfpythonmeetup.com or http://baypiggies.net.

About this talk

Aimed at a novice audience. I hope to:

  1. Give a basic overview of Django

  2. show you some of my favorite Django apps

  3. Talk about advanced Python language features you should be using.

    Feel free to ask questions. Raise your hand. Throw something.
    (No heckling!)

A little History

So let’s oveview!

Django: A "web application framework"

Same space as competitors like Ruby on Rails, Pyramid etc.

Sucessfully used on large projects:

50K+ downloads of Django version 1.5 via PyPi

4,200+ sites listed on http://www.djangosites.org/

Not comprehensive by any means! I have worked on 100+ sites not listed
there!
Curse you 'Django Unchained'
Google Trends. Curse you "Django Unchained"!

So Why Django?

Documentation

Getting Started

So, you want to build a web application.

django-talk/pinterest.jpg
django-talk/reddit.png

Reddit and Pinterest are very different projects.

But both are at least partly:

HTML interfaces to highly dynamic data stored in a database.
What do we have to know?

   Django is HTTP in, HTTP out

django-talk/django-simplified.png

Let’s see if we can understand this much.

Installation

See the documentation.

Basically:

$ virtualenv MYPROJECT
$ source MYPROJECT/bin/activate
(MYPROJECT)$ pip install django

Projects

A web application is a project.

Django comes with a tool to create a starting project for you.

$ django-admin.py startproject storytime
$ find storytime/
storytime/
storytime/storytime
storytime/storytime/settings.py
storytime/storytime/urls.py
storytime/storytime/__init__.py
storytime/storytime/wsgi.py
storytime/manage.py

A project contains:

We can use manage.py to start up a development server to view our web app!

$ python manage.py runserver
Validating models...

0 errors found
Django version 1.5.1, using settings 'storytime.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
django-talk/step1.jpg
Woohoo!

Applications

A project is made of many applications.

Django includes many built in apps in django.contrib for authentication, serving static files, security.

We need to create our own apps. The start page helpfully suggests we try:

$ python manage.py startapp story

Which results in some more files:

$ find story
story
story/views.py
story/__init__.py
story/tests.py
story/models.py

The start page also helpfully told us to edit the settings file to specify a database. Sqlite drivers come with Python so I’ll just use that to start with…

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': 'storytime.sqlite3',
    }
}

I also need to add my new application to my settings file’s INSTALLED_APPS directive:

settings.py
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'story',
)

Fundamental Concept #1: urls

The part after the domain.

http://sillystories.com/foo/

All web frameworks: provide a way to map foo/ to some code to do something.

   urls.py

Django has us write regular expressions that map a url to a view.

urls.py
from django.conf.urls import patterns, include, url


urlpatterns = patterns('',
    # Examples:
    # url(r'^$', 'storytime.views.home', name='home'),
    # url(r'^storytime/', include('storytime.foo.urls')),
)

This kinda sucks, especially for beginners!

Let’s write one url rule and understand it

urls.py
from django.conf.urls import patterns, include, url

urlpatterns = patterns('',
        url(r'^$', 'story.views.home', name='home'),
    )

For more details: see the excellent docs at https://docs.djangoproject.com/en/1.5/topics/http/urls/

Fundamental Concept #2: views

A view is Python code that takes a `request` object and returns a `response` object.

Sound familiar?

django-talk/django-simplified.png
story/views.py
from django.http import HttpResponse

def home(request):
    return HttpResponse("Hello world!")
django-talk/step2.jpg
It works!

But… I don’t see any HTML!

Fundamental Concept #3: templates

   Base Template

Let’s make a base template with our "site design" in it.

$ mkdir -p story/templates/story
$ touch story/templates/story/base.html

Let’s create a minimal html base template

base.html
<html>
  <head>
  <title>This is my web app!</title>
  </head>
  <body>
    <h1>Welcome to Storytime!</h1>
    {% block content %}
    {% endblock %}
  </body>
</html>

   Template inheritance

Let’s create another template called home.html.

home.html
{% extends "story/base.html" %}
{% block content %}
{{ hello }}
{% endblock %}

Oh yeah, the view!

story/views.py
from django.shortcuts import render_to_response

def home(request):
    return render_to_response("story/home.html", {'hello': "Hello World!"})
django-talk/step3.jpg
Now fortified with real HTML!

   Template tags and filters

You’re going to have to learn a bunch of tags and filters.

Don’t worry - they’re easy.

See https://docs.djangoproject.com/en/1.5/topics/templates/ and https://docs.djangoproject.com/en/1.5/ref/templates/builtins/

Fundamental Concept #4: models

Models: database tables represented in Python code.

Your handle to:

Importantly:

A model class == Database table.

A model instance == a database table row

story/models.py
from django.db import models

class Line(models.Model):
    text = models.CharField(max_length=255)

Django comes with a command to create the database structure for us:

$ python manage.py syncdb
Creating tables ...
Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_groups
Creating table auth_user_user_permissions
Creating table auth_user
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table story_line

Now that we’ve got a database created for our models let’s try using the interactive Python console to play with them.

$ python manage.py shell
>>> from story.models import Line
>>> line = Line(text="There once was a girl who had a blue hat.")
>>> line.save()

That’s all the Python code it takes to create a new row in our database. We can also query our database.

>>> Line.objects.all()
[<Line: Line object>]

Maybe we should update the view again!

story/views.py
from django.shortcuts import render_to_response
from .models import Line

def home(request):
    return render_to_response("story/home.html", {'lines': Line.objects.all()})

And the template

home.html

{% extends "story/base.html" %}

{% block content %}
<ul>
{% for line in lines %}
 <li style="color:{% cycle 'blue' 'green' %}">{{ line.text }}</li>
{% endfor %}
{% endblock %}
django-talk/step4.jpg
It works!

As usual see the excellent docs.

https://docs.djangoproject.com/en/1.5/topics/db/models/ and https://docs.djangoproject.com/en/1.5/topics/db/queries/

For free: Admin app

You can enable the built-in admin app by uncommenting a couple lines in the urls.py, the settings.py, and by adding an admin.py file to your application.

admin.py
from django.contrib import admin
from .models import Line

admin.site.register(Line)

I can use this interface to edit my data.

django-talk/step5.jpg

And the data shows up on my web page.

django-talk/step6.jpg
My 3rd grade daughter loves to write stories like this with her classmates.

Congratulations

You’ve just written your first web app

Of course there’s much, much more needed.

All that and it’s not enough

Django is a big framework.

There are lots of batteries included.

(the PDF download of the docs is 1200+ pages)

Applications. Lots and lots of applications.

Because apps are a fundamental piece in Django.

Following are my top 10 apps.

I could totally do a top 20.

I’ll be brief.

   A word about tools

   1. south

database migrations.

Coming soon to django.contrib thanks to a $20k fund raising round on Kickstarter!

   2. django-debug-toolbar

How the heck did I get to this page?

django-talk/debug_toolbar.png
Indispensable must have.

   3. django-extras

Developer Goodies:

   4. crispy-forms & floppy-forms

   5. Haystack

API to multiple backend search engines.

   6. Tastypie

Easily create RESTful api for your Django models.

   7. Celery

This is a big one.

Spin off tasks to separate worker processes.

Can I just punt on explaining this?

   8. easy-thumbnails

Resize images from template tags

<img src="{% thumbnail profile.photo 50x50 %}" alt="" />

   9. django-reversion

Edit history and undo for your models with admin support.

   10. django-grapelli

A prettier admin skin. Plus cool stuff like:

Enough with the apps

You get the idea.

Tons of high quality apps to handle

Got an idea?

Build a redistributable app so you can share your idea!

But make it Pythonic

And Djangoic. Djangoish? Djangonical?

Make it look like Django!

Django is just Python

Django is not magic.

It does use advanced Python language features to provide a clean API.

For instance:

   Declarative Classes

Django likes to use a declarative class to configure things.

Models
class Line(models.Model):
    text = models.CharField(max_length=255)
Forms
class RegistrationForm(forms.Form):
    username = forms.CharField(max_length=30)
    email = forms.EmailField(label="E-mail")
ModelForms
class PollForm(ModelForm):
    class Meta:
        model = Poll
TastyPie Resources!
class UserResource(ModelResource):
    class Meta:
        queryset = Users.objects.all()
        resource_name = "user"

I could keep going.

Haystack Indexes.

Django sitemaps.

They all look alike

Declarative classes whose class attributes are instances of a related type.
Models have ModelFields, Forms have FormFields, etc.

The Class you build is just configuration for building some other more powerful object.

This style is enabled by a feature called metaclasses in Python.

The ability to have the apparently normal instanciation of a class do something completely different.

Deep dark magic!

No!

Just a powerful language feature.

This is deeper than we can go in [fill in X remaining here] minutes.

Let’s try another example.

Abstracting complexity with decorators

If you’ve done a little Django you’ve bumped into the login_required decorator.

story/views.py
from django.shortcuts import render_to_response
from django.contrib.auth.decorators import login_required
from .models import Line

@login_required
def home(request):
    return render_to_response("story/home.html", {'lines': Line.objects.all()})

Just add @login_required above your view.

Pretty cool.

   What is a decorator?

Gonna move fast, see http://simeonfranklin.com/blog/2012/jul/1/python-decorators-in-12-steps/ for a much slower paced introduction if this is your first exposure to decorators.

A decorator is a function that takes function and returns a function in its place.

library = {}

def register(f):
    library[f.__name__] = f
    return f

@register
def myfunc():
    pass

The library variable looks like:

{'myfunc': <function myfunc at 0x9064e2c>}

OH - that’s exactly how Django keeps track of your custom template tags/filters!

Yup. Not magic. Just Python.

   Let’s make a useful decorator!

Our views frequently need to return a RequestContext.

Basically this lets Django provide access to a lot of variables like the current user to the template, automatically.

We could do this manually in our view:

views.py
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.contrib.auth.decorators import login_required
from .models import Line

@login_required
def home(request):
    return render_to_response("story/home.html",
                              {'lines': Line.objects.all()},
                              context_instance=RequestContext(request))

I hate that last line.

We could use a better shortcut: render automatically builds a request context for us.

views.py
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from .models import Line

@login_required
def home(request):
    return render(request,
                   "story/home.html",
                   {'lines': Line.objects.all()})

That’s better. But what about using a decorator to make this view really simple?

   @Render decorator

decorators.py
from django.shortcuts import render

class Render(object):
    def __init__(self, template):
        self.template = template

    def __call__(self, function):
        self.function = function
        def wrapper(request, *args):
            ctx = self.function(request, *args)
            return render(request, self.template, ctx)
        return wrapper

Take a minute:

Now our view looks like:

views.py
from django.contrib.auth.decorators import login_required
from .models import Line
from .shortcuts import Render

@login_required
@Render("story/home.html")
def home(request):
    return {'lines': Line.objects.all()}

Cool?

Mastering Django

All this leads me to my point. How do I get better at Django?

  1. practice. Write different types of apps

  2. read the docs. All of them. (All links on the front page at least. Start at the tutorial.)

  3. Get better at Python

Come see us at http://marakana.com/

Questions?

/

#