Create a simple Guestbook Application

In this blog post, we would create a simple Guestbook application based on the set up configuration of Django on Google App Engine that we have done previously (part 1,part 2)

If you have gone through the above, your project would have the simple structure like this:

This Guestbook application is based on the Google App Engine for Python 2.7 Tutorial. Apart from that, many references also come from the Official Django Tutorial and the Django Book. I found the materials presented there very concise and easy to understand and I highly recommend them.

To create our application, we would proceed with the following steps:

  1. Create the application folder
  2. Defining the models
  3. Defining the views
  4. Defining the templates
  5. Defining the static file folder
  6. Defining the urls

Create the Application Folder

There are 3 locations our application can be put in the project:

  1. In the root project folder (together with main.py and manage.py and at the same level with Project_Name folder)
  2. In the Project_Name folder
  3. In a dedicated folder for all applications called "apps"

In the 1st and 3rd approach, the application can be referenced like:

from guestbook.models import *

In contrast, in the 2nd approach, the application needs to be referenced like:

from Project_Name.guestbook.models import *

Besides, for the 3rd approach to work, the folder "apps" needs to be added to the system path. Instruction to do so can be found here

So depending on your preference, you can choose the location for your application folder accordingly. In this blog post, we would use the 1st approach, which is also used in the Official Django Tutorial:

We proceed to create a package in the root project folder named guestbook. A folder with an __init__.py is created as a result.


Defining the MODELS

We would use Google App Engine datastore model to create the models for our guestbook application. We proceed to create a PyDev module models (which is just a file models.py) in the guestbook package and define the following:

from google.appengine.ext import db

class Greeting(db.Model):
    """Models an individual Guestbook entry with an author, content, and date."""
    author = db.StringProperty()
    content = db.StringProperty(multiline=True)
    date = db.DateTimeProperty(auto_now_add=True)
    
    @classmethod
    def get_key_from_name(cls, guestbook_name=None):
        return db.Key.from_path('Guestbook', guestbook_name or 'default_guestbook')

Defining the VIEWS

We would define the views just like we do for any Django application, with some additional Google App Engine operations to manage the data models. We proceed to create a PyDev module views (which is just a file views.py) in the guestbook package and define the following:

from django.views.generic.simple import direct_to_template
from django.http import HttpResponseRedirect

from google.appengine.api import users

from guestbook.models import Greeting

import urllib

def main_page(request):
    guestbook_name = request.GET.get('guestbook_name', 'default_guestbook')
    guestbook_key = Greeting.get_key_from_name(guestbook_name)
    greetings_query = Greeting.all().ancestor(guestbook_key).order('-date')
    greetings = greetings_query.fetch(10)
    if users.get_current_user():
        url = users.create_logout_url(request.get_full_path())
        url_linktext = 'Logout'
    else:
        url = users.create_login_url(request.get_full_path())
        url_linktext = 'Login'
    template_values = {
        'greetings': greetings,
        'guestbook_name': guestbook_name,
        'url': url,
        'url_linktext': url_linktext,
    }
    return direct_to_template(request, 'guestbook/main_page.html', template_values)

def sign_post(request):
    if request.method == 'POST':
        guestbook_name = request.POST.get('guestbook_name')
        guestbook_key = Greeting.get_key_from_name(guestbook_name)
        greeting = Greeting(parent=guestbook_key)
    
        if users.get_current_user():
            greeting.author = users.get_current_user().nickname()
    
        greeting.content = request.POST.get('content')
        greeting.put()
        return HttpResponseRedirect('/?' + urllib.urlencode({'guestbook_name': guestbook_name}))
    return HttpResponseRedirect('/')

Defining the TEMPLATES

In the main_page view above, we use the HTML template to render the content of the page. We need to configure the TEMPLATE_DIRS in the settings.py file in the Project_Name package:

import os
ROOT_PATH = os.path.dirname(__file__)
TEMPLATE_DIRS = (os.path.join(ROOT_PATH, "templates"),)

This will let Django look into all "templates" folders in the all applications to find the templates to render. Do take note of the comma near the end, it is important that it’s there.

We would also need to add “guestbook” into the list of INSTALLED_APPS:

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    
    'guestbook',
)

After that, in the guestbook package, we would create a folder called "templates" and a subfolder called "guestbook" nested under it. This is to differentiate the template folders of different applications. Then, we would create the files main_page.html in the subfolder guestbook:

<html>
  <head>
    <link type="text/css" rel="stylesheet" href="/static/css/main.css" />
  </head>
  <body>
    {% for greeting in greetings %}
      {% if greeting.author %}
        <b>{{ greeting.author }}</b> wrote:
      {% else %}
        An anonymous person wrote:
      {% endif %}
      <blockquote>{{ greeting.content|escape }}</blockquote>
    {% endfor %}

    <form action="/sign/" method="post">
      {% csrf_token %}
      <input type="hidden" name="guestbook_name" value="{{ guestbook_name }}" />
      <div><textarea name="content" rows="3" cols="60"></textarea></div>
      <div><input type="submit" value="Sign Guestbook"></div>
    </form>

    <a href="{{ url }}">{{ url_linktext }}</a>

  </body>
</html>

Defining the Static File Folder

In the template above, we serve a static stylesheet file called main.css. To do this, we would creat a folder called "static" in the root project folder. We would instruct Google App Engine to serve all files in this folder statically by adding a static_dir handler in the app.yaml file:

handlers:
  
- url: /static
  static_dir: static
  
- url: /.*
  script: main.application

Then, we would create the "css" folder and the corresponding main.css file in this static folder.

body {
  font-family: Verdana, Helvetica, sans-serif;
  background-color: #DDDDDD;
}

Defining the URLs

We would define 2 URLs to serve 2 views main_page and sign_post accordingly. We first modify the urls.py file in the Project_Name package to direct all requests to guestbook application:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^', include('guestbook.urls')),
)

We would then create another PyDev module urls (which is just a file urls.py) in the guestbook package to process all requests related to guestbook application:

from django.conf.urls.defaults import *
from guestbook.views import main_page, sign_post

urlpatterns = patterns('',
    (r'^sign/$', sign_post),
    (r'^$', main_page),
)

Our application has been completed! The final folder structure would look like below:


You can proceed to run and test the application. Please note that when we run our application, a file named index.yaml would be automatically created in the root project folder. This is the index file of Google App Engine.