Google App Engine

Running Pure Django Projects on Google App Engine

This document is another in our series of guest articles written in conjunction with external developers. The Google App Engine team thanks Thomas Wanschik and Waldemar Kornewald for their time and expertise.

Thomas Wanschik & Waldemar Kornewald (All Buttons Pressed), Wesley Chun (Google)
November 2010
Python SDK: 1.2.3 (launch of Django 1.x support)

Introduction

Django-nonrel is a project which allows developers to run native Django projects (including Django's ORM) on non-relational databases, one of which is Google App Engine's Datastore. (This is all in addition to the standard traditional SQL databases that have always been supported by Django.) App Engine does come with some Django support, but this is mainly only the templating and views. Other tools that allow for rapid development such as forms, the built-in administration interface or Django authentication just won't run out-of-the-box. Django-nonrel changes this for Django developers.

To run Django on a "NoSQL" database, all you need is the appropriate backend. Django-nonrel has several of these, one for App Engine (djangoappengine) and another for MongoDB (django-mongodb-engine) at the time of this writing — a Cassandra backend and a ElasticSearch backend are also in active development at this time. If you wish to help build backends for other NoSQL databases, e.g., Redis, SimpleDB, CouchDB, etc., please join the discussion group.

What this means for Django users is that now you can use your existing Django knowledge on App Engine. As long as you keep App Engine restrictions in mind (e.g., no JOINs) your projects will work without any modifications other than changing the configuration settings. In the future even simple JOINs and aggregates will be supported! In addition, applications built using Django-nonrel will have a high degree of two-way portability on to and off of App Engine. For instance, code written for MongoDB with Django-nonrel can be reused on App Engine very easily. To get going, follow the download and install instructions at the djangoappengine page.

Current Django users: If you just want to run your existing or a new Django project on App Engine, you can stop reading this article. Instead, read the djangoappengine backend documentation and the Django-nonrel blog for more information about App Engine restrictions and about how to port and write effective code. However, if you want to learn how to port an existing App Engine webapp-based app to Django, then read on. At the time of this writing, Django-nonrel is a fork of Django you will use instead of your current Django installation. You can also grab the example package in this preconfigured ZIP file or its Mercurial repository to get a fully-working project now — all you need to do is to drop in Django-nonrel (and its associated packages) in the project directory.

This article's main purpose is to show existing App Engine developers how to convert your webapp apps to Django so that you can either keep it running on App Engine or take it to any external hosting provider supporting Django.

App Engine webapp app

Users may be familiar with the webapp lightweight web framework that comes with App Engine. While Google provides this as part of the App Engine distribution, you are not required to use it. Other frameworks that work with App Engine include Django, web2py, tipfy and a few others.

The sample app we're going to port is a guestbook application similar to but shorter and easier-to-read than the one in the Getting Started guide. The guestbook application is at the stage where we've added code for using these APIs: Users, Datastore, and Memcache. We've also properly moved all markup to a Django template, index.html. To keep our naming convention simple, we'll call our original webapp guestbook "webapp" and the Django port "django-guestbook".

Let's take a look at our original webapp app first. Below is our simple app.yaml file (to try it, please change the project name to something else other than "APP_NAME") — it is pretty much unmodified from what the App Engine launchers would generate when creating a new application.

# webapp/app.yaml
application: APP_NAME
version: 1
runtime: python
api_version: 1

handlers:
- url: .*
  script: main.py

The index.html has to be created by you however. Here's what ours looks like:

    <!-- Original web template, "webapp/index.html" -->
    <html>
    <body>Hello
    {% if user %}
        {{ user.nickname }}!
        [<a href="{{ logout }}"><b>sign out</b></a>]
    {% else %}
        World!
        [<a href="{{ login }}"><b>sign in</b></a>]
    {% endif %}

    <h2>Top 10 Most Recent Guestbook Entries</h2>

    {% for greeting in greetings %}
        <br>
        <small>[<i>{{ greeting.date.ctime }}</i>]</small>
        <b>
        {% if greeting.author %}
            <code>{{ greeting.author.nickname }}</code>
        {% else %}
            <i>anonymous</i>
        {% endif %}
        </b>
        wrote:
        {{ greeting.content|escape }}
    {% endfor %}

    <hr>
    <form action="/sign" method=post>
        <textarea name=content rows=3 cols=60></textarea>
        <br><input type=submit value="Sign Guestbook">
    </form>
    </body>
    </html>

Finally, we have our application in main.py:

# Main application file: "webapp/main.py"
import os
    
from google.appengine.api import memcache, users
from google.appengine.ext import db, webapp
from google.appengine.ext.webapp.template import render
from google.appengine.ext.webapp.util import run_wsgi_app

class Greeting(db.Model):
    author = db.UserProperty()
    content = db.TextProperty()
    date = db.DateTimeProperty(auto_now_add=True)

class MainHandler(webapp.RequestHandler):
    def get(self):
        user = users.get_current_user()
        greetings = memcache.get('greetings')
        if not greetings:
            greetings = Greeting.all().order('-date').fetch(10)
            memcache.add('greetings', greetings)
        context = {
            'user':      user,
            'greetings': greetings,
            'login':     users.create_login_url(self.request.uri),
            'logout':    users.create_logout_url(self.request.uri),
        }
        tmpl = os.path.join(os.path.dirname(__file__), 'index.html')
        self.response.out.write(render(tmpl, context))

class GuestBook(webapp.RequestHandler):
    def post(self):
        greeting = Greeting()
        greeting.content = self.request.get('content')
        greeting.put()
        memcache.delete('greetings')
        self.redirect('/')

application = webapp.WSGIApplication([
    ( '/', MainHandler),
    ( '/sign', GuestBook),
], debug=True)

def main():
    run_wsgi_app(application)

if __name__ == '__main__':
    main()

Now, let's port it to Django.

Federated Login: Please leave your App Engine's authentication as "Google Accounts" for now — do not set it to Federated Login until this issue and this one have been resolved.

Porting steps

There are basically 4 steps in migrating from webapp to Django:

  1. Converting to Django's file structure
  2. Transform your models
  3. Replace your request handlers
  4. Replace your URL-routing configurations
As mentioned before, if you're already familiar with Django, you can just get the completely ported django-guestbook application as a ZIP file or the hg repo that you can use immediately.

Converting to Django's file structure

The first step in understanding Django is to be aware of its development ecosphere and how that relates to the Django file structure. Django has the concept of a project, which is essentially a set of global files that dictate behavior and manage one or more applications. You can think of a project as a single website with one or more apps that run underneath it like a blog, guestbook, etc. You can read more about the differences between projects and apps by taking a look at the sidebar in the Creating Models section of the Django tutorial. Note that the terminology differs from App Engine where the "app" is all-inclusive of everything you upload to Google, so in that sense, it's more like what you would consider a Django project.

In Django, a project basically consists of 4 files (__init__.py, urls.py, settings.py, manage.py) plus one or more app directories. Here are brief descriptions of the files:

  • __init__.py — tells Python this is a package
  • urls.py — global URL config ("URLconf") file
  • settings.py — project-specific configuration
  • manage.py — command-line interface for apps

As mentioned above, a project will then contain one or more apps (one folder per app). Each of these subdirectories will have their own set of files (__init__.py, models.py, views.py, tests.py) as well as an optional app-level URLconf and a templates directory. The four most important files are:

  • __init__.py — same as above
  • models.py — data models
  • views.py — request handlers
  • tests.py — unit tests (optional but strongly-encouraged)

In addition, an app can also be "rebranded" as an app package, a reusable piece of functionality that can be dropped into any Django project. One example of such an app package would be a full-text-search app like nonrel-search.

Getting Started with Django: To get started with Django and see some of those files described above, here are a few steps you can follow immediately (assuming you have Django or Django-nonrel installed):
  • Create a new project:
    • $ django-admin.py startproject PROJECT_NAME
    • $ cd PROJECT_NAME
    • View the project files described above
  • Create a new app:
    • ./manage.py startapp APP_NAME
    • $ cd APP_NAME
    • View the app files described above

The guestbook application itself represents a Django app, so we start with creating an app called "guestbook" along with all required files via manage.py startapp guestbook. Note that our port's project name is "django-guestbook". This project contains the just created app "guestbook" so we get the following file structure: "django-guestbook/guestbook".

Models

There are several steps necessary in porting existing App Engine models to Django:

  1. Move your data models to the models.py file
  2. Derive your models from django.db.models.Model instead of google.appengine.ext.db.Model
  3. Replace App Engine properties with corresponding Django fields

Let's take a look at just the App Engine model definition for the Greeting class:

# App Engine Datastore model, "webapp/main.py"
from google.appengine.ext import db

class Greeting(db.Model):
    author = db.UserProperty()
    content = db.TextProperty()
    date = db.DateTimeProperty(auto_now_add=True)

Following the steps mentioned above we have to switch the model base class to Django's then replace the Property classes. UserProperty is kind of special because Django does not provide a field for users. Instead we have to use a reference to Django's User model.

Additionally, the guestbook application allows for the creation of Greeting objects with an anonymous author, so we have to allow storing empty values in that field. This can be done by setting the options null=True, blank=True. (The blank option is only used for validation in forms and Django's admin interface while null is used for validation at the model/database level). Here is the resulting content for the models.py file:

# Corresponding Django model, "django-guestbook/guestbook/models.py"
from django.db import models
from django.contrib.auth.models import User

class Greeting(models.Model):
    author = models.ForeignKey(User, null=True, blank=True)
    content = models.TextField()
    date = models.DateTimeField(auto_now_add=True)

Let's test it: As long as we don't have a user interface you can play with the models in a nice Python shell by running manage.py shell. Here's an example using Django style queries:

>>> from guestbook.models import Greeting
>>> Greeting.objects.count()
0
>>> greeting = Greeting(content='Hi!')
>>> greeting.save()
>>> Greeting.objects.count()
1
>>> greeting = Greeting.objects.all()[0]
>>> greeting.content
'Hi!'

Additionally, Django helps you with unit testing. You can put app-specific unit tests in a tests.py file within your Django apps. For example, unit tests for the guestbook app would go into guestbook/tests.py.

from django.test import TestCase
from guestbook.models import Greeting

class SimpleTest(TestCase):
    def setUp(self):
        Greeting(content='This is a test greeting').save()

    def test_setup(self):
        self.assertEqual(1, len(Greeting.objects.all()))
        self.assertEqual('This is a test greeting', Greeting.objects.all()[0].content)

Here we first test that we only have one entity in the test datastore, and then confirm that its content matches the test string 'This is a test greeting'. Django will automatically execute these unit tests when you run manage.py test. Django provides a handy django.test.TestCase class which extends Python's unittest.TestCase — we highly recommend that you use Django's TestCase as opposed to unittest in particular because the former automatically flushes your DB after every test which guarantees that each test starts with a clean slate. django.test.TestCase also comes with lots of other useful additions you should check out. See the Testing Django applications section in the Django documentation for more information.

Request handlers

Django's request handlers, called views, are just Python functions. That means that we have to replace all of App Engine's class-based request handlers with Django views. In general, these steps are:

  1. Replace request handlers with views and place in views.py
  2. Replace all App Engine queries with Django queries
  3. Output the query results presumably with some template

GET handler

We will now go through each of the request handlers, one-at-a-time. Let's start with the MainHandler class and its get() method:

# App Engine GET request handler, "webapp/main.py"
import os

from google.appengine.api import memcache, users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.template import render

class MainHandler(webapp.RequestHandler):
    def get(self):
        user = users.get_current_user()
        greetings = memcache.get('greetings')
        if not greetings:
            greetings = Greeting.all().order('-date').fetch(10)
            memcache.add('greetings', greetings)
        context = {
            'user':      user,
            'greetings': greetings,
            'login':     users.create_login_url(self.request.uri),
            'logout':    users.create_logout_url(self.request.uri),
        }
        tmpl = os.path.join(os.path.dirname(__file__), 'index.html')
        self.response.out.write(render(tmpl, context))
# Corresponding Django view, "django-guestbook/guestbook/views.py"
from django.core.cache import cache
from django.views.generic.simple import direct_to_template
from guestbook.forms import CreateGreetingForm
from guestbook.models import Greeting

def list_greetings(request):
    greetings = cache.get(MEMCACHE_GREETINGS)
    if greetings is None:
        greetings = Greeting.objects.all().order_by('-date')[:10]
        cache.add(MEMCACHE_GREETINGS, greetings)
    return direct_to_template(request, 'guestbook/index.html',
        {'greetings': greetings, 'form': CreateGreetingForm()})
      .
      .
      .

Here we've swapped out the use of App Engine's memcache API in favor of Django's caching framework. This way our code becomes platform-independent.

You've probably noticed that there is a strange CreateGreetingForm passed to the template. This is the form that gets displayed to the user when he wants to submit a greeting. In Django you normally don't write forms manually in HTML. There are several advantages to this and we'll explain everything once we look at the form submission view.

Queries

We've also replaced App Engine query with the appropriate one for Django. This can be pretty easy, as in our case:

# App Engine query
greetings = Greeting.all().order('-date').fetch(10)
# Corresponding Django query
greetings = Greeting.objects.all().order_by('-date')[:10]

Query results output

One of Django's greatest strengths is its adherence to the DRY principle (don't repeat yourself). The architects of Django recognized several common code patterns and have created various shortcuts to simplify repetitive tasks and common patterns. To this end, Django has the concept of generic views, which not only map results to templates, but also simplify the development process by abstracting a lot of recurring code.

In our case, the generic view direct_to_template is used in order to render the fetched Greeting objects and the form. Also, it takes care of adding useful extra variables like the current request and user to the view. This step can be configured via so-called context processors which let users add, modify, or delete variables that eventually get sent to the template. In general, generic views provide functionality for common tasks like creating/editing or displaying model instances.

What is the alternative? You would have to create custom views for all user output, giving yourself and your app a high probability of repeating yourself. Also note that direct_to_template is the simplest available generic view. You'll save a lot more time (and code) with more advanced generic views like object_list that takes care of paginating a query and displaying current results pages — you really don't want to be repeatedly writing such common functionality by hand.

Modified template structure

Before we talk about the POST handler and creating objects, let's finish off the output of the Greeting objects by showing you how we changed the original index.html template. Let's start with the listing of all guestbook entries:

<!-- App Engine object output in webapp/index.html -->
      .
      .
      .
{% for greeting in greetings %}
    <br>
    <small>[<i>{{ greeting.date.ctime }}</i>]</small>
    <b>
    {% if greeting.author %}
        <code>{{ greeting.author.nickname }}</code>
    {% else %}
        <i>anonymous</i>
    {% endif %}
    </b>
    wrote:
    {{ greeting.content|escape }}
{% endfor %}
      .
      .
      .
<!-- Corresponding Django object output in django-guestbook/guestbook/templates/guestbook/index.html -->
      .
      .
      .
{% for greeting in greetings %}
    <br>
    <small>[<i>{{ greeting.date.ctime }}</i>]</small>
    <b>
    {% if greeting.author %}
        <code>{{ greeting.author.username }}</code>
    {% else %}
        <i>anonymous</i>
    {% endif %}
    </b>
    wrote:
    {{ greeting.content }}
{% endfor %}
      .
      .
      .

If you look carefully, there are only two visible changes. The first one is the username attribute. The other change is that we don't use |escape to escape the greeting content. In new versions of Django, templates automatically escape all attributes. This reduces the risk of code-injection vulnerabilities significantly. Apart from that, there are very few changes because the App Engine app was already using an old version of the Django template language. All we had to do was switch to the equivalent Django model attribute name and the new auto-escaping mechanism.

Authentication: For the purposes of porting this app as quickly as possible, we've left out discussing auth because it represents an entire topic on its own so we'll just give a quick intro and save auth for another time.

In a webapp, you have a choice of four different authentication models:

Because we're no longer using webapp, we must go with what Django provides:

Setting up auth in Django is also simple but more complex than webapp's because you have to roll your own login and logout templates, user management, etc. If you use Google Accounts in your native app, you use the Google login screen and Google manages users.

Below is the original user information header in our webapp app then the equivalent for our Django port.

<!-- App Engine auth in webapp/index.html -->
      .
      .
      .
Hello
{% if user %}
    {{ user.nickname }}!
    [<a href="{{ logout }}"><b>sign out</b></a>]
{% else %}
    World!
    [<a href="{{ login }}"><b>sign in</b></a>]
{% endif %}
<!-- Corresponding Django auth in django-guestbook/templates/base.html -->
      .
      .
      .
Hello
{% if user.is_authenticated %}
    {{ user.username }}!
    [<a href="{% url django.contrib.auth.views.logout %}"><b>sign out</b></a>]
{% else %}
    World!
    [<a href="{% url django.contrib.auth.views.login %}"><b>sign in</b></a>]
{% endif %}
      .
      .
      .

Aside from a different method to display the username, you will notice the obvious change in the generation of login/logout links. We use the url template tag which gives us the URL for a corresponding view, which also then have to be registered in your settings.py and URLconf files. In the downloadable example project, we'll have all the code in place to use Django authentication... we just don't get into the details here — this includes templates that prompt the user to login and to create new users.

Also, you'll see that we've placed the login/logout template code into a base.html template and our index.html derives from that template. This isn't required, but it does set a good example, but because it's orthogonal to this discussion, we've left those details out of the content here. Just don't be surprised if you download the code and see a few minor bits that we didn't describe.

Finally, you must have noted the template location change from a simple index.html at the top-level directory to a more complicated django-guestbook/templates/guestbook/index.html. The reason for being pushed down into the application's directory is that it separates your code more cleanly into individual components. This is very important because it increases the reusability of your components and it helps prevent your project from becoming an unmaintainable mess once it begins to grow. Instead of putting the templates of all Django apps in the same folder and deal with name clashes, you should store them as part of its corresponding app. Similarly, every other app-specific code should be part of its file hierarchy instead of being placed higher up with the project.

Note that the app name appears twice in the path. The first occurrence is the Python package name of your app. The second occurrence is the folder name for your app's templates. Typically the template folder is named after the application name itself. However, in some cases an app might need to provide templates in a different folder. Django's Admin interface is one example. It contains templates for the admin app and it also provides templates in the "registration" folder. This is done by using the following template file structure: "admin/templates/admin/*.html" and "admin/templates/registration/*.html".

Okay, that was the first half. The second part involves user input, form submission, and creating a new App Engine entity using Django's ORM instead of the App Engine model class.

POST handler

# App Engine request handler, "webapp/main.py"
      .
      .
      .
class Guestbook(webapp.RequestHandler):
    def post(self):
        greeting = Greeting()
        greeting.content = self.request.get('content')
        greeting.put()
        memcache.delete('greetings')
        self.redirect('/')
      .
      .
      .
# Corresponding Django view (straightforward version), "django-guestbook/guestbook/views.py"
      .
      .
      .
from django.http import HttpResponseRedirect
from guestbook.models import Greeting
      .
      .
      .
def create_greeting(request):
    if request.method == 'POST':
        greeting = Greeting()
        greeting.content = request.POST.get('content')
        if request.user.is_authenticated():
            greeting.author = request.user
        greeting.save()
        cache.delete('greetings')
    return HttpResponseRedirect('/guestbook/')
      .
      .
      .
# Corresponding Django view (best-practice version), "django-guestbook/guestbook/views.py"
      .
      .
      .
from django.http import HttpResponseRedirect
from guestbook.forms import CreateGreetingForm
      .
      .
      .
def create_greeting(request):
    # bound form (user input in request)
    if request.method == 'POST':
        form = CreateGreetingForm(request.POST)
        if form.is_valid():
            greeting = form.save(commit=False)
            if request.user.is_authenticated():
                greeting.author = request.user
            greeting.save()
            cache.delete('greetings')
    return HttpResponseRedirect('/guestbook/')
      .
      .
      .

The App Engine handler is very straightforward: it grabs the submitted content from the web form and installs it as part of a new Greeting object, stores it in the datastore, flushes the stale cache element, and redirects to the front page. There is no other functionality in our handler. Here we show two ways to solve this in Django. In this particular example where you have just a single input field you might use the first version because it's very simple. However, in many cases you want to display more than a simple text input field. Also, over time your project will accumulate more and more features. This is where the first version breaks down.

The second (best-practice) version is more DRY because it uses a Django form. Imagine you want to add a new field to your model and have the user provide input for it. In the first version you'd have to modify (1) the Greeting model, (2) the submission view, and (3) the template which displays the form. In the second version you only modify the Greeting model. Everything else will automatically be handled by the form! Hold on, we're not finished, yet. Imagine you want to display a field for entering a date. In the first version you'd have to write a special routine which knows how to convert the user input (which is a string like "2/19/10") into a datetime.date object. In contrast, a Django form does this automatically for you! Oh, it gets even better: If the user enters an invalid date the Django form will display a nice message telling the user how to fix his input. You see, forms provide a lot of benefits, so let's take a look at that code more closely:

create_greeting() begins with a fork in the road based on whether the form was submitted or not. If not, we simply redirect back to the main page. As you can clearly see, the bulk of this view takes care of the situation where the form was submitted.

In this case, a Greeting object is automatically created with attributes assigned directly from the form and normally written to the database with the form.save() call. An attribute like the greeting.author isn't entered by the user — it saves request.user — (and neither is the date which is automatically created). If you require customizing the object, you need to: 1) use the commit=False flag for form.save(), 2) do your custom modifications, then 3) explicitly save that object to the database. The rest of the code (flush cache and redirect) is pretty much the same as the App Engine original.

The most intriguing part of this code right now has to be the form creation, presentation, submission, and validation, so let's take a look at that form code.

Introduction to Django Forms: Generically, Django forms are a programmatic abstraction of HTML forms used to present, capture, and validate data. Form objects solve the common practice of developers needing to manage mappings between HTML input fields, POST request parameters, and validation by encapsulating this functionality into testable, configurable objects. You instantiate them in one of two ways: bound or unbound.

The "binding" is whether or not there is data that comes from a form submission. In other words, if you instantiate it with no parameters, you want to show the form (unbound); conversely, if a form was posted, you'll pass in a dictionary like request.POST (as we've done up above) to process the form data (bound). But how do you create a form... especially when you have a model class which includes fields you want to use in the form? Creating a Form object is nearly identical to the model class itself:

# model class
class MyClass(models.Model):
    version = models.CharField(max_length=32)
    content = models.CharField(max_length=256)
    date = models.DateField(blank=True, null=True)

# form class
class MyClassForm(forms.Form):
    version = forms.CharField(max_length=32)
    content = forms.CharField(max_length=256)
    date = forms.DateField(required=False)

Huh? They look nearly identical! You know this is too much code duplication for Django & DRY. Instead, you'd actually write it like this — note the base class change:

# model-form class
class MyClassForm(forms.ModelForm):
    class Meta:
        model = MyClass

Instead of just a normal Form object, we're using a specific one called a ModelForm. Why create individual form elements duplicating efforts as shown earlier when you can tell the code to take those fields directly from a data model class? Our example is slightly more complex than the simple example here, so keep reading to see what other customizations we need to make for our app.

Our ModelForm object

Going slightly backwards, we just saw above how data that came in through the form and request are saved to the database before the user is redirected to the main guestbook entry page. Now we need to see how that form was created. As we saw in the POST handler, Greeting.author will come from the request object, and the date is added automatically, so when creating the form, we need to exclude those from the form because they don't come from the user:

# Django forms model, "django-guestbook/guestbook/forms.py"
from django import forms
from guestbook.models import Greeting

class CreateGreetingForm(forms.ModelForm):
    class Meta:
        model = Greeting
        exclude = ('author', 'date') # same as "fields = ('content',)"

For more complex models this can save you a lot of code. When using model-forms, Django automatically creates a form for a given model. Additionally, we can use a model form directly in a template as you can see from the Django version of the form snippet from the original webapp index.html:

   # App Engine's webapp/index.html snippet
         .
         .
         .
   <form action="/sign" method="post">
      <div><textarea name="content" rows="3" cols="60"></textarea></div>
      <div><input type="submit" value="Sign Guestbook"></div>
    </form>
         .
         .
         .
   # Corresponding Django snippet in "django-guestbook/templates/guestbook/index.html"
         .
         .
         .
   <form action="/guestbook/sign" method="post">{% csrf_token %}
        <table>{{ form }}</table>
        <input type="submit" value="Sign Guestbook" />
   </form>
         .
         .
         .

Okay, now we're ready to tie it all together by setting up our URLconf (urls.py) files.

URL-routing configurations

Because we now have different templates for creating and displaying Greeting instances, this leads to our app having two URLs in Django (but only one in our webapp app):

# App Engine URL configuration
- url: /.*
  script: main.py
# Django URL configuration, "django-guestbook/guestbook/urls.py"
from django.conf.urls.defaults import *

urlpatterns = patterns('guestbook.views',
    (r'^$',     'list_greetings'),
    (r'^sign$', 'create_greeting'),
)

