<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Thomas Brox R&#248;st &#187; Python</title>
	<atom:link href="http://thomas.broxrost.com/category/python/feed/" rel="self" type="application/rss+xml" />
	<link>http://thomas.broxrost.com</link>
	<description></description>
	<lastBuildDate>Tue, 04 May 2010 13:55:12 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>How to compile SimpleParse 2.1.0a1 for Python 2.6 on Windows Vista</title>
		<link>http://thomas.broxrost.com/2009/06/01/how-to-compile-simpleparse-210a1-for-python-26-on-windows-vista/#utm_source=feed&amp;utm_medium=feed&amp;utm_campaign=feed</link>
		<comments>http://thomas.broxrost.com/2009/06/01/how-to-compile-simpleparse-210a1-for-python-26-on-windows-vista/#comments</comments>
		<pubDate>Mon, 01 Jun 2009 12:20:36 +0000</pubDate>
		<dc:creator>Thomas Brox Røst</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[SimpleParse]]></category>
		<category><![CDATA[cygwin]]></category>
		<category><![CDATA[parsing]]></category>
		<category><![CDATA[windows]]></category>

		<guid isPermaLink="false">http://thomas.broxrost.com/?p=235</guid>
		<description><![CDATA[SimpleParse is a fast Python single-pass parser generator that I use regularly. When I finally made the move onto Python 2.6 it turned out that there is no pre-compiled package for 2.6 on Windows. So, here is my procedure for compiling the source package on Windows Vista. 1. Install Cygwin if you don&#8217;t already have [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://simpleparse.sourceforge.net/">SimpleParse</a> is a fast Python single-pass parser generator that I use regularly. When I finally made the move onto <a href="http://www.python.org/download/releases/2.6/">Python 2.6</a> it turned out that there is no pre-compiled package for 2.6 on Windows. So, here is my procedure for compiling the source package on Windows Vista.</p>
<p>1. Install <a href="http://www.cygwin.com/">Cygwin</a> if you don&#8217;t already have it on your system, and make sure that the version of Python you are installing SimpleParse for is on either the system or the Cygwin path.</p>
<p>2. Download and install <a href="http://www.microsoft.com/express/download/">Microsoft Visual C++ 2008 Express Edition</a>. You should ensure that you have the latest Vista service packs installed before attempting this. If the installer quits on you then just reboot the computer and try again. Without this installed, you wil get an &#8216;Unable to find vcvarsall.bat&#8217; error.</p>
<p>3. Download and unpack the <a href="http://sourceforge.net/project/showfiles.php?group_id=55673&#038;package_id=50682&#038;release_id=394687">SimpleParse 2.1.0a1 source</a>. Using the Cygwin shell, place yourself in the root source directory.</p>
<p>4. If we try to run <code>python setup.py install</code> at this point, the Visual C++ compiler will complain:</p>
<pre class="brush: python">
stt/TextTools/mxTextTools/mxTextTools.c(149) : error C2133:
&#039;mxTextSearch_Methods&#039; : unknown size
stt/TextTools/mxTextTools/mxTextTools.c(920) : error C2133:
&#039;mxCharSet_Methods&#039;: unknown size
stt/TextTools/mxTextTools/mxTextTools.c(2103) : error C2133:
&#039;mxTagTable_Methods&#039; : unknown size
error: command &#039;&amp;amp;amp;quot;C:\Program Files\Microsoft Visual Studio 9.0\VC\BIN\cl.exe&amp;amp;amp;quot;&#039;
failed with exit status 2
</pre>
<p>We have to add the following lines to <code>stt/TextTools/mxTextTools/mxTextTools.c</code>, starting at line 148 (before <code>staticforward</code> is used for the first time):</p>
<pre class="brush: cpp">
#ifdef _MSC_VER
#define staticforward extern
#endif
</pre>
<p>5. <code>with</code> is a Python 2.6 keyword, meaning it can&#8217;t be used as a variable, as is the case in the SimpleParse source code. So, we have to replace it with something else:</p>
<pre class="brush: python">
$ sed -r &#039;s/with/with_t/g&#039; &amp;amp;amp;lt; stt/TextTools/TextTools.py &amp;amp;amp;gt; tmp.txt
$ cp tmp.txt stt/TextTools/TextTools.py
</pre>
<p>6. Finally, run <code>python setup.py install</code> as usual.</p>
<div id="wherego_related"><h3>Readers who viewed this page, also viewed:</h3><ul><li><a href="http://thomas.broxrost.com/2009/05/27/on-the-sadness-of-nouns/" rel="bookmark" class="wherego_title">On the sadness of nouns</a></li><li><a href="http://thomas.broxrost.com/2009/03/12/why-amazon-web-services-just-became-a-competitive-web-hosting-provider/" rel="bookmark" class="wherego_title">Why Amazon Web Services just became a competitive web hosting provider</a></li><li><a href="http://thomas.broxrost.com/2008/08/21/persistent-django-on-amazon-ec2-and-ebs-the-easy-way/" rel="bookmark" class="wherego_title">Persistent Django on Amazon EC2 and EBS &#8211; The easy way</a></li><li><a href="http://thomas.broxrost.com/2008/04/08/django-on-google-app-engine/" rel="bookmark" class="wherego_title">Django on Google App Engine in 13 simple steps</a></li><li>Powered by <a href="http://ajaydsouza.com/wordpress/plugins/where-did-they-go-from-here/">Where did they go from here?</a></li></ul></div>]]></content:encoded>
			<wfw:commentRss>http://thomas.broxrost.com/2009/06/01/how-to-compile-simpleparse-210a1-for-python-26-on-windows-vista/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Running pytst 1.15 on a 64-bit platform</title>
		<link>http://thomas.broxrost.com/2009/01/25/running-pytst-115-on-a-64-bit-platform/#utm_source=feed&amp;utm_medium=feed&amp;utm_campaign=feed</link>
		<comments>http://thomas.broxrost.com/2009/01/25/running-pytst-115-on-a-64-bit-platform/#comments</comments>
		<pubDate>Sat, 24 Jan 2009 23:49:43 +0000</pubDate>
		<dc:creator>Thomas Brox Røst</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[pytst]]></category>

		<guid isPermaLink="false">http://thomas.broxrost.com/?p=130</guid>
		<description><![CDATA[Update: The latest version, 1.17, compiles on 64-bit platforms out of the box, so the patch below is no longer necessary. Nicolas Lehuen&#8217;s pytst is a C++ ternary search tree implementation with a Python interface. It&#8217;s an excellent tool&#8212;and it is also really, really fast. Unfortunately version 1.15 doesn&#8217;t compile on 64-bit platforms, giving the [...]]]></description>
			<content:encoded><![CDATA[<p><b>Update: The latest version, 1.17, compiles on 64-bit platforms out of the box, so the patch below is no longer necessary.</b></p>
<p>Nicolas Lehuen&#8217;s <a href="http://nicolas.lehuen.com/download/pytst/">pytst</a> is a C++ ternary search tree implementation with a Python interface. It&#8217;s an excellent tool&mdash;and it is also really, really fast.</p>
<p>Unfortunately version 1.15 doesn&#8217;t compile on 64-bit platforms, giving the following error messages:</p>
<pre class="brush: bash">
pythonTST.h:178: error: cannot convert &#039;int*&#039; to &#039;Py_ssize_t*&#039; for argument &#039;3&#039;
to &#039;int PyString_AsStringAndSize(PyObject*, char**, Py_ssize_t*)&#039;
tst_wrap.cxx: In function &#039;PyObject* _wrap__TST_walk__SWIG_1(PyObject*, int, PyO
bject**)&#039;:
tst_wrap.cxx:3175: error: cannot convert &#039;int*&#039; to &#039;Py_ssize_t*&#039; for argument &#039;3
&#039; to &#039;int PyString_AsStringAndSize(PyObject*, char**, Py_ssize_t*)&#039;
tst_wrap.cxx: In function &#039;PyObject* _wrap__TST_close_match(PyObject*, PyObject*
)&#039;:
tst_wrap.cxx:3250: error: cannot convert &#039;int*&#039; to &#039;Py_ssize_t*&#039; for argument &#039;3
&#039; to &#039;int PyString_AsStringAndSize(PyObject*, char**, Py_ssize_t*)&#039;
tst_wrap.cxx: In function &#039;PyObject* _wrap__TST_prefix_match(PyObject*, PyObject
*)&#039;:
[...and so on...]
</pre>
<p>Until Nicolas releases an updated version, here is the quick fix:</p>
<pre class="brush: python">
cp pythonTST.h pythonTST.h.orig
cp tst_wrap.cxx tst_wrap.cxx.orig
sed -r &#039;s/int size/Py_ssize_t size/&#039; &lt; tst_wrap.cxx.orig &gt; tst_wrap.cxx
sed -r &#039;s/int length/Py_ssize_t length/&#039; &lt; pythonTST.h.orig &gt; tmpfile
sed -r &#039;s/sizeof\(int\)/sizeof(long)/&#039; &lt; tmpfile &gt; pythonTST.h
</pre>
<p>Run these commands from the pytst source directory and you should be all set. I&#8217;m not sure if this a fully satisfactory solution, but at least this will get the test suite running again.</p>
<div id="wherego_related"><h3>Readers who viewed this page, also viewed:</h3><ul><li><a href="http://thomas.broxrost.com/2009/06/01/how-to-compile-simpleparse-210a1-for-python-26-on-windows-vista/" rel="bookmark" class="wherego_title">How to compile SimpleParse 2.1.0a1 for Python 2.6 on Windows Vista</a></li><li>Powered by <a href="http://ajaydsouza.com/wordpress/plugins/where-did-they-go-from-here/">Where did they go from here?</a></li></ul></div>]]></content:encoded>
			<wfw:commentRss>http://thomas.broxrost.com/2009/01/25/running-pytst-115-on-a-64-bit-platform/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Hacking comments in Django 1.0</title>
		<link>http://thomas.broxrost.com/2008/09/05/hacking-comments-in-django-10/#utm_source=feed&amp;utm_medium=feed&amp;utm_campaign=feed</link>
		<comments>http://thomas.broxrost.com/2008/09/05/hacking-comments-in-django-10/#comments</comments>
		<pubDate>Fri, 05 Sep 2008 11:59:02 +0000</pubDate>
		<dc:creator>Thomas Brox Røst</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://thomas.broxrost.com/?p=60</guid>
		<description><![CDATA[The recent release of Django 1.0 included a full rewrite of the comments framework. Comments have been available in Django for a while but were never properly documented until now. This article will show you how to adapt and extend the comments framework so that it fits the needs of your application. Why extend it? [...]]]></description>
			<content:encoded><![CDATA[<p>The recent release of <a href="http://www.djangoproject.com/weblog/2008/sep/03/1/" target="_blank">Django 1.0</a> included a full rewrite of the <a href="http://docs.djangoproject.com/en/dev/ref/contrib/comments/#ref-contrib-comments-index" target="_blank">comments framework</a>. Comments have been available in Django for a while but were never properly documented until now.</p>
<p>This article will show you how to adapt and extend the comments framework so that it fits the needs of your application. Why extend it? Well, mainly because the framework does what it says on the box&mdash;and nothing more. It allows you to attach comments to any Django object instance but for the rest of the business logic&mdash;e.g. regulating who can modify and delete comments&mdash;you are on your own.</p>
<p>Also, the current documentation does not cover all features so what I am writing here should hopefully fill a few gaps.</p>
<p><b>Prerequisites</b></p>
<p>You need to be familiar with Django. If you&#8217;re not, then have a look at the <a href="http://docs.djangoproject.com/en/dev/" target="_blank">tutorial</a> in the official documentation or alternatively at my <a href="http://thomas.broxrost.com/2008/04/08/django-on-google-app-engine/#utm_source=feed&amp;utm_medium=feed&amp;utm_campaign=feed" target="_blank">previous article</a> on how to get started with Django on Google App Engine.</p>
<p><b>How comments work</b></p>
<p>It&#8217;s really simple&mdash;just skim through <a href="http://docs.djangoproject.com/en/dev/ref/contrib/comments/" target="_blank">the well-written documentation</a> and you should pretty much be able to figure it out.</p>
<p>For example, to show a comment form for an instance of a model called <code>my_model_instance</code>, you just need two lines of template code:</p>
<pre class="brush: html">
{% load comments %}
{% render_comment_form for my_model_instance %}
</pre>
<p>The magic behind the comments framework lies in its use of <a href="http://www.djangoproject.com/documentation/models/generic_relations/" target="_blank">generic model relations</a>. This is a very powerful (and well-hidden) Django feature that allows your models to have generic foreign keys, meaning they can link to any other model. The comments framework uses this technique to ensure that comments can be attached to an arbitrary model in your application.</p>
<p><b>The scenario</b></p>
<p>I will be describing a real-life case from my company web site, <a href="http://eventseer.net" target="_blank">Eventseer.net</a>. Eventseer is an event tracker that helps researchers stay informed on upcoming conferences and workshops. It uses the comments framework for two different purposes.</p>
<p>Firstly, registered users can add comments to each event in our database. Secondly, all users can claim a personal profile page where they get what we call a whiteboard&mdash;which is simply a blogging application. Each entry on a whiteboard can be commented on by other registered users.</p>
<p><b>The problem</b></p>
<p>There are some limitations when it comes to adding comments on Eventseer. For example, only registered users are allowed to add comments. After a comment has been added, only the user who added it or an administrator are allowed to delete it.</p>
<p>These are fairly typical requirements&mdash;which are not supported out of the box in the comments framework. There is some support for using the built-in permissions system, but this will still not let you exercise fine-grained per user access control.</p>
<p>Moreover, the default comment templates are ugly as sin and will have to adapted to fit your application.</p>
<p><b>Step 1: Enabling comments</b></p>
<p>This is described well enough in the standard documentation. However, if we want to add extra functionality there are a couple of extra things to be done.</p>
<p>First, we add the comments framework to <code>INSTALLED_APPS</code> in <code>settings.py</code>:</p>
<pre class="brush: python">
# eventseer/settings.py

INSTALLED_APPS = (
    ...
    &#039;django.contrib.comments&#039;,
    &#039;eventseer.mod_comments&#039;,
    ...
)
</pre>
<p>Note that I also added an app called <code>eventseer.mod_comments</code>. This is where our comments wrapper code will reside. (I will be using the <code>eventseer</code> project name for the rest of this tutorial).</p>
<p>Now synchronize the database:</p>
<pre class="brush: bash">
$ python manage.py syncdb
</pre>
<p>This creates the tables necessary for storing the comments.</p>
<p>Finally, add an entry in your base <code>urls.py</code>:</p>
<pre class="brush: python">
# eventseer/urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns(&#039;&#039;,
    ...
    (r&#039;^comments/&#039;, include(&#039;eventseer.mod_comments.urls&#039;)),
)
</pre>
<p>This is where we deviate from the standard documentation: Instead of routing all comment URLs to the bundled comments application we instead route them to our own custom application. This allows us to intercept comment URLs as required.</p>
<p><b>Step 2: Add the modified comments application</b></p>
<p>This is done the usual way:</p>
<pre class="brush: bash">
$ python manage.py startapp mod_comments
</pre>
<p>In the previous step we added a reference to <code>urls.py</code> in the <code>mod_comments</code> application, so this file must be added:</p>
<pre class="brush: python">
# eventseer/mod_comments/urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns(&#039;&#039;,
    (r&#039;^delete/(?P&lt;comment_id&gt;\d+)/$&#039;, &#039;eventseer.mod_comments.views.delete&#039;),
    (r&#039;&#039;, include(&#039;django.contrib.comments.urls&#039;)),
)
</pre>
<p>The first line routes requests to <code>/comments/delete/</code> to a custom delete view which we will create in the next step. For this example this is the only behavior we wish to modify. The last line ensures that all other requests are passed through to <code>django.contrib.comments.urls</code>.</p>
<p><b>Step 3: Create the wrapper view</b></p>
<p>We want to make sure that only the user who wrote a comment or administrators are allowed to delete it. This can be taken care of in <code>mod_comments/views.py</code>:</p>
<pre class="brush: python">
# eventseer/mod_comments/views.py

from django.contrib.auth.decorators import login_required
from django.contrib.comments.models import Comment
from django.http import Http404
from django.shortcuts import get_object_or_404
import django.contrib.comments.views.moderation as moderation

@login_required
def delete(request, comment_id):
    comment = get_object_or_404(Comment, pk=comment_id)
    if request.user == comment.user or \
       request.user.is_staff:
        return moderation.delete(request, comment_id)
    else:
        raise Http404
</pre>
<p>First we wrap the delete function with the <code>login_required</code> decorator so as to keep out non-authenticated users. We then check if the user who made the delete request actually owns the comment or if the user has administrator permissions. If either case holds true we pass the request on to the original <code>delete</code> method. Otherwise a 404 (page not found) error is raised.</p>
<p>We can of course modify the view method signature as required. In fact, the original delete method can be completely bypassed if that is what we want.</p>
<p><b>Step 4: Modifying delete behavior</b></p>
<p>By default the delete view shows a confirmation page (<code>comments/delete.html</code>) on GET requests and does the actual deletion on POST requests. After the deletion is done you will be shown the standard <code>deleted.html</code> template. Alternatively, adding a <code>next</code> parameter to the POST request will send the user to the given URL.</p>
<p>Say we wish to make some changes to the confirmation page, <code>comments/delete.html</code>. Instead of modifying the original in the Django distribution we create our own version. Create the directory <code>eventseer/mod_comments/templates/comments</code> and copy <code>delete.html</code> into it.</p>
<p>You will typically find this file in <code>/usr/lib/python2.5/site-packages/django/contrib/comments/templates/comments</code> on Linux systems or <code>C:/Python2.5/Lib/site-packages/django/contrib/comments/templates/comments</code> on Windows systems&mdash;your mileage may vary.</p>
<p>Typically you will wish to change this template to fit in with your site design, for instance by inheriting from your base templates.</p>
<p>To make the modified template take precedence, just add the new directory to <code>settings.py</code>:</p>
<pre class="brush: python">
# eventseer/settings.py

TEMPLATE_DIRS = (
    ...
    &#039;/home/eventseer/src/eventseer/mod_comments/templates&#039;,
    ...
)
</pre>
<p>This will make sure that the Django URL resolver queries the <code>eventseer/mod_comment/templates</code> directory&mdash;where it will find our alternative version of <code>comments/delete.html</code>. Requests to other comment views that use the other default templates will be passed through to the correct default location.</p>
<p><b>Conclusion</b></p>
<p>The Django comments framework is the easiest and quickest way to add commenting functionality to your application. The flip side of this simplicity is that you will often have to extend the framework to make it behave according to your requirements. As this tutorial have shown, this can be done without making changes to the comments framework itself. One of the core strengths of Django is how it provides a set of reusable building blocks upon which you can add your own advanced functionality as required.</p>
<p>At the time of writing, the comments framework documentation is somewhat sparse. If you want to learn more about the inner workings of Django comments you will have to consult <a href="http://code.djangoproject.com/browser/django/trunk/django/contrib/comments" target="_blank">the source code</a>&mdash;there are quite a few undocumented features that are really useful.</p>
<p><b>UPDATE</b></p>
<p>Tim Hoelscher <a href="#comment-7357#utm_source=feed&amp;utm_medium=feed&amp;utm_campaign=feed">noticed that</a> I hadn&#8217;t said anything about how to work around the Django permission system, which was an unintentional omission.</p>
<p>The original <code>delete</code> method in <code>django.contrib.comments.views.moderation</code> requires that the user who wants to delete a comment has the <code>comments.can_moderate</code> permission. Regular users do not have this permission by default, so we have to set it for all users who are allowed to delete comments. (Remember, the wrapper <code>delete</code> makes sure that they can only delete their own comments.)</p>
<p>An easy way to solve this is to create a &#8216;user&#8217; group, assign the <code>comments.can_moderate</code> permission to this group, and finally assign all users to this group. This can be done through the admin interface, with a few lines of SQL, or within your Django application. Refer to the <a href="http://docs.djangoproject.com/en/dev/topics/auth/#permissions">Django permissions</a> documentation for more information on how permissions work.</p>
<div id="wherego_related"><h3>Readers who viewed this page, also viewed:</h3><ul><li><a href="http://thomas.broxrost.com/2009/04/06/when-endnote-x2-fails/" rel="bookmark" class="wherego_title">When EndNote X2 fails</a></li><li><a href="http://thomas.broxrost.com/2008/04/08/django-on-google-app-engine/" rel="bookmark" class="wherego_title">Django on Google App Engine in 13 simple steps</a></li><li>Powered by <a href="http://ajaydsouza.com/wordpress/plugins/where-did-they-go-from-here/">Where did they go from here?</a></li></ul></div>]]></content:encoded>
			<wfw:commentRss>http://thomas.broxrost.com/2008/09/05/hacking-comments-in-django-10/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Serving static files with Django and AWS &#8211; going fast on a budget</title>
		<link>http://thomas.broxrost.com/2008/08/14/serving-static-files-with-django-and-aws-going-fast-on-a-budget/#utm_source=feed&amp;utm_medium=feed&amp;utm_campaign=feed</link>
		<comments>http://thomas.broxrost.com/2008/08/14/serving-static-files-with-django-and-aws-going-fast-on-a-budget/#comments</comments>
		<pubDate>Thu, 14 Aug 2008 19:47:19 +0000</pubDate>
		<dc:creator>Thomas Brox Røst</dc:creator>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://thomas.broxrost.com/?p=39</guid>
		<description><![CDATA[I just posted an article on how to improve Django response times through the use of pre-generated static files: Speed matters. When Google tried adding 20 extra results to their search pages, traffic dropped by 20%. The reason? Page generation took an extra .5 seconds. This article will show how Eventseer utilizes an often overlooked [...]]]></description>
			<content:encoded><![CDATA[<p>I just posted an article on how to improve Django response times through the use of pre-generated static files:</p>
<blockquote><p>Speed matters.</p>
<p>When Google tried adding 20 extra results to their search pages, <a href="http://eventseer.net/external/?url=http://glinden.blogspot.com/2006/11/marissa-mayer-at-web-20.html" target="_blank">traffic dropped by 20%</a>. The reason? Page generation took an extra .5 seconds.</p>
<p>This article will show how Eventseer utilizes an often overlooked way of improving the responsiveness of a web application: Pre-generating and serving static files instead of dynamic pages.</p></blockquote>
<p>The full posting can be read <a href="http://eventseer.net/p/thomas_brox_roest/whiteboardentry/13/">here</a>.</p>
<div id="wherego_related"><h3>Readers who viewed this page, also viewed:</h3><ul><li><a href="http://thomas.broxrost.com/2010/02/02/aws-cuts-data-transfer-rates-pricing-comparison-update/" rel="bookmark" class="wherego_title">AWS cuts data transfer rates: Pricing comparison update</a></li><li><a href="http://thomas.broxrost.com/2009/03/12/why-amazon-web-services-just-became-a-competitive-web-hosting-provider/" rel="bookmark" class="wherego_title">Why Amazon Web Services just became a competitive web hosting provider</a></li><li><a href="http://thomas.broxrost.com/2009/08/28/updated-awsgo-daddy-dedicated-server-cost-comparison/" rel="bookmark" class="wherego_title">Updated AWS/Go Daddy dedicated server cost comparison</a></li><li><a href="http://thomas.broxrost.com/2008/04/08/django-on-google-app-engine/" rel="bookmark" class="wherego_title">Django on Google App Engine in 13 simple steps</a></li><li>Powered by <a href="http://ajaydsouza.com/wordpress/plugins/where-did-they-go-from-here/">Where did they go from here?</a></li></ul></div>]]></content:encoded>
			<wfw:commentRss>http://thomas.broxrost.com/2008/08/14/serving-static-files-with-django-and-aws-going-fast-on-a-budget/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Porting legacy databases to Google App Engine</title>
		<link>http://thomas.broxrost.com/2008/06/15/porting-legacy-databases-to-google-app-engine/#utm_source=feed&amp;utm_medium=feed&amp;utm_campaign=feed</link>
		<comments>http://thomas.broxrost.com/2008/06/15/porting-legacy-databases-to-google-app-engine/#comments</comments>
		<pubDate>Sun, 15 Jun 2008 16:27:10 +0000</pubDate>
		<dc:creator>Thomas Brox Røst</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://thomas.broxrost.com/?p=23</guid>
		<description><![CDATA[A reader posed the following question: &#8220;I&#8217;m trying to convert my django app to work with google app engine. This is preferred rather than spending $100/year extra for a site with ssh access, plus I love the appengine dashboard. Here is my issue: My current django app is fairly static. It pulls all its data [...]]]></description>
			<content:encoded><![CDATA[<p> A reader posed the following question:</p>
<blockquote><p>&#8220;I&#8217;m trying to convert my django app to work with google app engine.  This is preferred rather than spending $100/year extra for a site with ssh access, plus I love the appengine dashboard.</p>
<p>Here is my issue: My current django app is fairly static.  It pulls all its data from a mysql database containing ~6,000 rows.  This itself is built from a gadfly database, so it should be pretty easy to get these values into the datastore/gql. </p>
<p>How can I sync my database with appengine?&#8221;</p></blockquote>
<p>This is a highly relevant problem if you are porting an existing Django application to the Google App Engine. Luckily, the App Engine SDK includes a <a href="http://code.google.com/appengine/articles/bulkload.html" target="_blank">bulk data uploader tool</a> that does the job. Let&#8217;s work through an example where we use this tool to transfer data from an existing MySQL database onto a Django application running on Google App Engine.</p>
<p><strong>Case description:</strong> We have an inventory database that is currently stored in MySQL. This database is to be made available through a Django web application that allows visitors to review the inventory. The database is named &#8216;customerdb&#8217; and has a single table called &#8216;inventory&#8217;:&#8217;</p>
<pre class="brush: python">
mysql&gt; select * from inventory;
+----------+----------+
| name     | quantity |
+----------+----------+
| ham      |        2 |
| cheese   |        7 |
| macaroni |        1 |
+----------+----------+
3 rows in set (0.00 sec)
</pre>
<p><strong>Setup:</strong> We need an App Engine-ready Django application that provides us with the views and models we need to display our inventory. For this scenario we will name the application &#8216;upload-demo&#8217; and make it available on <a href="http://upload-demo.appspot.com">http://upload-demo.appspot.com</a>. My <a href="http://thomas.broxrost.com/2008/04/08/django-on-google-app-engine/#utm_source=feed&amp;utm_medium=feed&amp;utm_campaign=feed">earlier</a> <a href="http://thomas.broxrost.com/2008/04/20/django-on-google-app-engine-templates-and-static-files/#utm_source=feed&amp;utm_medium=feed&amp;utm_campaign=feed">tutorials</a> should provide you with what you need to build the basic application structure.</p>
<p>The full set of application files can be downloaded <a href='http://thomas.broxrost.com/wp-content/uploads/2008/06/upload-demo.zip'>here</a>. References to the application name and paths will have to be changed according to your system setup.</p>
<p>Once the fundamentals are in place you should add an inventory model that mirrors the table in our database:</p>
<pre class="brush: python">
# upload-demo/uploaddemo/main/models.py

from google.appengine.ext import db

class Inventory(db.Model):
    name = db.StringProperty()
    quantity = db.IntegerProperty()
</pre>
<p>We also need a view that displays the data:</p>
<pre class="brush: python">
# upload-demo/uploaddemo/main/views.py

from django.http import HttpResponse
from uploaddemo.main.models import Inventory

def main(request):
    result = &quot;&quot;
    items = Inventory.all()

    for item in items:
        result += &quot;%s: %i&lt;br/&gt;&quot; % (item.name, item.quantity)

    return HttpResponse(result)
</pre>
<p>Finally, your <em>urls.py</em> should point towards the view:</p>
<pre class="brush: python">
# upload-demo/uploaddemo/urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns(&quot;&quot;,
    (r&quot;^$&quot;, &quot;uploaddemo.main.views.main&quot;),
)
</pre>
<p>The application directory structure should look exactly like this:</p>
<p><img src="http://thomas.broxrost.com/wp-content/uploads/2008/06/directory_structure.jpg" alt="Project directory structure" title="directory_structure" width="304" height="218" class="alignnone size-full wp-image-20" /></p>
<p>To verify that we are good to go, deploy the application to App Engine:</p>
<pre class="brush: bash">
[test@mybox ~]$ appcfg.py update upload-demo
</pre>
<p>You should see an empty page&mdash;which makes sense since we have no data yet.</p>
<p><strong>Step 1 &#8211; Create a bulk load handler:</strong> The bulk loader accepts CSV-formatted data which it will feed it into the datastore:</p>
<pre class="brush: python">
# upload-demo/loader.py

from google.appengine.ext import bulkload

class InventoryLoader(bulkload.Loader):
    def __init__(self):
        fields = [
            (&quot;name&quot;, str),
            (&quot;quantity&quot;, int)
        ]

        bulkload.Loader.__init__(self, &quot;Inventory&quot;, fields)

if __name__ == &quot;__main__&quot;:
    bulkload.main(InventoryLoader())
</pre>
<p>In this case we have created a loader for the Inventory model where the fields match the name and type of the fields in the model. Note that the loader is kept outside of the Django application.</p>
<p><strong>Step 2 &#8211; Add the handler to the project:</strong> This is done by adding an entry to <em>app.yaml</em> that references <em>loader.py</em>:</p>
<pre class="brush: bash">
# upload-demo/app.yaml

application: upload-demo
version: 1
runtime: python
api_version: 1

handlers:
- url: /load
  script: loader.py
  login: admin
- url: /.*
  script: main.py
</pre>
<p>A login will be required to access the loader URL&mdash;we don&#8217;t want anyone to add to our inventory without permission.</p>
<p><strong>Step 3 &#8211; Convert the data to CSV:</strong></p>
<p>Getting this step right can be surprisingly tricky, depending on your legacy database. For MySQL you may have to make sure that the user account has file write access rights:</p>
<pre class="brush: bash">
[root@mybox ~]# mysql -u root
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 74740
Server version: 5.0.45 Source distribution

Type &#039;help;&#039; or &#039;\h&#039; for help. Type &#039;\c&#039; to clear the buffer.

mysql&gt; grant file on *.* to &#039;test&#039;@&#039;localhost&#039;;
Query OK, 0 rows affected (0.01 sec)

mysql&gt; flush privileges;
Query OK, 0 rows affected (0.01 sec)
</pre>
<p>This command might have to be run as root, depending on how your database is configured. To do the data dump we run the following <em>select</em> statement:</p>
<pre class="brush: bash">
[test@mybox ~]$ mysql -u test customerdb -e &quot;select * into
    outfile &#039;/tmp/inventory.txt&#039; fields terminated by &#039;,&#039; from
    inventory&quot;
[test@mybox ~]$ cat /tmp/inventory.txt
ham,2
cheese,7
macaroni,1
</pre>
<p>If you are using PostgreSQL you can achieve the same by using the <a href="http://www.postgresql.org/docs/8.3/static/sql-copy.html" target="_blank"><em>COPY</em></a> command.</p>
<p><strong>Step 4 &#8211; Upload the data:</strong> First, redeploy your application to App Engine:</p>
<pre class="brush: bash">
[test@mybox ~]$ appcfg.py update upload-demo
</pre>
<p>We then use the <em>bulkload_client.py</em> script to upload our CSV file. The script is found in the <em>tools</em> folder of your App Engine installation&mdash;you may have to add it to your PATH. Note that you have to use double dashes for the parameters.</p>
<pre class="brush: bash">
[test@mybox ~]$ bulkload_client.py --filename=/tmp/inventory.txt
    --kind=Inventory --url=http://upload-demo.appspot.com/load

INFO 2008-06-15 07:39:21,682 bulkload_client.py]
    Starting import; maximum 10 entities per post
INFO 2008-06-15 07:39:21,684 bulkload_client.py]
    Importing 3 entities in 29 bytes
ERROR 2008-06-15 07:39:21,997 bulkload_client.py]
    An error occurred while importing: Received code 302: Found
&lt;HTML&gt;&lt;HEAD&gt;&lt;meta http-equiv=&quot;content-type&quot; content=&quot;text/html;charset=utf-8&quot;&gt;
&lt;TITLE&gt;302 Moved&lt;/TITLE&gt;&lt;/HEAD&gt;&lt;BODY&gt;
&lt;H1&gt;302 Moved&lt;/H1&gt;
The document has moved
&lt;A HREF=&quot;https://www.google.com/accounts/ServiceLogin?service=ah&amp;
continue=http://upload-demo.appspot.com/_ah/login%3Fcontinue%3Dhttp://
upload-demo.appspot.com/load&amp;ltmpl=gm&amp;ahname=Django+data+u
pload+demo&amp;sig=f9861d41d527e55f15742b8d54504bcc&quot;&gt;here&lt;/A&gt;.
&lt;/BODY&gt;&lt;/HTML&gt;

ERROR    2008-06-15 07:39:21,997 bulkload_client.py] Import failed
</pre>
<p>Now, that didn&#8217;t work. Remember that <em>app.yaml</em> says we have to authenticate ourself as an admin user before we can upload data. Try visiting <a href="http://upload-demo.appspot.com/load" target="_blank">http://upload-demo.appspot.com/load</a> in a web browser. After having authenticated yourself using your Google account you will be redirected to the following page:</p>
<p><img src="http://thomas.broxrost.com/wp-content/uploads/2008/06/loader_authentication.jpg" alt="Loader authentication screen" title="loader_authentication" width="500" height="203" class="alignnone size-full wp-image-21" /></p>
<p>Just what we needed! Add the cookie string parameter to the previous request and try again:</p>
<pre class="brush: bash">
[test@mybox ~]$ bulkload_client.py --filename=/tmp/inventory.txt
    --kind=Inventory --url=http://upload-demo.appspot.com/load
    --cookie=&#039;ACSID=AJKiYcE[...]1Hh4&#039;

