Gae Django

gae
django

How to create a model?

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:

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)

How to test a model using the pyton shell?

You can play with the models in a nice Python shell by running manage.py shell. Here's an example using Django style queries:

manage.py shell
>>> 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!'

How to port request handler to django view?

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

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.

How to query the back-end datastore?

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]

What choices do we have for authentication?

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

  • no authentication
  • Google Accounts (Users service)
  • Google Apps Domain
  • Federated Login via OpenID (Experimental) (starting in SDK 1.3.4)

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

  • no authentication
  • Django authentication
  • Federated Login (see 3rd-party django-socialauth package)

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 %}
      .
      .
      .
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License