Python App Engine apps use a YAML file to store some overall app configuration including global URL routing and more specific URL-routing within the application for specific handler dispatch. Similarly in Django, the global configurations occur in the settings.py file while specific URL-routing and dispatch happen in the URLconf files (urls.py at the project level and for individual apps). The first argument to patterns is a module in which Django can find the corresponding views — think of it as a prefix. All remaining arguments are tuples which map regular expressions to views.

Global files

In order to make the port complete we have to adapt the remaining two global files for a Django project.

Project URLconf file

In order to activate the app's URLconf file (django-guestbook/guestbook/urls.py), we have to include it into the global django-guestbook/urls.py file. In Django this can be achieved by using the include() function. Additionally, we add URLs for logging users in and out as well as for creating them.

from django.conf.urls.defaults import *
from django.contrib.auth.forms import AuthenticationForm

urlpatterns = patterns('',
    (r'^$', 'django.views.generic.simple.redirect_to', {'url': '/guestbook/',}),
    (r'^guestbook/', include('guestbook.urls')),

    # auth specific urls
    (r'^accounts/create_user/$', 'guestbook.views.create_new_user'),
    (r'^accounts/login/$', 'django.contrib.auth.views.login',
        {'authentication_form': AuthenticationForm,
        'template_name': 'guestbook/login.html',}),
    (r'^accounts/logout/$', 'django.contrib.auth.views.logout',
        {'next_page': '/guestbook/',}),
)

