Preface

Django docs = killer feature

http://docs.djangoproject.com

(Examples shamelessly stolen from official tutorial*)

* which this is not.

Python

Environment


http://docs.djangoproject.com/en/dev/intro/install/

Surprise!

Built-in development* web server

$ python manage.py runserver

* not for production!

virtualenv


http://www.virtualenv.org/

Philosophy

Philosophy


Building Blocks

Project

"...collection of configuration and apps for a particular Web site"


App

Distinct unit of functionality


myproject

Creating Projects & Apps

$ python django-admin.py startproject mysite
$ cd mysite
$ python manage.py startapp myapp

Just Python modules!

mysite/
    __init__.py
    manage.py
    settings.py
    urls.py
    myapp/
      __init__.py
      models.py
      tests.py
      views.py

"MTV"

MTV ?
  • Model
  • Template
  • View

Models

Models

models.py

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField('Post Body')
    pub_date = models.DateTimeField('Date Published',
                                    auto_now_add=True)

    def __unicode__(self):
        return self.title

Models

Creating the Database

$ python manage.py syncdb

Models

In the shell

$ python manage.py shell

>>> from myapp.models import Post
>>> from datetime import datetime
>>> p = Post(title='New Post')
>>> p.body = 'Lorem Ipsum'
>>> p.save()
>>> p2 = Post.objects.get(id=1)
>>> p3 = Post.objects.get(title='New Post')
>>> posts = Post.objects.filter(pub_date__gte=datetime(2011, 1, 1)
>>> posts = posts.filter(title__contains='Django')
>>> print posts

Request Lifecycle

URLconf

urls.py

from django.conf.urls.defaults import *
from myapp import views

urlpatterns = patterns('',
    (r'^about/$', views.about),
    (r'^blog/post/(\d+)/$', views.view_post, name="post"),
    (r'^blog/by-month/(?P<year>\d{4})/(?P<month>\d{2})/$', 
        views.posts_by_month, name='posts_by_month'),
    (r'^blog/post/(\d+)/post-comment/$', views.post_comment),
)

Views

views.py

from django.http import HttpResponse

def about(request):
    return HttpResponse("Hello World")
def view_post(request, post_id):
    return HttpResponse("Post %d" % post_id)
def posts_by_month(request, month, year):
    return HttpResponse("Posts for %d/%d" % (year, month,))

Views

views.py

from django.shortcuts import render_to_response
from django.template import RequestContext
from django.http import Http404
from myapp.models import Post

def view_post(request, post_id):
    try:
        p = Post.objects.get(pk=post_id)
    except Post.DoesNotExist:
        raise Http404
    return render_to_response('myapp/post.html',
        {'post': p },
        context_instance=RequestContext(request))

Templates

Templates

post.html

<html>
    <head>
        <title>{{ post.title }}</title>
    </head>
    <body>
        <h1>{{ post.title }}</h1>
        <h3>{{ post.pub_date }}</h3>
        <p>{{ post.body }}</p>
    </body>
</html>

Templates: Filters

post.html

<h1>{{ post.title|upper }}</h1>
<h3>{{ post.pub_date|date:"m/d/Y" }}</h3>

Templates: Tags

post.html

{% if post.id == 1 %}
    This is the first post!
{% endif %}

Templates: Inheritance

base.html

<html>
    <head><title>{% block title %}My Site{% endblock %}</title></head>
    <body>
        {% block content %}{% endblock %}
    </body>
</html>

page.html

{% extends "base.html" %}
{% block title %}{{ block.super }} | Page Title{% endblock %}
{% block content %}
    <p>Some content.</p>
{% endblock %}

Forms

forms.py

from django import forms

class ArchiveForm(forms.Form):
    year = forms.ChoiceField(choices=range(2009, 2013), empty_label=None)
    month = forms.ChoiceField(choices=range(1,13), empty_label=None)

sidebar_archive.html

<form action="{{ url }}" method="POST">
    <ul>
        {{ form.as_ul }} {# form.as_table #} {# form.as_p #}
    </ul>
    <input type="submit">
</form>

Forms

views.py

from myapp.forms import ArchiveForm
from django.core.urlresolvers import reverse
from django.http import Http404, HttpResponseRedirect

def redirect_to_archive(request):
    if request.method == "POST":
        archive_form = ArchiveForm(data=request.POST)
        if archive_form.is_valid():
            year = archive_form.cleaned_data.get('year')
            month = archive_form.cleaned_data.get('month')
            return HttpResponseRedirect(reverse('posts_by_month',
                args={'year': year, 'month': month}))
        else:
            raise Http404
    else:
        return HttpResponseRedirect("/")

ModelForms

models.py

class Comment(models.Model):
    created_on = models.DateField(auto_now_add=True)
    name = models.CharField(max_length=50)
    email = models.EmailField()
    body = models.TextField()
    post = models.ForeignKey('Post')

forms.py

from django import forms
from myapp.models import Comment

class CommentForm(forms.ModelForm):

    class Meta:
        model = Comment
        fields = ('name', 'email', 'body')

ModelForms

views.py

from django.http import HttpResponseBadRequest, HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.shortcuts import get_object_or_404
from myapp.forms import CommentForm
from myapp.models import Comment, Post

def post_comment(request, post_id):
    post = get_object_or_404(Post, id=post_id)
    if request.method == 'POST':
        comment = Comment(post=post)
        comment_form = CommentForm(instance=comment, 
            data=request.POST)
        if comment_form.is_valid():
            comment_form.save()
            return HttpResponseRedirect(reverse('post',
                args=[post_id])
    else:
        return HttpResponseBadRequest()

ModelForms

from django import forms
from myapp.models import Comment
from datetime import datetime, timedelta

class CommentForm(forms.ModelForm):
    body = forms.CharField(widget=forms.Textarea(
        attrs={'rows': 5, 'cols': 40}))
  
    def clean(self):
        email = self.cleaned_data.get(email)
        one_minute_ago = datetime.now() - timedelta(minutes=1)
        recent_comments = Comment.objects.filter(email=email,
            created_on__gte=one_minute_ago)
        if recent_comments.count() > 0:
            raise forms.ValidationError('You talk too much!')
        return self.cleaned_data
  
    class Meta:
        model = Comment
        fields = ('name', 'email', 'body')

Admin

admin.py

from myapp.models import Post, Comment
from django.contrib import admin

admin.site.register(Post)

class CommentAdmin(admin.ModelAdmin):
    list_display = ('email', 'created_on')
    list_filter = ('post', )
    search_fields = ('name', 'email')
admin.site.register(Comment, CommentAdmin)

Admin


Admin

Override-able ModelForm

Admin


Admin

Filter, Search, etc.

I Wish I Knew...

Debugging


django-debug-toolbar


Debug Toolbar

Debugging


django-extensions

$ python manage.py shell_plus
From 'auth' autoload: Permission, Group, User, Message
From 'contenttypes' autoload: ContentType
From 'sessions' autoload: Session
From 'sites' autoload: Site
From 'myapp' autoload: Post, Comment
From 'admin' autoload: LogEntry
>>> 

Also: create_superuser, print_user_for_session, etc.

settings.py


It's Python!

import os
MEDIA_ROOT = os.path.join(PROJECT_PATH, 'media')

Context Processors

from django.conf import settings

def analytics(request):
    return {'ANALYTICS_CODE': settings.ANALYTICS_CODE,
            'ANALYTICS_DOMAIN': settings.ANALYTICS_DOMAIN,
           }

Middleware

Middleware

Transactions

South



Schema Migration

http://south.aeracode.org

Contrib/Pluggable Apps

Deployment

And There's More...

Thanks!

Pony