Lessons‎ > ‎

Django Framework

Django is a web application framework based on Python.  Typically, App Engine applications that use Django use a subset of the full Django framework, since Django's ORM requires a relational SQL database backend.  But quite a bit of the Django framework can be used on App Engine:
Some Django features don't work natively on App Engine (but you can get around almost all of these limitations by using app-engine-patch or the newer Django nonrel project):
Templateu
  • Django Models
  • Django Admin Console
  • User Management/Accounts
  • Session management

Object-Relational Mapping (ORM)

When you define your models, Django takes care of the database interface. The App Engine Datastore is not directly supported by Django because it's not a relational database. But the Datastore has a model interface that is very similar to Django's. The following intro was adapted from an app-engine-patch tutorial by Jesaja Everling.

sample/guestbook/models.py defines a simple object model:
from google.appengine.ext import db

class Greeting(db.Model):
    author = db.StringProperty()
    website = db.StringProperty()
    content = db.StringProperty(multiline=True)
    date = db.DateTimeProperty(auto_now_add=True)

sample/guestbook/urls.py defines the URL patterns for this app:
from django.conf.urls.defaults import *

urlpatterns = patterns('guestbook.views',
    (r'^$', 'list_entries'),
)

sample/urls.py then points to sample/guestbook/urls.py:
urlpatterns.extend(patterns('',
    (r'guestbook/', include('guestbook.urls')),
))

The guestbook app also needs to be added to sample/settings.py:
INSTALLED_APPS = (
    'django.contrib.humanize',
    # ...
    'guestbook',
)

sample/guestbook/views.py contain the code to view and sign the guestbook:
from django import forms
from django.shortcuts import render_to_response, redirect
from guestbook.models import Greeting

class GuestbookForm(forms.Form):
    author = forms.CharField(max_length=40)
    website = forms.URLField(required=False)
    content = forms.CharField(widget=forms.Textarea)

def list_entries(request):
    guestbook_form = GuestbookForm(request.POST or None)
    if guestbook_form.is_valid():
        greeting = Greeting(
            author=guestbook_form.cleaned_data['author'],
            website=guestbook_form.cleaned_data['website'],
            content=guestbook_form.cleaned_data['content'])
        greeting.put()
        return redirect(list_entries)
    greetings_list = Greeting.all().order('-date').fetch(10)
    return render_to_response('guestbook/index.html', locals())


sample/guestbook/templates/guestbook/index.html contains the HTML template:
{% extends "master.html" %}

{% block body %}
<h1>Guestbook</h1>
{% for greeting in greetings_list %}
<p><a href="{{ greeting.website }}">{{ greeting.author }}</a> said
{{ greeting.date|timesince }} ago:<br />
{{ greeting.content }}</p>
{% endfor %}

<form action="/guestbook/" method="POST">
<table>
{{ guestbook_form }}
<tr><th></th><td><input type="submit" value="Submit"></td></tr>
</table>
</form>
{% endblock %}

Datastore access

The App Engine datastore is schema-less. This means that if you decide to add or remove properties in your models, you don't have to adjust the columns in your database table. Missing properties will simply return None, and entities will not be matched by filters on missing properties. Actually, each entity can have flexible properties that are added and removed at runtime, if you use the google.appengine.ext.db.Expando class for your model definition.

You can read entities from the datastore by using the model's all() attribute:
Greeting.all()                              # all greetings
Greeting.all().fetch(10)                    # fetch only 10 greetings
Greeting.all().order('-date')               # newest greetings first
Greeting.all().filter('author', 'Johann')   # all greetings from Johann
Greeting.all().filter('author', 'Johann').order('-date')

Or you could use GQL, a datastore query language that is very similar to SQL:

SELECT * FROM Greeting WHERE author = 'Johann' ORDER BY date DESC LIMIT 10 OFFSET 20

You can write an entity to the datastore with the put() method:
greeting = Greeting.all().order('date').get()  # fetch the oldest greeting
greeting.author = 'Johann'                     # change a property
greeting.put()                                 # save it back to the datastore

Django Forms

The guestbook uses a simple Django form to automatically validate the input from your users. For example, it uses URLField to check if the submitted value looks like a website address. If not, the form is displayed again, with a short error message.

The form is included in the template using {{ guestbook_form }} which produces three table rows with labels and input fields. Or you can use {{ guestbook_form.as_p }} if you want to structure it with <p> rather than <table>.

The user-submitted input is checked when you call guestbook_form.is_valid(). This method returns False if there was a problem. In that case, we display the input form again. The error messages are automatically displayed by the form.

Django Templates

Django includes a template system to generate HTML output for your app. The guestbook/index.html template above extends templates/master.html and simply adds its content in the body block. So if you want to make changes to the header or footer that appears on every page on your site, you can just edit master.html. The template files contain HTML, with two special Django features: {% tag %} and {{ value }} or {{ value|filter }}. The values are passed through render_to_response as a Python dict (associative array). The view function above uses local() to pass all local variables.
Comments