settings.py File

The project settings.py requires extensive work over the stock one from Django. In fact, just replace it with the one that comes with our example ported project which you can see below. The most important part is adding your app's name to INSTALLED_APPS in order to point Django to our guestbook app. Also don't forget to change the SECRET_KEY to your own!

from djangoappengine.settings_base import *

import os

SECRET_KEY = '=r-$b*8hgw3sc58&9t0twan5ch1k-3d3vfc4(wk0rn3wa1dhvi'

INSTALLED_APPS = (
    'djangoappengine',
    'djangotoolbox',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'guestbook',
)

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
)

TEMPLATE_CONTEXT_PROCESSORS = (
    'django.contrib.auth.context_processors.auth',
    'django.core.context_processors.request',
)

LOGIN_REDIRECT_URL = '/guestbook/'

ADMIN_MEDIA_PREFIX = '/media/admin/'
MEDIA_ROOT = os.path.join(os.path.dirname(__file__), 'media')
TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), 'templates'),)

ROOT_URLCONF = 'urls'

At this point, the porting process is complete, and you can safely delete all remaining files from the old webapp application, leaving only the Django files (except for app.yaml which is necessary for deployment).

Deploy and run your project

You can test your Django project locally by executing

$ manage.py runserver

from your shell and visiting localhost:8000 from within your browser.

In order to upload your code as well as the latest Django (Django-nonrel) version to App Engine, copy the app.yaml that comes with the example ported project and replace the application name with your own APP_NAME app ID. Then execute the following Django command from your shell:

$ manage.py deploy

You'll be prompted for your usual login and password. That's it, the django-guestbook project should now run on App Engine using Django-nonrel.

Epilogue

Once you've completed the final task of uploading your app (and barring any bugs), you should be able to hit your Django project running on App Engine now... congratulations! Of course, this is only the end of one story.

Vendor & database lock-in

Should you ever decide to take your app off of App Engine and go with traditional hosting, you can run it with another NoSQL database like MongoDB — you only have to change your settings.py to point to the other database and remove djangoappengine from your project. It'd also be easy enough to port a majority of your code to a SQL database, so as long as you haven't designed your data model around specific NoSQL features.

This is extremely powerful because you're not locked into either Google nor a specific non-relational database. This is the power of the flexibility that Django-nonrel brings to your Django projects. According to a member of the Google App Engine team:

The Django-nonrel project is significant in that it is the first one to make "vendor lock-in" much less of an issue when deploying apps on Google App Engine. Many enterprise and small businesses are concerned with lock-in — the phenomenon where a firm puts its apps and/or its data into the hands of a single vendor that makes it difficult or impossible for their customers to migrate their data and/or apps elsewhere. Using the approach described here, if you ever feel down the road that your app has outgrown Google App Engine or wish to host it yourself, you will be able to do just that. Similarly, if you've developed a Django app and feel that it's maxing out your capacity, or your app is scaling beyond the resources at your disposal, bring it over to App Engine with minimal effort and let Google scale it for you! Of course here at Google, we'd love to keep you as an App Engine platform user, but you now have a choice, and the more choices there are, the better it is for industry as a whole.