INFO 2008-06-15 07:50:58,541 bulkload_client.py]
    Starting import; maximum 10 entities per post
INFO 2008-06-15 07:50:58,549 bulkload_client.py]
    Importing 3 entities in 29 bytes
INFO 2008-06-15 07:50:59,102 bulkload_client.py]
    Import succcessful
</pre>
<p>If you visit <a href="http://upload-demo.appspot.com">http://upload-demo.appspot</a> you should now see the data we just uploaded.</p>
<p><strong>Final notes:</strong> This simple example should be enough to get you started. When converting real-life databases you will have to deal with more complex schemas with references between tables. The discussion <a href="http://groups.google.com/group/google-appengine/browse_thread/thread/f12c18d60c632dd3/271674e41ad2cdd6?#271674e41ad2cdd6" target="_blank">here</a> should point you in the right direction. You may also find the SDK documentation on <a href="http://code.google.com/appengine/docs/datastore/typesandpropertyclasses.html" target="_blank">types and property classes</a> useful when porting your legacy database.</p>
<div id="wherego_related"><h3>Readers who viewed this page, also viewed:</h3><ul><li><a href="http://thomas.broxrost.com/2009/05/27/on-the-sadness-of-nouns/" rel="bookmark" class="wherego_title">On the sadness of nouns</a></li><li><a href="http://thomas.broxrost.com/2010/02/02/aws-cuts-data-transfer-rates-pricing-comparison-update/" rel="bookmark" class="wherego_title">AWS cuts data transfer rates: Pricing comparison update</a></li><li><a href="http://thomas.broxrost.com/2009/08/28/updated-awsgo-daddy-dedicated-server-cost-comparison/" rel="bookmark" class="wherego_title">Updated AWS/Go Daddy dedicated server cost comparison</a></li><li><a href="http://thomas.broxrost.com/2008/04/08/django-on-google-app-engine/" rel="bookmark" class="wherego_title">Django on Google App Engine in 13 simple steps</a></li><li><a href="http://thomas.broxrost.com/2008/04/20/django-on-google-app-engine-templates-and-static-files/" rel="bookmark" class="wherego_title">Django on Google App Engine: Templates and static files</a></li><li>Powered by <a href="http://ajaydsouza.com/wordpress/plugins/where-did-they-go-from-here/">Where did they go from here?</a></li></ul></div>]]></content:encoded>
			<wfw:commentRss>http://thomas.broxrost.com/2008/06/15/porting-legacy-databases-to-google-app-engine/feed/</wfw:commentRss>
		<slash:comments>32</slash:comments>
		</item>
		<item>
		<title>Django on Google App Engine: Templates and static files</title>
		<link>http://thomas.broxrost.com/2008/04/20/django-on-google-app-engine-templates-and-static-files/#utm_source=feed&amp;utm_medium=feed&amp;utm_campaign=feed</link>
		<comments>http://thomas.broxrost.com/2008/04/20/django-on-google-app-engine-templates-and-static-files/#comments</comments>
		<pubDate>Sun, 20 Apr 2008 14:21:30 +0000</pubDate>
		<dc:creator>Thomas Brox Røst</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://thomas.broxrost.com/?p=15</guid>
		<description><![CDATA[In a previous tutorial we learned how to set up a simple Django project on the Google App Engine. We also saw how to use the App Engine datastore in place of the Django model API. Now, let&#8217;s have a look at how to integrate Django templates. I will also show you how to serve [...]]]></description>
			<content:encoded><![CDATA[<p>In a <a href="http://thomas.broxrost.com/2008/04/08/django-on-google-app-engine/#utm_source=feed&amp;utm_medium=feed&amp;utm_campaign=feed">previous tutorial</a> we learned how to set up a simple <a href="http://www.djangoproject.com/">Django</a> project on the <a href="http://code.google.com/appengine/">Google App Engine</a>. We also saw how to use the App Engine <a href="http://code.google.com/appengine/docs/datastore/">datastore</a> in place of the Django model API.</p>
<p>Now, let&#8217;s have a look at how to integrate Django templates. I will also show you how to serve static files.</p>
<p><strong>Important:</strong> Remember to <a href="http://code.google.com/p/googleappengine/downloads/list">upgrade to the latest version of the App Engine SDK</a> (version 1.0.1 at the time of writing). Otherwise, this tutorial will not work for you if you are developing on Windows.</p>
<p><strong>Step 1:</strong> Set up an App Engine project&mdash;I am calling mine <em>djangostatic</em>. Follow steps 1 through 7 from the <a href="http://thomas.broxrost.com/2008/04/08/django-on-google-app-engine/#utm_source=feed&amp;utm_medium=feed&amp;utm_campaign=feed">previous tutorial</a>, remembering to substitute the project directory path and project name in <em>main.py</em> and <em>app.yaml</em>, and you will be all set.</p>
<p><strong>Step 2:</strong> We will create a simple view that makes use of a template. First, let us define the template. Create a directory where you can store templates:</p>
<pre class="brush: bash">
tmp/djangostatic$ cd djangostatic/main
tmp/djangostatic/djangostatic/main$ mkdir -p templates/main
</pre>
<p>Then, add the file <em>main.html</em > to your new template directory:</p>
<pre class="brush: html">
# djangostatic/djangostatic/main/templates/main/main.html

&lt;html&gt;
    &lt;head&gt;
        &lt;link href=&quot;/css/main.css&quot; type=&quot;text/css&quot;
               rel=&quot;stylesheet&quot;&gt;&lt;/link&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;p&gt;
            Hello world!
        &lt;/p&gt;
    &lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Note that the template refers to a style sheet file, <em>main.css</em>, which we will create later on.</p>
<p><strong>Step 3:</strong> Django needs to be told where to search for template files: this is done in the <em>settings.py</em> file. The settings file is mostly pre-configured; we just have to modify the part that sets the <em>TEMPLATE_DIRS</em> variable:</p>
<pre class="brush: python">
# djangostatic/djangostatic/settings.py

import os
ROOT_PATH = os.path.dirname(__file__)

TEMPLATE_DIRS = (
    ROOT_PATH + &quot;/main/templates&quot;,
)
</pre>
<p><strong>Step 4:</strong> After creating the template and telling Django where to find it, we have to write a view that does the actual rendering:</p>
<pre class="brush: python">
# djangostatic/djangostatic/main/views.py

from django.shortcuts import render_to_response

def main(request):
    return render_to_response(&quot;main/main.html&quot;)
</pre>
<p>This tells Django to use the template <em>main/main.html</em> when rendering the response. The <em>render_to_response</em> method is a convenient shortcut for rendering a template and returning a response in one step.</p>
<p><strong>Step 5:</strong> Finally, we need to map a URL to our view&mdash;this is done in <em>urls.py</em>:</p>
<pre class="brush: python">
# djangostatic/djangostatic/urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns(&quot;&quot;,
    (r&quot;^$&quot;, &quot;djangostatic.main.views.main&quot;),
)
</pre>
<p>Start your development server (<em>dev_appserver.py djangostatic</em>), fire up your browser, and open the page at <em>http://127.0.0.1:8080/</em>. If you have done everything right so far, you should get the &#8220;hello world&#8221; message from the template.</p>
<p><strong>Step 6:</strong> So what about the style sheet file, <em>main.css</em>? A style sheet file is a typical example of a static file. We use Django for rendering dynamic pages, so requests for static files should not be handled by the Django engine. In a regular Django application, we usually configure the web server to route such requests to a specific directory. On the App Engine, we achieve the same effect by adding a static handler to <em>app.yaml</em>:</p>
<pre class="brush: bash">
# djangostatic/app.yaml

application: djangostatic
version: 1
runtime: python
api_version: 1

handlers:
- url: /css
  static_dir: media/css

- url: /.*
  script: main.py
</pre>
<p>Here, we have added an entry that routes all requests beginning with <em>/css</em> to the directory <em>media/css</em>. Let us create this directory:</p>
<pre class="brush: bash">
tmp/djangostatic$ mkdir -p media/css
</pre>
<p><strong>Step 7:</strong> The link in our template specified <em>/css/main.css</em> as the full URL, so we have to add the <em>main.css</em> file to our new directory:</p>
<pre class="brush: css">
# djangostatic/media/css/main.css

p {
    font-size: 48px;
}
</pre>
<p>Reload the application page; the browser should now be able to make use of the style sheet so that the message is displayed in a larger font. You can view the final results <a href="http://djangostatic.appspot.com/">here</a>.</p>
<p><strong>Final notes:</strong> To learn more about how to serve static files on App Engine, have a look at the <a href="http://code.google.com/appengine/docs/configuringanapp.html">official documentation on how to configure an app</a>. Django templates are very powerful&mdash;this tutorial has only shown you the absolute basics. Visit the <a href="http://www.djangoproject.com/documentation/templates/">Django template documentation</a> to get the full story.</p>
<div id="wherego_related"><h3>Readers who viewed this page, also viewed:</h3><ul><li><a href="http://thomas.broxrost.com/2008/06/15/porting-legacy-databases-to-google-app-engine/" rel="bookmark" class="wherego_title">Porting legacy databases to Google App Engine</a></li><li><a href="http://thomas.broxrost.com/2009/08/28/updated-awsgo-daddy-dedicated-server-cost-comparison/" rel="bookmark" class="wherego_title">Updated AWS/Go Daddy dedicated server cost comparison</a></li><li><a href="http://thomas.broxrost.com/2009/03/12/why-amazon-web-services-just-became-a-competitive-web-hosting-provider/" rel="bookmark" class="wherego_title">Why Amazon Web Services just became a competitive web hosting provider</a></li><li><a href="http://thomas.broxrost.com/2008/04/08/django-on-google-app-engine/" rel="bookmark" class="wherego_title">Django on Google App Engine in 13 simple steps</a></li><li>Powered by <a href="http://ajaydsouza.com/wordpress/plugins/where-did-they-go-from-here/">Where did they go from here?</a></li></ul></div>]]></content:encoded>
			<wfw:commentRss>http://thomas.broxrost.com/2008/04/20/django-on-google-app-engine-templates-and-static-files/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Django on Google App Engine in 13 simple steps</title>
		<link>http://thomas.broxrost.com/2008/04/08/django-on-google-app-engine/#utm_source=feed&amp;utm_medium=feed&amp;utm_campaign=feed</link>
		<comments>http://thomas.broxrost.com/2008/04/08/django-on-google-app-engine/#comments</comments>
		<pubDate>Tue, 08 Apr 2008 16:48:39 +0000</pubDate>
		<dc:creator>Thomas Brox Røst</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://blog.broxrost.com/?p=8</guid>
		<description><![CDATA[In this tutorial I will show you how to get a simple datastore-backed Django application up and running on the Google App Engine. I will assume that you are somewhat familiar with Django. Update 1: You can download the full set of files from here. Make sure to fix the sys.path in main.py. Update 2: [...]]]></description>
			<content:encoded><![CDATA[<p>In this tutorial I will show you how to get a simple datastore-backed Django application up and running on the <a href="http://code.google.com/appengine/" target="_blank">Google App Engine</a>. I will assume that you are somewhat familiar with Django.</p>
<p><strong>Update 1:</strong> You can download the full set of files from <a href="http://thomas.broxrost.com/wp-content/uploads/2008/04/mashname.zip#utm_source=feed&amp;utm_medium=feed&amp;utm_campaign=feed">here</a>. Make sure to fix the sys.path in main.py.</p>
<p><strong>Update 2:</strong> There is now a <a href="http://www.tsdesign.info/blog/2008/04/11/google-app-engine-ile-13-basit-adimda-django/" target="_blank">Turkish translation</a> of this tutorial, courtesy of Türker Sezer.</p>
<p><strong>Update 3:</strong> Now in <a href="http://konkursof.blogspot.com/2008/05/django-google-app-engine-12.html" target="_blank">Russian</a> as well.</p>
<p><strong>Update 4:</strong> <a href="http://marcioandreyoliveira.blogspot.com/2009/04/13-passos-simples-para-termos-o-django.html">Brazilian Portuguese tranlation</a> by Marcio Andrey Oliveira.</p>
<hr /><strong>Step 1:</strong> Register an app name and install the development kit per the <a href="http://code.google.com/appengine/docs/gettingstarted/devenvironment.html" target="_blank">instructions</a>.</p>
<p><strong>Step 2:</strong> Create a directory for your application—for this tutorial my application is called <em>mashname</em>:</p>
<pre class="brush: bash">
tmp$ mkdir mashname
tmp$ cd mashname
</pre>
<p><strong>Step 3:</strong> Add a file called <em>main.py</em> to your new directory:</p>
<pre class="brush: python">
# main.py

import os, sys
os.environ[&amp;quot;DJANGO_SETTINGS_MODULE&amp;quot;] = &amp;quot;mashname.settings&amp;quot;
sys.path.append(&amp;quot;/home/brox/tmp/mashname&amp;quot;)

# Google App Engine imports.
from google.appengine.ext.webapp import util

# Force Django to reload its settings.
from django.conf import settings
settings._target = None

import django.core.handlers.wsgi
import django.core.signals
import django.db
import django.dispatch.dispatcher

# Log errors.
#django.dispatch.dispatcher.connect(
#   log_exception, django.core.signals.got_request_exception)

# Unregister the rollback event handler.
django.dispatch.dispatcher.disconnect(
django.db._rollback_on_exception,
django.core.signals.got_request_exception)

def main():
    # Create a Django application for WSGI.
    application = django.core.handlers.wsgi.WSGIHandler()

    # Run the WSGI CGI handler with that application.
    util.run_wsgi_app(application)

if __name__ == &amp;quot;__main__&amp;quot;:
    main()
</pre>
<p>This is basically the same file as suggested <a href="http://code.google.com/appengine/articles/django.html" target="_blank">here</a>, except I had to set the Python path to be able to test locally. I also had to set the DJANGO_SETTINGS_MODULE—this might not be necessary when running on the App Engine. I had to disable the error logging which I was not able to get working.</p>
<p><strong>Step 4:</strong> Add a file called <em>app.yaml</em> to the same directory:</p>
<pre class="brush: bash">
application: mashname
version: 1
runtime: python
api_version: 1

handlers:
- url: /.*
  script: main.py
</pre>
<p>Make sure to get the application name right.</p>
<p><strong>Step 5:</strong> From your <em>mashname</em> directory, create a new Django project:</p>
<pre class="brush: bash">
tmp/mashname$ django-admin.py startproject mashname
</pre>
<p>(I&#8217;m assuming that your current Django setup is working as it should.)</p>
<p><strong>Step 6:</strong> You should now be able to test your application:</p>
<pre class="brush: bash">
tmp/mashname$ cd ..
tmp$ dev_appserver.py mashname
INFO     2008-04-08 19:08:10,023 appcfg.py] Checking for updates to the SDK.
INFO     2008-04-08 19:08:10,384 appcfg.py] The SDK is up to date.
INFO     2008-04-08 19:08:10,404 dev_appserver_main.py] Running application mash
name on port 8080: http://localhost:8080
</pre>
<p>Point your browser towards <em>http://127.0.0.1:8080/</em> and you should get the standard Django <em>It worked!</em> message.</p>
<p><strong>Step 7:</strong> Create a Django app within the project:</p>
<pre class="brush: bash">
tmp$ cd mashname
tmp/mashname$ python mashname/manage.py startapp main
</pre>
<p><strong>Step 8:</strong> Now it is time to add a model. We will be creating a simple application that logs all visitors to the data store and displays their IP address and time of visit. Edit  <em>~/mashname/mashname/main/models.py</em> so that it looks like this:</p>
<pre class="brush: python">
# models.py

from google.appengine.ext import db

class Visitor(db.Model):
    ip = db.StringProperty()
    added_on = db.DateTimeProperty(auto_now_add=True)
</pre>
<p>There is no need to sync the database since we are not using regular Django models.</p>
<p><strong>Step 9:</strong> Now we create a view that is responsible for both adding data to the Visitor model and showing the previous visitors. Edit <em>views.py</em> (in the same directory as <em>models.py</em>) so that it does what we want:</p>
<pre class="brush: python">
# views.py

from django.http import HttpResponse

from mashname.main.models import Visitor

def main(request):
    visitor = Visitor()
    visitor.ip = request.META[&amp;quot;REMOTE_ADDR&amp;quot;]
    visitor.put()

    result = &amp;quot;&amp;quot;
    visitors = Visitor.all()
    visitors.order(&amp;quot;-added_on&amp;quot;)

    for visitor in visitors.fetch(limit=40):
        result += visitor.ip + u&amp;quot; visited on &amp;quot; + unicode(visitor.added_on) + u&amp;quot;&amp;quot;

    return HttpResponse(result)
</pre>
<p><strong>Step 10:</strong> Finally, make your <em>urls.py</em> point towards the view:</p>
<pre class="brush: python">
# urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns(&amp;quot;&amp;quot;,
    (r&amp;quot;^$&amp;quot;, &amp;quot;mashname.main.views.main&amp;quot;),
)
</pre>
<p><strong>Step 11:</strong> Test your application (as in step 6) and everything should hopefully work. For each page reload a new entry is added to the Visitor model and shown in the view.</p>
<p><strong>Step 12:</strong> Upload your application to the Google App Engine:</p>
<pre class="brush: bash">
tmp$ appcfg.py update mashname
</pre>
<p>For the first upload you will have to provide the mail address and password for your Google account.</p>
<p><strong>Step 13:</strong> Enjoy! To view the final results, go to <a href="http://mashname.appspot.com/" target="_blank">http://mashname.appspot.com/</a>.</p>
<div id="wherego_related"><h3>Readers who viewed this page, also viewed:</h3><ul><li><a href="http://thomas.broxrost.com/2009/06/01/how-to-compile-simpleparse-210a1-for-python-26-on-windows-vista/" rel="bookmark" class="wherego_title">How to compile SimpleParse 2.1.0a1 for Python 2.6 on Windows Vista</a></li><li><a href="http://thomas.broxrost.com/2009/04/06/when-endnote-x2-fails/" rel="bookmark" class="wherego_title">When EndNote X2 fails</a></li><li><a href="http://thomas.broxrost.com/2008/04/20/django-on-google-app-engine-templates-and-static-files/" rel="bookmark" class="wherego_title">Django on Google App Engine: Templates and static files</a></li><li><a href="http://thomas.broxrost.com/2009/05/27/on-the-sadness-of-nouns/" rel="bookmark" class="wherego_title">On the sadness of nouns</a></li><li><a href="http://thomas.broxrost.com/2009/08/28/updated-awsgo-daddy-dedicated-server-cost-comparison/" rel="bookmark" class="wherego_title">Updated AWS/Go Daddy dedicated server cost comparison</a></li><li>Powered by <a href="http://ajaydsouza.com/wordpress/plugins/where-did-they-go-from-here/">Where did they go from here?</a></li></ul></div>]]></content:encoded>
			<wfw:commentRss>http://thomas.broxrost.com/2008/04/08/django-on-google-app-engine/feed/</wfw:commentRss>
		<slash:comments>51</slash:comments>
		</item>
	</channel>
</rss>
