Due to a home dev project I am currently involved in with another contributor, called byteface, it has lead me to using the Google AppEngine and specifically the Python flavour as opposed to the Java flavour.  I chose Python as I am enjoying further exposure to different programming languages.  The Django framework is nearly entirely supported on the Google AppEngine with some modules omitted due to either security considerations or conflict.  Both the DJango framework and the Google AppEngine have their own ORM, so this is one of the module omissions where you have to use the Google ORM, (wonder if they ever thought of doing GHibernate ;-)).

imageAnother point to make also is that you can choose to use the Google http handler functions or DJango’s.  I have gone with Django’s so I can get a complete experience of the framework and utilise all of the powerful utilities and features.

In this post I want to show how you can emulate the project structure of a default ASP.NET MVC 1.0 application, i.e. File –> New –> ASP.NET MVC Application inside Visual Studio.  The image on the right is an example of one freshly created, all I have done is remove any references to Account, as for the purposes of this example I just want to emulate the core things like structure and behaviour.

The following are considerations which I need to think about in order to emulate Microsoft’s ASP.NET implementation of MVC including:

  • Master page templates
    • i.e. Site.Master
  • Static content
  • Routing
  • Controller Actions
    • Including HTTP Verb restrictions
  • General layout

Off the top of my head one thing which will need to be an add in is the routing, as this is due to one of the rules/principles of python:

Explicit is better than implicit

So basically in the context of routing, we would have to define all of our routes as opposed to what we can do in ASP.NET MVC and have a route to match the {controller}/{action}/{id} url pattern.  From a little research I did, Ruby on Rails also has this out of the box.  So I am not sure if I will be able to manage this part of the project in this post, so when I find a robust way of doing it inside Django I will again post something about it.  It may be the case I stick with the explicit route and conform to the Zen of Python(>>> import this).

Google App Engine SDK Console

If you have not done already, you will need to download the SDK and create the application.  I am using the GUI for this, so download and create an application.  The folder structure should have been created for you.  In the case of this example I named my application narbley and in return in created the following folder structure inside my app engine source path:

  • narbley
    • narbley

So it created a folder with the same name inside this folder with the relevant application files for Google App Engine. Because I want to use the Django framework, I deleted the nested narbley folder as I will create this using the django-admin utility.

To create the base django app files I ran the following cmd from inside my application folder:

django-admin-py startproject narbley

What is narbley? Simply a code name for this project I am working on with a fellow contributor.  It is a codename for many reasons including, we are not 100% sure on what we are building yet, that and it is very difficult to find a name for a Google AppEngine … App which is not taken, so we tried to be abstract, and failing that we simply kept adding a letter to the end and trying it, hence narbley was created.

Also you will need to follow this short how to, to use the Django Framework completely over the Google one, http://code.google.com/appengine/articles/django.html

The Home Controller

From what I have seen, in Python and Django the views are actually a front controller.  I would not class the function which handles the request the view, in the context of DJango I would  regard the templates as the views, the controller as the front controller with the different actions on and the model, the model.

For the http verb restriction you find in the ASP.NET MVC 1.0 implementation, I did some searching about and actually came back to finding that one has already been written in DJango and its usage should very familiar to you.

from django.template import Context, loader
from django.http import HttpResponse
from django.views.decorators.http import require_http_methods
12
@require_http_methods(["GET"])
def index(request):
    t = loader.get_template('home/index.html')
    c = Context({
        'message': "Welcome to the Django Framework on the Google App Engine"
    })
    return HttpResponse(t.render(c))

@require_http_methods(["GET"])
def about(request):
    t = loader.get_template('home/about.html')
    c = Context({
        'message': "Narbley is the codename"
    })
    return HttpResponse(t.render(c))

Great so that is the index and the about page sorted.  One thing I will point out here is a point with regards to how python handles namespaces/file structure/packages or how ever you want to call the equivalent.  I have created a folder called controllers and inside I have a file called homecontroller.py.  I also need to add the file __init__.py .  Unfortunately as of this moment I cannot tell you why, only that I know it requires it, and subsequently I think for any other folder nesting.

Master/Content Page Templates

These are self explanatory and their implementation quite similar, with a simple change to the import and the syntax of course.  From my experience to date, the DJango templates require the .html extension whether they are content or master.  You need to add a reference to any folders which will be deemed as template folders inside the settings.py file, i.e.:

ROOT_PATH = os.path.dirname(__file__)
TEMPLATE_DIRS = (
    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
    # Always use forward slashes, even on Windows.
    # Don't forget to use absolute paths, not relative paths.
    ROOT_PATH + '/views',
)

Inline with the project files from a default ASP.NET MVC Project I have simply copied and changed the files to use the DJango templating syntax, notice how:

  • The content placeholders are defined
  • The master page is defined and referenced

The master page (views/shared/master.html)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>{% block title %}{% endblock %}</title>
    <link href="/content/site.css" rel="stylesheet" type="text/css" />
    {% block css_includes %}{% endblock %}
    {% block script_includes %}{% endblock %}
</head>

<body>
    <div class="page">

        <div id="header">
            <div id="title">
                <h1>My MVC Application</h1>
            </div>
            
            <div id="menucontainer">
            
                <ul id="menu">              
                    <li><a href="/home/">Home</a></li>
                    <li><a href="/home/about/">About</a></li>
                </ul>
            
            </div>
	<br style="clear:both"/>
        </div>

        <div id="main">
            {% block content %}{% endblock %}

            <div id="footer">
		{% block footer %}{% endblock %}
            </div>
        </div>
    </div>
</body>
</html>

The homepage (views/home/index.html)