Prior work

Before Django-nonrel, there were two projects preceding it that did a partial job at getting Django projects running on App Engine. The Django-nonrel team's previous project app-engine-patch is still fairly popular even though they warn people that they've stopped working on it, and that people should use Django-nonrel instead. An even older project created by other developers, including some from Google, was called Google App Engine Helper for Django with an example of how to use it documented in Python Web Development with Django. Django-nonrel obsoletes both of these projects because they require you to use either custom data classes for your model or Google's datastore classes. You cannot use Django's model layer and are required to modify code if using either of them. In other words, please do not use the Helper nor the Patch any more!

With Django-nonrel, you can use Django data models as-is. This gives you the possibility to reuse already existing Django apps, often without the need to modify the app itself. One example is Django's Admin interface which just works out-of-the-box. Additionally, this enables the non-relational database communities to share and collaborate on non-relational Django apps resulting in a much bigger community than on their own.

In summary you should use Django-nonrel because of the significant disadvantages to the older approaches:

  • Requires significant application code modification
  • Must use specialized model classes or Google's (vendor lock-in)
  • Cannot use existing Django apps

Django-nonrel solves most of the problems with these older approaches.

Future work

At the time of this writing, members of the Django-nonrel team have started on a new project called django-dbindexer, a denormalization and indexing layer for Django-nonrel so that you can use JOINs on App Engine and other non-relational databases for example. So far, you can use filters like a case insensitive exact filter or a month filter which aren't supported on App Engine out of the box.

Currently Django-nonrel is standalone package, we're hoping at some point (perhaps Django version 1.4) to roll all of this work into Django proper so you will be able to use exactly the same code with the official Django distro in combination with an appropriate database backend, such as djangoappengine for App Engine apps. Regardless, if you are new to Django this should give you a good starting point. Once your app is in pure Django, you can probably guess (and be correct) that it can now benefit from the numerous other Django projects that are out there.

One example is the django-mediagenerator which automatically combines and compresses your JavaScript/CSS files when deploying your project or django-socialauth which allows your users to login via various providers like Gmail so you don't have to force your users through registration again. In case you wonder about file transfers to the Blobstore on App Engine: even this is possible using django-filetransfer. If you need a simple full-text search solution with stemming support you can use nonrel-search. django-dbindexer, the project the Django-nonrel team is most excited about, adds support for filters like endswith, contains or weekday filters, all which aren't supported natively on App Engine itself.

In the near future django-dbindexer will support JOINs and automatic denormalization. If this caught your curiosity you should subscribe to the Django-nonrel & webdev blog for the latest updates and tutorials about Django-nonrel and web development.

Summary

Google App Engine is a revolutionary cloud-hosting platform that allows you to run your apps on Google App Engine. It comes with a thin web framework called webapp. Users however, are not required to use webapp. They can use any WSGI-compatible web framework that interfaces to App Engine, as mentioned that the beginning of this article.

Django is one of the most popular and fully-featured web frameworks for App Engine, and parts of Django are made available for App Engine apps. Django has traditionally only worked on top of standard relational databases. Enter Django-nonrel, the fork of Django that allows it to run on top of non-relational databases such as MongoDB and App Engine's datastore.

In this article, we've shown you how you can take an existing webapp and port it to pure Django and have Django-nonrel run it in the cloud for you on Google App Engine. The most significant benefits to users are: a) you code "normal" Django using Django's data models, and b) it abolishes the notion of vendor lock-in for Django/Python-based apps so that you can move them between traditional hosting and Google App Engine with very little penalty. With Django-nonrel, users are able to choose between the power and scalability of App Engine and the control and flexibility of doing it yourself.

References

Authentication required

You need to be signed in with Google+ to do that.

Signing you in...

Google Developers needs your permission to do that.