I am only attaching the code for the homepage as the about page is very similar.

{% extends "shared/master.html" %}

{% block title %}DJango Example{% endblock %}
{% block css_includes %}{% endblock %}
{% block script_includes %}{% endblock %}


{% block content %}
<p>{{ message }}.</p>
{% endblock %}

{% block footer %}
Footer here
{% endblock %}

image Apart from that there is just the default application files required by Python/DJango and Google App Engine.  I am currently looking into:

  • Routing
  • Extension less Urls
  • JSON support libraries in Python
  • The Message Queue of the Google App Engine

The resulting project structure for the base project is shown on the right.  I opened the website inside Visual Studio to see the structure, not for editing lol.

I should have deleted it but I have not, the http_methods file is not used, I forgot to delete.

Cheers,

Andrew


Sunday, December 20, 2009 4:09:25 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer

Of late I am deving in parallel on a sample at home project for a few reasons including practicing skills both in architecture but also so I can get more exposure to new languages, with the most recent addition being PYTHON.  The Google AppEngine currently supports PYTHON and JAVA, and since I am already doing a couple of things in JAVA I thought I would try my hand at PYTHON and I have to say, “I like it.”

Before I got onto the AppEngine, an immediate accompaniment to programming in PYTHON for the web was the DJango framework.  When you start reading through even the first page of the starting tutorials, you realise how easy and fast it is to get up and running with a saleable application.  The Google app engine includes features of DJango, but not all of them.  One main example is the fact that Google has defined its own data store and ORM for use with the AppEngine.

So inside the web application, we would want to logically separate different handlers for different situations, and the example which I am including in this post is for a Forum.  Two handlers immediately spring to mind being a handler to List all the forums and a handler to view an individual forum by id.

  • /Forums
  • /Forum/1/somename-of-the-forum

A colleague in the place were I work, pointed out a really handy function called slugify inside the DJango framework which simply took and string and made a url friendly version i.e. “The Forum” because “the-forum.”  Unfortunately though, I think this is not included inside the Google AppEngine, so it will probably be best to create an implementation of a “Slug Field” for our objects, and then it will be easy to say something like “forum.slug().”  I have not et implemented this yet, just up to the url rewriting part :-)

Yep, these functions are included inside the Google AppEngine, I was stupid for thinking otherwise.  Check out the import and the implementation I used below on the last code snippet.

I have created a file called handlers.py but I think it will be better to separate the handlers by file and by topic area i.e. Forum, Thread, Post etc…  but for this example, the two handlers I create are inside this 1 file.  Also, my implementation of the handler is purely for example only, i.e. I am outputting a string so we can recognise the different behaviour in each handler.

ForumListHandler

class ForumListHandler(webapp.RequestHandler):
    def get(self):
        for item in Forum.all():
            self.response.out.write("%s<br>" %
                                    (item.title))

ForumViewHandler

class ForumViewHandler(webapp.RequestHandler):
    def get(self,id,name):
        self.response.out.write("you are are looking for forum %s with id of : %s" % (name,id))

If you notice in the second handler, the function has 3 parameters, which will become clear with the next code snippet how they are populated.  The first handler will simply output a list of the forums, and the second will output a string which includes the values it will extract from the url of the get request.

I am currently paid to develop ASP.NET MVC where this syntax of automatically mapping a request with an overloaded function is very familiar and well structured.  Inside the main.py file you have a block of code where you add your mappings to handlers.  The following is the result of adding the required handlers for this example:

def main():
  application = webapp.WSGIApplication([('/', MainHandler),
                                        ('/forums',ForumListHandler),
                                        ('/forum/(\d+)/(.*)',ForumViewHandler)],
                                       debug=True)
  wsgiref.handlers.CGIHandler().run(application)

Of course the url mapping uses regular expressions, and I think from looking at this, that it first checks that the number of matches it has made on the url, is equal to the number of parameters inside the handler.  If this is a match it will then supply each parameter in the order in which they are matched using the regular expression.  From the above you can see I first match a sequence of digits, so this fits in with my get function shown above with the first parameter after self being id.

UPDATE:

I changed my implementation of the url rewrite rules, and for the forum view I decided that I would make the parameter a slug, with an obvious rule that each slug is unique.  The reason I did this, is because the entity key you get with an object inside the Google app engine, is not really the cleanest thing to put in there as it contains the entity type and guid and things.  So below is the updated version and also an updated handler for the forum list handler so we can output the forums and follow the links.  I will use the datastore admin tool to add some forums in.  Oh, just to mention, when a new model is saved to the datastore, you automatically get a CRUD interface for it.  A rough equivalence of this on Microsoft technology I would say is dynamic data, makes me wonder if they got inspiration from DJango for that.

def main():
  application = webapp.WSGIApplication([('/', MainHandler),
                                        ('/forums',ForumListHandler),
                                        ('/forum/(.*)',ForumViewHandler)],
                                       debug=True)
  wsgiref.handlers.CGIHandler().run(application)

And the updated handlers, which now outputs the link, and the other which simply accepts the slug:

from google.appengine.ext import webapp
from models import Forum
from django.template import defaultfilters

class ForumViewHandler(webapp.RequestHandler):
    def get(self,slug):
        self.response.out.write("you are are looking for forum with slug of : %s" % (slug))

class ForumListHandler(webapp.RequestHandler):
    def get(self):
        for item in Forum.all():
            self.response.out.write("<a href=\"/forum/%s\">%s</a><br>" %
                                    (defaultfilters.slugify(item.title), item.title))

image

image

Cheers,

Andrew


Sunday, November 29, 2009 11:38:28 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer