<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" xml:lang="en-us"><title>All Buttons Pressed</title><link href="http://www.allbuttonspressed.com/" rel="alternate" /><id>http://www.allbuttonspressed.com/</id><updated>2011-11-24T12:24:00Z</updated><subtitle>Web development, App Engine, NoSQL, client-side Python</subtitle><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/AllButtonsPressed" /><feedburner:info uri="allbuttonspressed" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>AllButtonsPressed</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><entry><title>Goodbye</title><link href="http://feedproxy.google.com/~r/AllButtonsPressed/~3/H7UaEtH40oo/goodbye" rel="alternate" /><updated>2011-11-24T12:24:00Z</updated><author><name>Waldemar Kornewald</name></author><id>http://www.allbuttonspressed.com/goodbye</id><summary type="html">&lt;div class="document"&gt;
&lt;p&gt;Hi everyone,&lt;/p&gt;
&lt;p&gt;unfortunately, Thomas and I have to say goodbye to our open-source community. We started contributing because we thought it was interesting and we had fun building something together. However, our projects have stopped being fun. We're not even using most of them, anymore (not even at our startup). This is why we've made a final decision: We've stopped maintaining our open-source projects. All of them.&lt;/p&gt;
&lt;p&gt;This primarily affects djangoappengine users. The MongoDB backend has its own capable contributors, so django-mongodb-engine users should be unaffected by this. We're really sorry, especially to those who have a business that depends on djangoappengine. Thanks to everyone who has supported us.&lt;/p&gt;
&lt;p&gt;If anyone is interested in continuing djangoappengine development, please send a mail to the &lt;a class="reference external" href="http://groups.google.com/forum/#!forum/django-non-relational"&gt;Django-nonrel discussion group&lt;/a&gt;.&lt;/p&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;Best wishes,&lt;/div&gt;
&lt;div class="line"&gt;Waldemar and Thomas&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Thanks for your warm response! We really didn't expect that. Wilfred Hughes offered to take over, so hopefully our projects won't die. By the way, I'll try to keep blogging about web development, NoSQL, and App Engine. There's a whole lot of stuff we've learned and keep learning at our &lt;a class="reference external" href="http://www.pentotype.com"&gt;startup&lt;/a&gt; and I'd like to share that with you from time to time.&lt;/p&gt;
&lt;p&gt;Please also read the &lt;a class="reference external" href="http://news.ycombinator.com/item?id=3274662"&gt;comments on Hacker News&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;

&lt;div class="static-share-buttons" style="overflow:hidden"&gt;
&lt;a class="share-button" target="_blank" title="Tweet this!" style="margin-right:5px;" href="http://twitter.com/share?text=Goodbye&amp;amp;url=http%3A//www.allbuttonspressed.com/goodbye&amp;amp;via=wkornewald"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/twitter-ba4f7679fb49cd4eb99cae4267d48d23c81137d4.png" alt="Tweet this!" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Facebook" style="margin-right:5px;" href="http://www.facebook.com/share.php?u=http%3A//www.allbuttonspressed.com/goodbye&amp;amp;t=Goodbye"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/facebook-4b8233c3eb59d633eacdc5dbd8b98be769b9386d.png" alt="Share on Facebook" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Email a friend" style="margin-right:5px;" href="http://feedburner.google.com/fb/a/emailFlare?itemTitle=Goodbye&amp;amp;uri=http%3A//www.allbuttonspressed.com/goodbye"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/email-e5a5556616278659d7761a5df6c58238d77ec47c.png" alt="Email a friend" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Delicious" style="margin-right:5px;" href="http://del.icio.us/post?url=http%3A//www.allbuttonspressed.com/goodbye&amp;amp;title=Goodbye"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/delicious-400c2ae7e6871479f4bb34a4f8f476784a0992bf.png" alt="Share on Delicious" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on StumbleUpon" style="margin-right:5px;" href="http://www.stumbleupon.com/submit?url=http%3A//www.allbuttonspressed.com/goodbye&amp;amp;title=Goodbye"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/stumbleupon-b725d91b84d24fa42787a8c79aa2695c5e5fb288.png" alt="Share on StumbleUpon" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Digg" style="margin-right:5px;" href="http://digg.com/submit?url=http%3A//www.allbuttonspressed.com/goodbye&amp;amp;title=Goodbye"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/digg-8ada117a5610042c9fd592d3f4d3d20be242334d.png" alt="Share on Digg" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Reddit" style="margin-right:5px;" href="http://reddit.com/submit?url=http%3A//www.allbuttonspressed.com/goodbye&amp;amp;title=Goodbye"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/reddit-f2c9d5792319c1a340054f310f4646642a48fe43.png" alt="Share on Reddit" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Technorati" style="margin-right:5px;" href="http://technorati.com/faves?sub=favthis&amp;amp;add=http%3A//www.allbuttonspressed.com/goodbye"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/technorati-1fa5656a85357eba71c018c718a7b36561a6f71c.png" alt="Share on Technorati" width="32" height="32" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="http://www.allbuttonspressed.com/goodbye#disqus_thread"&gt;Leave a comment&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/AllButtonsPressed/~4/H7UaEtH40oo" height="1" width="1"/&gt;</summary><feedburner:origLink>http://www.allbuttonspressed.com/goodbye</feedburner:origLink></entry><entry><title>Using dev_appserver with Python 2.7 on App Engine</title><link href="http://feedproxy.google.com/~r/AllButtonsPressed/~3/DNHJZpv7zas/using-dev_appserver-with-python-2-7-on-app-engine" rel="alternate" /><updated>2011-10-18T15:29:00Z</updated><author><name>Waldemar Kornewald</name></author><id>http://www.allbuttonspressed.com/using-dev_appserver-with-python-2-7-on-app-engine</id><summary type="html">&lt;div class="document"&gt;
&lt;p&gt;Starting with SDK 1.6.0 the App Engine fully supports Python 2.7. This article originally described a workaround for getting Python 2.7 running on SDK 1.5.5. Now it only describes how to port your &lt;code class="docutils literal"&gt;app.yaml&lt;/code&gt; and request handlers to Python 2.7.&lt;/p&gt;
&lt;p&gt;The following instructions work with any WSGI-compliant web framework. I'll explain how to use Python 2.7 with djangoappengine/Django-nonrel at the end of this post. Yes, Django-nonrel works with Python 2.7 on App Engine. This blog (&lt;a class="reference external" href="http://www.allbuttonspressed.com/"&gt;All Buttons Pressed&lt;/a&gt;) is already running on Python 2.7 with &lt;code class="docutils literal"&gt;threadsafe: yes&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let's start with a simple &lt;code class="docutils literal"&gt;app.yaml&lt;/code&gt; for Python 2.5:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;application: yourappid
version: 1
runtime: python
api_version: 1

handlers:
- url: /.*
  script: handler.py
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class="docutils literal"&gt;handler.py&lt;/code&gt; file looks like this:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #555555"&gt;google.appengine.ext&lt;/span&gt; &lt;span style="font-weight: bold"&gt;import&lt;/span&gt; webapp
&lt;span style="font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #555555"&gt;google.appengine.ext.webapp.util&lt;/span&gt; &lt;span style="font-weight: bold"&gt;import&lt;/span&gt; run_wsgi_app

&lt;span style="color: #999988; font-style: italic"&gt;# This can be any WSGI handler. Here we just use webapp.&lt;/span&gt;
application &lt;span style="font-weight: bold"&gt;=&lt;/span&gt; webapp&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;WSGIApplication(&lt;span style="font-weight: bold"&gt;...&lt;/span&gt;)

&lt;span style="font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #990000; font-weight: bold"&gt;main&lt;/span&gt;():
    run_wsgi_app(application)

&lt;span style="font-weight: bold"&gt;if&lt;/span&gt; __name__ &lt;span style="font-weight: bold"&gt;==&lt;/span&gt; &lt;span style="color: #bb8844"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;:
    main()
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In order to use Python 2.7 you have to change your &lt;code class="docutils literal"&gt;app.yaml&lt;/code&gt; like this:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;application: yourappid
version: 1
runtime: python27
api_version: 1
threadsafe: yes

handlers:
- url: /.*
  script: handler.application
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here we've changed the runtime to &lt;code class="docutils literal"&gt;python27&lt;/code&gt;, added &lt;code class="docutils literal"&gt;threadsafe: yes&lt;/code&gt; and modified the last line (&lt;code class="docutils literal"&gt;script&lt;/code&gt;) to point to the WSGI handler directly instead of just the &lt;code class="docutils literal"&gt;handler.py&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;Also, our &lt;code class="docutils literal"&gt;handler.py&lt;/code&gt; can be simplified like this:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #555555"&gt;google.appengine.ext&lt;/span&gt; &lt;span style="font-weight: bold"&gt;import&lt;/span&gt; webapp
&lt;span style="font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #555555"&gt;google.appengine.ext.webapp.util&lt;/span&gt; &lt;span style="font-weight: bold"&gt;import&lt;/span&gt; run_wsgi_app

application &lt;span style="font-weight: bold"&gt;=&lt;/span&gt; webapp&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;WSGIApplication(&lt;span style="font-weight: bold"&gt;...&lt;/span&gt;)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In other words, we've just removed &lt;code class="docutils literal"&gt;main()&lt;/code&gt;. Note, webapp users would normally port their code to &lt;a class="reference external" href="http://webapp-improved.appspot.com/"&gt;webapp2&lt;/a&gt;, but that's not really relevant for this post, so let's just ignore that detail.&lt;/p&gt;
&lt;p&gt;That's all you need to use Python 2.7 on both the production server and dev_appserver.&lt;/p&gt;
&lt;div class="section" id="using-djangoappengine-with-python-2-7"&gt;
&lt;h2&gt;Using djangoappengine with Python 2.7&lt;/h2&gt;
&lt;p&gt;If you're a Django-nonrel / &lt;a class="reference external" href="http://www.allbuttonspressed.com/projects/djangoappengine"&gt;djangoappengine&lt;/a&gt; user you can just change your &lt;code class="docutils literal"&gt;app.yaml&lt;/code&gt; to look like this:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;application: yourappid
version: 1
runtime: python27
api_version: 1
threadsafe: yes

builtins:
- remote_api: on

inbound_services:
- warmup

libraries:
- name: django
  version: latest

handlers:
- url: /_ah/queue/deferred
  script: djangoappengine.deferred.handler.application
  login: admin

- url: /_ah/stats/.*
  script: djangoappengine.appstats.application

- url: /.*
  script: djangoappengine.main.application
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That's it. djangoappengine will take care of the rest. Note that the &lt;code class="docutils literal"&gt;libraries&lt;/code&gt; section is currently necessary due to a bug in SDK 1.6.0. Since you're already deploying a custom &lt;code class="docutils literal"&gt;django&lt;/code&gt; package it shouldn't be necessary to enable Django in the &lt;code class="docutils literal"&gt;libraries&lt;/code&gt; section. The App Engine team will fix this bug in the next SDK release.&lt;/p&gt;
&lt;p&gt;Happy porting!&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="static-share-buttons" style="overflow:hidden"&gt;
&lt;a class="share-button" target="_blank" title="Tweet this!" style="margin-right:5px;" href="http://twitter.com/share?text=Using%20dev_appserver%20with%20Python%202.7%20on%20App%20Engine&amp;amp;url=http%3A//www.allbuttonspressed.com/using-dev_appserver-with-python-2-7-on-app-engine&amp;amp;via=wkornewald"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/twitter-ba4f7679fb49cd4eb99cae4267d48d23c81137d4.png" alt="Tweet this!" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Facebook" style="margin-right:5px;" href="http://www.facebook.com/share.php?u=http%3A//www.allbuttonspressed.com/using-dev_appserver-with-python-2-7-on-app-engine&amp;amp;t=Using%20dev_appserver%20with%20Python%202.7%20on%20App%20Engine"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/facebook-4b8233c3eb59d633eacdc5dbd8b98be769b9386d.png" alt="Share on Facebook" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Email a friend" style="margin-right:5px;" href="http://feedburner.google.com/fb/a/emailFlare?itemTitle=Using%20dev_appserver%20with%20Python%202.7%20on%20App%20Engine&amp;amp;uri=http%3A//www.allbuttonspressed.com/using-dev_appserver-with-python-2-7-on-app-engine"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/email-e5a5556616278659d7761a5df6c58238d77ec47c.png" alt="Email a friend" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Delicious" style="margin-right:5px;" href="http://del.icio.us/post?url=http%3A//www.allbuttonspressed.com/using-dev_appserver-with-python-2-7-on-app-engine&amp;amp;title=Using%20dev_appserver%20with%20Python%202.7%20on%20App%20Engine"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/delicious-400c2ae7e6871479f4bb34a4f8f476784a0992bf.png" alt="Share on Delicious" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on StumbleUpon" style="margin-right:5px;" href="http://www.stumbleupon.com/submit?url=http%3A//www.allbuttonspressed.com/using-dev_appserver-with-python-2-7-on-app-engine&amp;amp;title=Using%20dev_appserver%20with%20Python%202.7%20on%20App%20Engine"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/stumbleupon-b725d91b84d24fa42787a8c79aa2695c5e5fb288.png" alt="Share on StumbleUpon" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Digg" style="margin-right:5px;" href="http://digg.com/submit?url=http%3A//www.allbuttonspressed.com/using-dev_appserver-with-python-2-7-on-app-engine&amp;amp;title=Using%20dev_appserver%20with%20Python%202.7%20on%20App%20Engine"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/digg-8ada117a5610042c9fd592d3f4d3d20be242334d.png" alt="Share on Digg" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Reddit" style="margin-right:5px;" href="http://reddit.com/submit?url=http%3A//www.allbuttonspressed.com/using-dev_appserver-with-python-2-7-on-app-engine&amp;amp;title=Using%20dev_appserver%20with%20Python%202.7%20on%20App%20Engine"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/reddit-f2c9d5792319c1a340054f310f4646642a48fe43.png" alt="Share on Reddit" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Technorati" style="margin-right:5px;" href="http://technorati.com/faves?sub=favthis&amp;amp;add=http%3A//www.allbuttonspressed.com/using-dev_appserver-with-python-2-7-on-app-engine"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/technorati-1fa5656a85357eba71c018c718a7b36561a6f71c.png" alt="Share on Technorati" width="32" height="32" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="http://www.allbuttonspressed.com/using-dev_appserver-with-python-2-7-on-app-engine#disqus_thread"&gt;Leave a comment&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/AllButtonsPressed/~4/DNHJZpv7zas" height="1" width="1"/&gt;</summary><feedburner:origLink>http://www.allbuttonspressed.com/using-dev_appserver-with-python-2-7-on-app-engine</feedburner:origLink></entry><entry><title>F() objects and QuerySet.update() support in djangoappengine</title><link href="http://feedproxy.google.com/~r/AllButtonsPressed/~3/U_Ij9YIz5Q8/f-objects-and-queryset-update-support-in-djangoappengine" rel="alternate" /><updated>2011-07-07T14:55:00Z</updated><author><name>Thomas Wanschik</name></author><id>http://www.allbuttonspressed.com/blog/django/f-objects-and-queryset-update-support-in-djangoappengine</id><summary type="html">&lt;div class="document"&gt;
&lt;p&gt;As some of you already noticed, we added supported for &lt;a class="reference external" href="https://docs.djangoproject.com/en/1.3/ref/models/querysets/#update"&gt;QuerySet.update()&lt;/a&gt; and &lt;a class="reference external" href="https://docs.djangoproject.com/en/dev/topics/db/queries/#query-expressions"&gt;F() objects&lt;/a&gt; as part of some features we currently need for our startup &lt;a class="reference external" href="http://www.pentotype.com/"&gt;pentotype&lt;/a&gt;. Finally, it's possible to use simple transactions on App Engine via Django-nonrel :) Let's see how it works.&lt;/p&gt;
&lt;div class="section" id="django-s-beauty"&gt;
&lt;h2&gt;Django's beauty&lt;/h2&gt;
&lt;p&gt;In order to show what we gain by using Django's &lt;code class="docutils literal"&gt;QuerySet.update()&lt;/code&gt; method and &lt;code class="docutils literal"&gt;F()&lt;/code&gt; objects let me illustrate the code differences between the old djangoappengine version and the new one using a simple transaction which increments the value of a counter by a specific amount. In both cases we have the following model definition:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="color: #999988; font-style: italic"&gt;# models.py&lt;/span&gt;
&lt;span style="font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #555555"&gt;django.db&lt;/span&gt; &lt;span style="font-weight: bold"&gt;import&lt;/span&gt; models

&lt;span style="font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #445588; font-weight: bold"&gt;Accumulator&lt;/span&gt;(models&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;Model):
    counter &lt;span style="font-weight: bold"&gt;=&lt;/span&gt; models&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;IntegerField()
    name &lt;span style="font-weight: bold"&gt;=&lt;/span&gt; models&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;CharField(max_length&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;&lt;span style="color: #009999"&gt;500&lt;/span&gt;)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The old version looks like this:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #555555"&gt;google.appengine.ext&lt;/span&gt; &lt;span style="font-weight: bold"&gt;import&lt;/span&gt; db

&lt;span style="font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #990000; font-weight: bold"&gt;increment_counter&lt;/span&gt;(pk, amount):
    obj &lt;span style="font-weight: bold"&gt;=&lt;/span&gt; Accumulator&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;objects&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;get(pk&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;pk)
    obj&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;counter &lt;span style="font-weight: bold"&gt;+=&lt;/span&gt; amount
    obj&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;save()

db&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;run_in_transaction(increment_counter, &lt;span style="color: #009999"&gt;1&lt;/span&gt;, &lt;span style="color: #009999"&gt;5&lt;/span&gt;)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;whereas the new one looks like this:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #555555"&gt;django.db.models&lt;/span&gt; &lt;span style="font-weight: bold"&gt;import&lt;/span&gt; F
Accumulator&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;objects&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;filter(pk&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;&lt;span style="color: #009999"&gt;1&lt;/span&gt;)&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;update(counter&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;F(&lt;span style="color: #bb8844"&gt;&amp;#39;counter&amp;#39;&lt;/span&gt;) &lt;span style="font-weight: bold"&gt;+&lt;/span&gt; &lt;span style="color: #009999"&gt;5&lt;/span&gt;)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Obviously, it's more elegant to update a counter using &lt;code class="docutils literal"&gt;QuerySet.update()&lt;/code&gt; and &lt;code class="docutils literal"&gt;F()&lt;/code&gt; objects because it's much clearer and we have to write less code, thus resulting in more productivity.&lt;/p&gt;
&lt;p&gt;Additionally, it's possible to do more complicated transactions, for example, updating multiple entities using Django's elegant syntax:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;Accumulator&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;objects&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;filter(name__startswith&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;&lt;span style="color: #bb8844"&gt;&amp;#39;simple&amp;#39;&lt;/span&gt;)&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;update(counter&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;F(&lt;span style="color: #bb8844"&gt;&amp;#39;counter&amp;#39;&lt;/span&gt;) &lt;span style="font-weight: bold"&gt;+&lt;/span&gt; &lt;span style="color: #009999"&gt;1&lt;/span&gt;)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note, that this will update each entity in its own transaction. With the old version we would have to iterate over each entity explicitly and start the transaction:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;counters &lt;span style="font-weight: bold"&gt;=&lt;/span&gt;  Accumulator&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;objects&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;filter(name__startswith&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;&lt;span style="color: #bb8844"&gt;&amp;#39;simple&amp;#39;&lt;/span&gt;)

&lt;span style="font-weight: bold"&gt;for&lt;/span&gt; counter &lt;span style="font-weight: bold"&gt;in&lt;/span&gt; counters:
   db&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;run_in_transaction(increment_counter, counter&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;pk, &lt;span style="color: #009999"&gt;5&lt;/span&gt;)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Again, we benefit from writing less code.&lt;/p&gt;
&lt;p&gt;However, one of the main benefits of &lt;code class="docutils literal"&gt;QuerySet.update()&lt;/code&gt; and &lt;code class="docutils literal"&gt;F()&lt;/code&gt; objects is that the code becomes even more portable between backends than before. For example, the &lt;a class="reference external" href="http://django-mongodb.org/"&gt;MongoDB backend&lt;/a&gt; also has support for &lt;code class="docutils literal"&gt;F()&lt;/code&gt; objects and &lt;code class="docutils literal"&gt;QuerySet.update()&lt;/code&gt;, so the same code can be used for transactional updates on App Engine and MongoDB. Moreover, the same code runs transactionally safe on SQL databases, too!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="what-s-next"&gt;
&lt;h2&gt;What's next&lt;/h2&gt;
&lt;p&gt;Support for &lt;code class="docutils literal"&gt;QuerySet.update()&lt;/code&gt; and &lt;code class="docutils literal"&gt;F()&lt;/code&gt; objects is the foundation of portable transactions over different databases. In combination with the &lt;a class="reference external" href="/projects/django-dbindexer"&gt;django-dbindexer&lt;/a&gt; it now becomes possible to add support for automatically sharded counters as described in &lt;a class="reference external" href="/blog/django/2010/01/Sharding-with-Django-on-App-Engine#django-s-advantage"&gt;Sharding with Django on App Engine&lt;/a&gt;! In other words, we could add an index definition in order to tell django-dbindexer to automatically shard specific updates. Stay tuned!&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="static-share-buttons" style="overflow:hidden"&gt;
&lt;a class="share-button" target="_blank" title="Tweet this!" style="margin-right:5px;" href="http://twitter.com/share?text=F%28%29%20objects%20and%20QuerySet.update%28%29%20support%20in%20djangoappengine&amp;amp;url=http%3A//www.allbuttonspressed.com/blog/django/f-objects-and-queryset-update-support-in-djangoappengine&amp;amp;via=wkornewald"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/twitter-ba4f7679fb49cd4eb99cae4267d48d23c81137d4.png" alt="Tweet this!" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Facebook" style="margin-right:5px;" href="http://www.facebook.com/share.php?u=http%3A//www.allbuttonspressed.com/blog/django/f-objects-and-queryset-update-support-in-djangoappengine&amp;amp;t=F%28%29%20objects%20and%20QuerySet.update%28%29%20support%20in%20djangoappengine"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/facebook-4b8233c3eb59d633eacdc5dbd8b98be769b9386d.png" alt="Share on Facebook" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Email a friend" style="margin-right:5px;" href="http://feedburner.google.com/fb/a/emailFlare?itemTitle=F%28%29%20objects%20and%20QuerySet.update%28%29%20support%20in%20djangoappengine&amp;amp;uri=http%3A//www.allbuttonspressed.com/blog/django/f-objects-and-queryset-update-support-in-djangoappengine"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/email-e5a5556616278659d7761a5df6c58238d77ec47c.png" alt="Email a friend" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Delicious" style="margin-right:5px;" href="http://del.icio.us/post?url=http%3A//www.allbuttonspressed.com/blog/django/f-objects-and-queryset-update-support-in-djangoappengine&amp;amp;title=F%28%29%20objects%20and%20QuerySet.update%28%29%20support%20in%20djangoappengine"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/delicious-400c2ae7e6871479f4bb34a4f8f476784a0992bf.png" alt="Share on Delicious" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on StumbleUpon" style="margin-right:5px;" href="http://www.stumbleupon.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/f-objects-and-queryset-update-support-in-djangoappengine&amp;amp;title=F%28%29%20objects%20and%20QuerySet.update%28%29%20support%20in%20djangoappengine"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/stumbleupon-b725d91b84d24fa42787a8c79aa2695c5e5fb288.png" alt="Share on StumbleUpon" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Digg" style="margin-right:5px;" href="http://digg.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/f-objects-and-queryset-update-support-in-djangoappengine&amp;amp;title=F%28%29%20objects%20and%20QuerySet.update%28%29%20support%20in%20djangoappengine"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/digg-8ada117a5610042c9fd592d3f4d3d20be242334d.png" alt="Share on Digg" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Reddit" style="margin-right:5px;" href="http://reddit.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/f-objects-and-queryset-update-support-in-djangoappengine&amp;amp;title=F%28%29%20objects%20and%20QuerySet.update%28%29%20support%20in%20djangoappengine"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/reddit-f2c9d5792319c1a340054f310f4646642a48fe43.png" alt="Share on Reddit" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Technorati" style="margin-right:5px;" href="http://technorati.com/faves?sub=favthis&amp;amp;add=http%3A//www.allbuttonspressed.com/blog/django/f-objects-and-queryset-update-support-in-djangoappengine"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/technorati-1fa5656a85357eba71c018c718a7b36561a6f71c.png" alt="Share on Technorati" width="32" height="32" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="http://www.allbuttonspressed.com/blog/django/f-objects-and-queryset-update-support-in-djangoappengine#disqus_thread"&gt;Leave a comment&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/AllButtonsPressed/~4/U_Ij9YIz5Q8" height="1" width="1"/&gt;</summary><feedburner:origLink>http://www.allbuttonspressed.com/blog/django/f-objects-and-queryset-update-support-in-djangoappengine</feedburner:origLink></entry><entry><title>Minor updates and API changes</title><link href="http://feedproxy.google.com/~r/AllButtonsPressed/~3/VyX4SyHzPIE/minor-updates-and-api-changes" rel="alternate" /><updated>2011-05-05T10:18:00Z</updated><author><name>Thomas Wanschik</name></author><id>http://www.allbuttonspressed.com/blog/django/minor-updates-and-api-changes</id><summary type="html">&lt;div class="document"&gt;
&lt;p&gt;Maybe you noticed that we've started to work on our projects again. ;) We unsurprisingly (:P) passed our final physics exams and are ready to work on NoSQL development again. Waldemar has already started to do some work in order to get our efforts into Django trunk. In the meantime I've started to work on some bug reports concerning &lt;a class="reference external" href="http://www.allbuttonspressed.com/projects/django-dbindexer"&gt;django-dbindexer&lt;/a&gt; and &lt;a class="reference external" href="http://www.allbuttonspressed.com/projects/nonrel-search"&gt;nonrel-search&lt;/a&gt;. Both now have a dependency on &lt;a class="reference external" href="http://www.allbuttonspressed.com/projects/django-autoload"&gt;django-autoload&lt;/a&gt; which ensures the loading of indexes or signal handlers before any request is processed. As a consequence, you have to adapt your code if you make use of django-dbindexer or nonrel-search. The documentation has been changed in order to reflect these changes. Maybe we'll take a few days off to go on vacation. Then, we'll come back and continue improving the way of NoSQL development.&lt;/p&gt;
&lt;/div&gt;

&lt;div class="static-share-buttons" style="overflow:hidden"&gt;
&lt;a class="share-button" target="_blank" title="Tweet this!" style="margin-right:5px;" href="http://twitter.com/share?text=Minor%20updates%20and%20API%20changes&amp;amp;url=http%3A//www.allbuttonspressed.com/blog/django/minor-updates-and-api-changes&amp;amp;via=wkornewald"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/twitter-ba4f7679fb49cd4eb99cae4267d48d23c81137d4.png" alt="Tweet this!" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Facebook" style="margin-right:5px;" href="http://www.facebook.com/share.php?u=http%3A//www.allbuttonspressed.com/blog/django/minor-updates-and-api-changes&amp;amp;t=Minor%20updates%20and%20API%20changes"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/facebook-4b8233c3eb59d633eacdc5dbd8b98be769b9386d.png" alt="Share on Facebook" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Email a friend" style="margin-right:5px;" href="http://feedburner.google.com/fb/a/emailFlare?itemTitle=Minor%20updates%20and%20API%20changes&amp;amp;uri=http%3A//www.allbuttonspressed.com/blog/django/minor-updates-and-api-changes"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/email-e5a5556616278659d7761a5df6c58238d77ec47c.png" alt="Email a friend" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Delicious" style="margin-right:5px;" href="http://del.icio.us/post?url=http%3A//www.allbuttonspressed.com/blog/django/minor-updates-and-api-changes&amp;amp;title=Minor%20updates%20and%20API%20changes"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/delicious-400c2ae7e6871479f4bb34a4f8f476784a0992bf.png" alt="Share on Delicious" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on StumbleUpon" style="margin-right:5px;" href="http://www.stumbleupon.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/minor-updates-and-api-changes&amp;amp;title=Minor%20updates%20and%20API%20changes"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/stumbleupon-b725d91b84d24fa42787a8c79aa2695c5e5fb288.png" alt="Share on StumbleUpon" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Digg" style="margin-right:5px;" href="http://digg.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/minor-updates-and-api-changes&amp;amp;title=Minor%20updates%20and%20API%20changes"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/digg-8ada117a5610042c9fd592d3f4d3d20be242334d.png" alt="Share on Digg" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Reddit" style="margin-right:5px;" href="http://reddit.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/minor-updates-and-api-changes&amp;amp;title=Minor%20updates%20and%20API%20changes"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/reddit-f2c9d5792319c1a340054f310f4646642a48fe43.png" alt="Share on Reddit" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Technorati" style="margin-right:5px;" href="http://technorati.com/faves?sub=favthis&amp;amp;add=http%3A//www.allbuttonspressed.com/blog/django/minor-updates-and-api-changes"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/technorati-1fa5656a85357eba71c018c718a7b36561a6f71c.png" alt="Share on Technorati" width="32" height="32" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="http://www.allbuttonspressed.com/blog/django/minor-updates-and-api-changes#disqus_thread"&gt;Leave a comment&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/AllButtonsPressed/~4/VyX4SyHzPIE" height="1" width="1"/&gt;</summary><feedburner:origLink>http://www.allbuttonspressed.com/blog/django/minor-updates-and-api-changes</feedburner:origLink></entry><entry><title>SimpleDB backend for Django-nonrel</title><link href="http://feedproxy.google.com/~r/AllButtonsPressed/~3/b9BHg6BbfbU/simpledb-backend-for-django-nonrel" rel="alternate" /><updated>2011-05-03T07:16:00Z</updated><author><name>Waldemar Kornewald</name></author><id>http://www.allbuttonspressed.com/blog/django/simpledb-backend-for-django-nonrel</id><summary type="html">&lt;div class="document"&gt;
&lt;p&gt;Dan Fairs has started developing a SimpleDB backend. That's the sixth backend for Django-nonrel! Check out &lt;a class="reference external" href="https://github.com/danfairs/django-simpledb"&gt;django-simpledb&lt;/a&gt; from github. The code is experimental and far from finished, but in a recent &lt;a class="reference external" href="http://twitter.com/#!/danfairs/status/65141310293671936"&gt;tweet&lt;/a&gt; Dan mentioned that he thinks he's very close to having the Django admin running on SimpleDB. That's huge, Dan! :) We're very close to supporting every popular NoSQL database. Guys, if you want to use Dango-nonrel with SimpleDB please help Dan with the &lt;a class="reference external" href="https://github.com/danfairs/django-simpledb"&gt;django-simpledb&lt;/a&gt; backend (or help Mirko with the &lt;a class="reference external" href="http://www.allbuttonspressed.com/blog/django/redis-backend-for-django-nonrel-in-development"&gt;Redis backend&lt;/a&gt; if you prefer that).&lt;/p&gt;
&lt;/div&gt;

&lt;div class="static-share-buttons" style="overflow:hidden"&gt;
&lt;a class="share-button" target="_blank" title="Tweet this!" style="margin-right:5px;" href="http://twitter.com/share?text=SimpleDB%20backend%20for%20Django-nonrel&amp;amp;url=http%3A//www.allbuttonspressed.com/blog/django/simpledb-backend-for-django-nonrel&amp;amp;via=wkornewald"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/twitter-ba4f7679fb49cd4eb99cae4267d48d23c81137d4.png" alt="Tweet this!" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Facebook" style="margin-right:5px;" href="http://www.facebook.com/share.php?u=http%3A//www.allbuttonspressed.com/blog/django/simpledb-backend-for-django-nonrel&amp;amp;t=SimpleDB%20backend%20for%20Django-nonrel"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/facebook-4b8233c3eb59d633eacdc5dbd8b98be769b9386d.png" alt="Share on Facebook" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Email a friend" style="margin-right:5px;" href="http://feedburner.google.com/fb/a/emailFlare?itemTitle=SimpleDB%20backend%20for%20Django-nonrel&amp;amp;uri=http%3A//www.allbuttonspressed.com/blog/django/simpledb-backend-for-django-nonrel"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/email-e5a5556616278659d7761a5df6c58238d77ec47c.png" alt="Email a friend" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Delicious" style="margin-right:5px;" href="http://del.icio.us/post?url=http%3A//www.allbuttonspressed.com/blog/django/simpledb-backend-for-django-nonrel&amp;amp;title=SimpleDB%20backend%20for%20Django-nonrel"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/delicious-400c2ae7e6871479f4bb34a4f8f476784a0992bf.png" alt="Share on Delicious" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on StumbleUpon" style="margin-right:5px;" href="http://www.stumbleupon.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/simpledb-backend-for-django-nonrel&amp;amp;title=SimpleDB%20backend%20for%20Django-nonrel"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/stumbleupon-b725d91b84d24fa42787a8c79aa2695c5e5fb288.png" alt="Share on StumbleUpon" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Digg" style="margin-right:5px;" href="http://digg.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/simpledb-backend-for-django-nonrel&amp;amp;title=SimpleDB%20backend%20for%20Django-nonrel"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/digg-8ada117a5610042c9fd592d3f4d3d20be242334d.png" alt="Share on Digg" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Reddit" style="margin-right:5px;" href="http://reddit.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/simpledb-backend-for-django-nonrel&amp;amp;title=SimpleDB%20backend%20for%20Django-nonrel"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/reddit-f2c9d5792319c1a340054f310f4646642a48fe43.png" alt="Share on Reddit" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Technorati" style="margin-right:5px;" href="http://technorati.com/faves?sub=favthis&amp;amp;add=http%3A//www.allbuttonspressed.com/blog/django/simpledb-backend-for-django-nonrel"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/technorati-1fa5656a85357eba71c018c718a7b36561a6f71c.png" alt="Share on Technorati" width="32" height="32" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="http://www.allbuttonspressed.com/blog/django/simpledb-backend-for-django-nonrel#disqus_thread"&gt;Leave a comment&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/AllButtonsPressed/~4/b9BHg6BbfbU" height="1" width="1"/&gt;</summary><feedburner:origLink>http://www.allbuttonspressed.com/blog/django/simpledb-backend-for-django-nonrel</feedburner:origLink></entry><entry><title>Redis backend for Django-nonrel in development</title><link href="http://feedproxy.google.com/~r/AllButtonsPressed/~3/kMPgpCCyuaU/redis-backend-for-django-nonrel-in-development" rel="alternate" /><updated>2011-04-16T05:48:00Z</updated><author><name>Waldemar Kornewald</name></author><id>http://www.allbuttonspressed.com/blog/django/redis-backend-for-django-nonrel-in-development</id><summary type="html">&lt;div class="document"&gt;
&lt;p&gt;Mirko Rossini has finally made his Redis backend public. It's called &lt;a class="reference external" href="https://github.com/MirkoRossini/django-redis-engine"&gt;django-redis-engine&lt;/a&gt; and according to him it's still in pre-alpha stage. From a quick glance at the code it looks like you'll be able to define database indexes in order to support queries that go beyond Redis' (meager ;) native query capabilities (much like with &lt;a class="reference external" href="http://www.allbuttonspressed.com/projects/django-dbindexer"&gt;django-dbindexer&lt;/a&gt;). This is awesome because you won't have to maintain indexes by hand. If you want to see a stable and fully featured Redis backend please help Mirko. Just clone the &lt;a class="reference external" href="https://github.com/MirkoRossini/django-redis-engine"&gt;repository&lt;/a&gt; from github and start playing with the code. Enjoy!&lt;/p&gt;
&lt;p&gt;P.S.: Sorry for the long period of silence. We're still not finished with our final diploma exams, but we're getting close. Soon we can restart blogging more frequently and take the results of the &lt;a class="reference external" href="http://www.allbuttonspressed.com/blog/django/2010/12/Merry-Christmas"&gt;previous survey&lt;/a&gt; into account.&lt;/p&gt;
&lt;/div&gt;

&lt;div class="static-share-buttons" style="overflow:hidden"&gt;
&lt;a class="share-button" target="_blank" title="Tweet this!" style="margin-right:5px;" href="http://twitter.com/share?text=Redis%20backend%20for%20Django-nonrel%20in%20development&amp;amp;url=http%3A//www.allbuttonspressed.com/blog/django/redis-backend-for-django-nonrel-in-development&amp;amp;via=wkornewald"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/twitter-ba4f7679fb49cd4eb99cae4267d48d23c81137d4.png" alt="Tweet this!" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Facebook" style="margin-right:5px;" href="http://www.facebook.com/share.php?u=http%3A//www.allbuttonspressed.com/blog/django/redis-backend-for-django-nonrel-in-development&amp;amp;t=Redis%20backend%20for%20Django-nonrel%20in%20development"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/facebook-4b8233c3eb59d633eacdc5dbd8b98be769b9386d.png" alt="Share on Facebook" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Email a friend" style="margin-right:5px;" href="http://feedburner.google.com/fb/a/emailFlare?itemTitle=Redis%20backend%20for%20Django-nonrel%20in%20development&amp;amp;uri=http%3A//www.allbuttonspressed.com/blog/django/redis-backend-for-django-nonrel-in-development"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/email-e5a5556616278659d7761a5df6c58238d77ec47c.png" alt="Email a friend" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Delicious" style="margin-right:5px;" href="http://del.icio.us/post?url=http%3A//www.allbuttonspressed.com/blog/django/redis-backend-for-django-nonrel-in-development&amp;amp;title=Redis%20backend%20for%20Django-nonrel%20in%20development"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/delicious-400c2ae7e6871479f4bb34a4f8f476784a0992bf.png" alt="Share on Delicious" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on StumbleUpon" style="margin-right:5px;" href="http://www.stumbleupon.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/redis-backend-for-django-nonrel-in-development&amp;amp;title=Redis%20backend%20for%20Django-nonrel%20in%20development"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/stumbleupon-b725d91b84d24fa42787a8c79aa2695c5e5fb288.png" alt="Share on StumbleUpon" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Digg" style="margin-right:5px;" href="http://digg.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/redis-backend-for-django-nonrel-in-development&amp;amp;title=Redis%20backend%20for%20Django-nonrel%20in%20development"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/digg-8ada117a5610042c9fd592d3f4d3d20be242334d.png" alt="Share on Digg" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Reddit" style="margin-right:5px;" href="http://reddit.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/redis-backend-for-django-nonrel-in-development&amp;amp;title=Redis%20backend%20for%20Django-nonrel%20in%20development"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/reddit-f2c9d5792319c1a340054f310f4646642a48fe43.png" alt="Share on Reddit" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Technorati" style="margin-right:5px;" href="http://technorati.com/faves?sub=favthis&amp;amp;add=http%3A//www.allbuttonspressed.com/blog/django/redis-backend-for-django-nonrel-in-development"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/technorati-1fa5656a85357eba71c018c718a7b36561a6f71c.png" alt="Share on Technorati" width="32" height="32" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="http://www.allbuttonspressed.com/blog/django/redis-backend-for-django-nonrel-in-development#disqus_thread"&gt;Leave a comment&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/AllButtonsPressed/~4/kMPgpCCyuaU" height="1" width="1"/&gt;</summary><feedburner:origLink>http://www.allbuttonspressed.com/blog/django/redis-backend-for-django-nonrel-in-development</feedburner:origLink></entry><entry><title>JOINs for NoSQL databases via django-dbindexer - First steps</title><link href="http://feedproxy.google.com/~r/AllButtonsPressed/~3/VZ5J1031l_8/joins-for-nosql-databases-via-django-dbindexer-first-steps" rel="alternate" /><updated>2011-02-01T07:27:00Z</updated><author><name>Thomas Wanschik</name></author><id>http://www.allbuttonspressed.com/blog/django/joins-for-nosql-databases-via-django-dbindexer-first-steps</id><summary type="html">&lt;div class="document"&gt;
&lt;p&gt;At the moment, we have a lot to do in terms of finishing our diploma thesis. However we're so excited about the early results of the currently refactored django-dbindexer that we couldn't hold back and keep django-dbindexer's simple JOIN support for non-relational databases a secret anymore. Yes, you didn't read wrongly, this post is about JOIN support for NoSQL databases! We'll show how to use in-memory JOINs and how to get JOINs working if the joined field's value doesn't change. So let's unpack our delayed Christmas present. ;)&lt;/p&gt;
&lt;div class="section" id="let-s-rock"&gt;
&lt;h2&gt;Let's rock!&lt;/h2&gt;
&lt;p&gt;Let's take the example of a photo-user relationship, just like in our post &lt;a class="reference external" href="http://www.allbuttonspressed.com/blog/django/2010/09/JOINs-via-denormalization-for-NoSQL-coders-Part-1-Intro"&gt;JOINs via denormalization for NoSQL coders, Part 1&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="color: #999988; font-style: italic"&gt;# photo/models.py:&lt;/span&gt;

&lt;span style="font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #555555"&gt;django.contrib.auth.models&lt;/span&gt; &lt;span style="font-weight: bold"&gt;import&lt;/span&gt; User
&lt;span style="font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #555555"&gt;django.db&lt;/span&gt; &lt;span style="font-weight: bold"&gt;import&lt;/span&gt; models

&lt;span style="font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #445588; font-weight: bold"&gt;Photo&lt;/span&gt;(models&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;Model):
    owner &lt;span style="font-weight: bold"&gt;=&lt;/span&gt; models&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;ForeignKey(User, null&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;&lt;span style="color: #999999"&gt;True&lt;/span&gt;, blank&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;&lt;span style="color: #999999"&gt;True&lt;/span&gt;)
    popularity &lt;span style="font-weight: bold"&gt;=&lt;/span&gt; models&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;IntegerField(choices&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;[(&lt;span style="color: #009999"&gt;0&lt;/span&gt;, &lt;span style="color: #bb8844"&gt;&amp;#39;low&amp;#39;&lt;/span&gt;), (&lt;span style="color: #009999"&gt;1&lt;/span&gt;, &lt;span style="color: #bb8844"&gt;&amp;#39;medium&amp;#39;&lt;/span&gt;), (&lt;span style="color: #009999"&gt;2&lt;/span&gt;, &lt;span style="color: #bb8844"&gt;&amp;#39;high&amp;#39;&lt;/span&gt;)],
                                     default&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;&lt;span style="color: #009999"&gt;0&lt;/span&gt;)
    published &lt;span style="font-weight: bold"&gt;=&lt;/span&gt; models&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;DateField(auto_now_add&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;&lt;span style="color: #999999"&gt;True&lt;/span&gt;)
    title &lt;span style="font-weight: bold"&gt;=&lt;/span&gt; models&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;CharField(max_length&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;&lt;span style="color: #009999"&gt;500&lt;/span&gt;)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here, each photo entity is linked to a user via a many-to-one relationship and has a popularity tag assigned to it. Additionally we store the date when the photo has been published and give the photo a title. Now let's say you want to get all of a user's photos. In order to do so, NoSQL databases force us to use ugly workarounds. One possible way is to first get the user (or better the corresponding primary key in order to avoid fetching the whole user entity out of the database) and thereafter the user's photos:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #990000; font-weight: bold"&gt;get_user_photos&lt;/span&gt;(first_name, last_name):
    user_pk &lt;span style="font-weight: bold"&gt;=&lt;/span&gt; User&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;objects&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;values(&lt;span style="color: #bb8844"&gt;&amp;#39;pk&amp;#39;&lt;/span&gt;)&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;get(first_name&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;first_name, last_name&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;last_name)
    &lt;span style="font-weight: bold"&gt;return&lt;/span&gt; Photo&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;objects&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;filter(owner__pk&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;user_pk)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;However with our reborn baby, django-dbindexer, you can just use Django's spanning relationship syntax (double underscores) to which you are used to:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #990000; font-weight: bold"&gt;get_user_photos&lt;/span&gt;(first_name, last_name):
    &lt;span style="font-weight: bold"&gt;return&lt;/span&gt; Photo&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;objects&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;filter(owner__first_name&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;first_name, owner__last_name&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;last_name)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Feels a lot more comfortable, right? :) To get this working all you have to do is to &lt;a class="reference external" href="http://www.allbuttonspressed.com/blog/django/2010/09/Get-SQL-features-on-NoSQL-with-django-dbindexer#installation"&gt;install the django-dbindexer&lt;/a&gt; and add the following index definition:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="color: #999988; font-style: italic"&gt;# photo/dbindexes.py:&lt;/span&gt;

&lt;span style="font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #555555"&gt;models&lt;/span&gt; &lt;span style="font-weight: bold"&gt;import&lt;/span&gt; Photo
&lt;span style="font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #555555"&gt;dbindexer.lookups&lt;/span&gt; &lt;span style="font-weight: bold"&gt;import&lt;/span&gt; StandardLookup
&lt;span style="font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #555555"&gt;dbindexer.api&lt;/span&gt; &lt;span style="font-weight: bold"&gt;import&lt;/span&gt; register_index

register_index(Photo, {&lt;span style="color: #bb8844"&gt;&amp;#39;owner__first_name&amp;#39;&lt;/span&gt;: StandardLookup(),
                       &lt;span style="color: #bb8844"&gt;&amp;#39;owner__last_name&amp;#39;&lt;/span&gt;: StandardLookup(),
})
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and one of the new join resolvers (&lt;code class="docutils literal"&gt;InMemoryJOINResolver&lt;/code&gt; or &lt;code class="docutils literal"&gt;ConstantFieldJOINResolver&lt;/code&gt;) to &lt;code class="docutils literal"&gt;DBINDEXER_BACKENDS&lt;/code&gt; in your settings:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="color: #999988; font-style: italic"&gt;# settings.py:&lt;/span&gt;

DBINDEXER_BACKENDS &lt;span style="font-weight: bold"&gt;=&lt;/span&gt; (
    &lt;span style="color: #bb8844"&gt;&amp;#39;dbindexer.backends.BaseResolver&amp;#39;&lt;/span&gt;,
    &lt;span style="color: #bb8844"&gt;&amp;#39;dbindexer.backends.FKNullFix&amp;#39;&lt;/span&gt;,
    &lt;span style="color: #bb8844"&gt;&amp;#39;dbindexer.backends.InMemoryJOINResolver&amp;#39;&lt;/span&gt;,
)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That's it. django-dbindexer will handle anything else magically for you. :) Under the hook the refactored django-dbindexer now uses backends to resolve lookups. The &lt;code class="docutils literal"&gt;BaseResolver&lt;/code&gt; is responsible for resolving lookups like &lt;code class="docutils literal"&gt;__iexact&lt;/code&gt; or &lt;code class="docutils literal"&gt;__regex&lt;/code&gt; for example and the &lt;code class="docutils literal"&gt;FKNullFix&lt;/code&gt; backend will make &lt;code class="docutils literal"&gt;__isnull&lt;/code&gt; queries work correctly on &lt;code class="docutils literal"&gt;ForeignKey&lt;/code&gt;. In this example we choose the &lt;code class="docutils literal"&gt;InMemoryJOINResolver&lt;/code&gt; which is used to resolve JOINs in-memory, just like we did manually in the example above. On the other hand, the &lt;code class="docutils literal"&gt;ConstantFieldJOINResolver&lt;/code&gt; would denormalize the user's &lt;code class="docutils literal"&gt;first_name&lt;/code&gt; and &lt;code class="docutils literal"&gt;last_name&lt;/code&gt; into the &lt;code class="docutils literal"&gt;Photo&lt;/code&gt; model and use these denormalized properties to execute the query. Anything described in &lt;a class="reference external" href="http://www.allbuttonspressed.com/blog/django/2010/09/JOINs-via-denormalization-for-NoSQL-coders-Part-1-Intro"&gt;JOINs via denormalization for NoSQL coders, Part 1&lt;/a&gt; is then done automatically by the &lt;code class="docutils literal"&gt;ConstantFieldJOINResolver&lt;/code&gt; for you. :)&lt;/p&gt;
&lt;p&gt;JOINs aren't limited to simple examples like the one above. Of course you can combine a query with filters on the &lt;code class="docutils literal"&gt;Photo&lt;/code&gt; model:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #990000; font-weight: bold"&gt;get_popular_user_photos&lt;/span&gt;(first_name, last_name):
    &lt;span style="font-weight: bold"&gt;return&lt;/span&gt; Photo&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;objects&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;filter(popularity&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;&lt;span style="color: #009999"&gt;2&lt;/span&gt;, owner__first_name&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;first_name,
                                owner__last_name&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;last_name)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can even span filters over multiple relationships if you have more complex models, just use multiple double underscores. If the photo model contains a &lt;code class="docutils literal"&gt;ForeignKey&lt;/code&gt; to a group which itself contains a &lt;code class="docutils literal"&gt;ForeignKey&lt;/code&gt; to a creator, a query getting all photos whose group creator is a specific user would look like this:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #990000; font-weight: bold"&gt;get_creators_groups_photos&lt;/span&gt;(first_name, last_name):
    &lt;span style="font-weight: bold"&gt;return&lt;/span&gt; Photo&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;objects&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;filter(group__creator__first_name&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;first_name,
                                group__creator__last_name&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;last_name)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note that this query can return photos belonging to more than just one group because a user could have created multiple groups.&lt;/p&gt;
&lt;p&gt;And as a nice side effect of the refactoring process, you can combine JOINs with other index definitions like an &lt;code class="docutils literal"&gt;__iexact&lt;/code&gt; lookup on the user's &lt;code class="docutils literal"&gt;first_name&lt;/code&gt; and &lt;code class="docutils literal"&gt;last_name&lt;/code&gt; in combination  with a &lt;code class="docutils literal"&gt;__month&lt;/code&gt; lookup on &lt;code class="docutils literal"&gt;published&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #990000; font-weight: bold"&gt;get_user_photos_created_in_month&lt;/span&gt;(first_name, last_name, month):
    &lt;span style="font-weight: bold"&gt;return&lt;/span&gt; Photo&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;objects&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;filter(popularity&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;&lt;span style="color: #009999"&gt;2&lt;/span&gt;, published__month&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;month,
                                owner__first_name__iexact&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;first_name,
                                owner__last_name__iexact&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;last_name)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Just add the needed index definitions to your index module:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="color: #999988; font-style: italic"&gt;# photo/dbindexes.py:&lt;/span&gt;

&lt;span style="font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #555555"&gt;models&lt;/span&gt; &lt;span style="font-weight: bold"&gt;import&lt;/span&gt; Photo
&lt;span style="font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #555555"&gt;dbindexer.lookups&lt;/span&gt; &lt;span style="font-weight: bold"&gt;import&lt;/span&gt; StandardLookup
&lt;span style="font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #555555"&gt;dbindexer.api&lt;/span&gt; &lt;span style="font-weight: bold"&gt;import&lt;/span&gt; register_index

register_index(Photo, {&lt;span style="color: #bb8844"&gt;&amp;#39;owner__first_name&amp;#39;&lt;/span&gt;: (StandardLookup(), &lt;span style="color: #bb8844"&gt;&amp;#39;iexact&amp;#39;&lt;/span&gt;),
                       &lt;span style="color: #bb8844"&gt;&amp;#39;owner__last_name&amp;#39;&lt;/span&gt;: (StandardLookup(), &lt;span style="color: #bb8844"&gt;&amp;#39;iexact&amp;#39;&lt;/span&gt;),
                       &lt;span style="color: #bb8844"&gt;&amp;#39;published&amp;#39;&lt;/span&gt;: &lt;span style="color: #bb8844"&gt;&amp;#39;month&amp;#39;&lt;/span&gt;,
})
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It couldn't be easier than that, right? :) First we hardly believed our own eyes when we tested JOINs on App Engine on production but it really works. :P Just give it a try and &lt;a class="reference external" href="https://bitbucket.org/twanschik/django-dbindexer-testapp/overview"&gt;download the django-dbindexer-testapp&lt;/a&gt;. :)  If you wanna see even more complex examples then take a look at the &lt;a class="reference external" href="https://bitbucket.org/wkornewald/django-dbindexer/src/59b2dd9d6dca/dbindexer/tests.py"&gt;unit tests&lt;/a&gt; of django-dbindexer.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="inmemoryjoinresolver-or-constantfieldjoinresolver"&gt;
&lt;h2&gt;&lt;code class="docutils literal"&gt;InMemoryJOINResolver&lt;/code&gt; or &lt;code class="docutils literal"&gt;ConstantFieldJOINResolver&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;At the moment you have to choose globally between either the &lt;code class="docutils literal"&gt;InMemoryJOINResolver&lt;/code&gt; or the &lt;code class="docutils literal"&gt;ConstantFieldJOINResolver&lt;/code&gt; in you settings. All queries involving JOINs will then use the join resolver chosen. This will be changed in a future release of django-dbindexer. For now, you may wonder which join resolver to use. In general, in-memory JOINs are useful for simple cases for which you know that you get only a small set of entities on the joined side i.e. the &lt;code class="docutils literal"&gt;User&lt;/code&gt; side. This includes examples like the one above as well as one-to-one relationships. If you know that you have to deal with larger sets of entities on the joined side you have to use the &lt;code class="docutils literal"&gt;ConstantFieldJOINResolver&lt;/code&gt; because in-memory JOINs don't scale in such cases.&lt;/p&gt;
&lt;p&gt;Currently you can't combine in-memory JOINs with OR-queries and exclude-queries. Anything else should work. In-memory JOINs will try to query as efficiently as possible. On App Engine for example dbindexer will batch-get and use efficient &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;values('pk')&lt;/span&gt;&lt;/code&gt; filters (which become a &lt;code class="docutils literal"&gt;keys_only&lt;/code&gt; filter) where possible. Additionally,  fields on the joined side won't be created for &lt;code class="docutils literal"&gt;StandardLookup&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code class="docutils literal"&gt;ConstantFieldJOINResolver&lt;/code&gt; is more efficient than the &lt;code class="docutils literal"&gt;InMemoryJOINResolver&lt;/code&gt; and doesn't have limitations neither for OR-queries nor for exclude-queries i.e. you can execute the following query for example:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="font-weight: bold"&gt;return&lt;/span&gt; Photo&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;objects&lt;span style="font-weight: bold"&gt;.&lt;/span&gt;exclude(Q(owner__first_name__iexact&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;&lt;span style="color: #bb8844"&gt;&amp;#39;Danzo&amp;#39;&lt;/span&gt;) &lt;span style="font-weight: bold"&gt;|&lt;/span&gt;
                             Q(owner__last_name__iexact&lt;span style="font-weight: bold"&gt;=&lt;/span&gt;&lt;span style="color: #bb8844"&gt;&amp;#39;Shimura&amp;#39;&lt;/span&gt;))
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Additionally it is useful for more than one-to-one-relations. However, at the moment it only works for constant fields i.e. if the user's &lt;code class="docutils literal"&gt;first_name&lt;/code&gt; and the &lt;code class="docutils literal"&gt;last_name&lt;/code&gt; doesn't change. In the future we will add support for non-constant values too.&lt;/p&gt;
&lt;p&gt;For both resolvers, JOINs only work in the &lt;code class="docutils literal"&gt;ForeignKey&lt;/code&gt; direction. No reverse lookups supported at the moment. :(&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="expressive-nosql-beyond-scalability"&gt;
&lt;h2&gt;Expressive NoSQL - Beyond scalability&lt;/h2&gt;
&lt;p&gt;So the first steps towards JOIN support for NoSQL databases are done. We plan to extend the backends to handle JOINs for non-constant fields in a scaleable way, just like described in our &lt;a class="reference external" href="http://www.allbuttonspressed.com/blog/django/2010/09/JOINs-via-denormalization-for-NoSQL-coders-Part-1-Intro"&gt;JOINs via denormalization for NoSQL coders series&lt;/a&gt;. Additionally, django-dbindexer's API will be extended to allow to specify a join resolver on a per field/filter bases. With the refactored django-dbindexer and its backend system even support for aggregates becomes possible. This will allow you to write complex queries in a few minutes instead of hours (including unit tests and debugging ;). No more hand-written denormalization and map/reduce and aggregate code. Just tell the indexer what you want to do and it'll handle it for you in a way you specify (via a backend)! Developers still have to think about how to design their code. However, django-dbindexer allows them to use Django's expressive ORM and thus being much more productive. Another possible extension is a backend for &lt;a class="reference external" href="http://www.allbuttonspressed.com/projects/nonrel-search"&gt;nonrel-search&lt;/a&gt; which could be used in order to add full-text search functionality for the admin interface. :) It's hard to think of something that would not be possible! :P If you want to help with the next exciting phase of development you can drop us a mail: &lt;a class="reference external" href="http://groups.google.com/group/django-non-relational"&gt;http://groups.google.com/group/django-non-relational&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="static-share-buttons" style="overflow:hidden"&gt;
&lt;a class="share-button" target="_blank" title="Tweet this!" style="margin-right:5px;" href="http://twitter.com/share?text=JOINs%20for%20NoSQL%20databases%20via%20django-dbindexer%20-%20First%20steps&amp;amp;url=http%3A//www.allbuttonspressed.com/blog/django/joins-for-nosql-databases-via-django-dbindexer-first-steps&amp;amp;via=wkornewald"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/twitter-ba4f7679fb49cd4eb99cae4267d48d23c81137d4.png" alt="Tweet this!" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Facebook" style="margin-right:5px;" href="http://www.facebook.com/share.php?u=http%3A//www.allbuttonspressed.com/blog/django/joins-for-nosql-databases-via-django-dbindexer-first-steps&amp;amp;t=JOINs%20for%20NoSQL%20databases%20via%20django-dbindexer%20-%20First%20steps"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/facebook-4b8233c3eb59d633eacdc5dbd8b98be769b9386d.png" alt="Share on Facebook" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Email a friend" style="margin-right:5px;" href="http://feedburner.google.com/fb/a/emailFlare?itemTitle=JOINs%20for%20NoSQL%20databases%20via%20django-dbindexer%20-%20First%20steps&amp;amp;uri=http%3A//www.allbuttonspressed.com/blog/django/joins-for-nosql-databases-via-django-dbindexer-first-steps"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/email-e5a5556616278659d7761a5df6c58238d77ec47c.png" alt="Email a friend" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Delicious" style="margin-right:5px;" href="http://del.icio.us/post?url=http%3A//www.allbuttonspressed.com/blog/django/joins-for-nosql-databases-via-django-dbindexer-first-steps&amp;amp;title=JOINs%20for%20NoSQL%20databases%20via%20django-dbindexer%20-%20First%20steps"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/delicious-400c2ae7e6871479f4bb34a4f8f476784a0992bf.png" alt="Share on Delicious" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on StumbleUpon" style="margin-right:5px;" href="http://www.stumbleupon.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/joins-for-nosql-databases-via-django-dbindexer-first-steps&amp;amp;title=JOINs%20for%20NoSQL%20databases%20via%20django-dbindexer%20-%20First%20steps"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/stumbleupon-b725d91b84d24fa42787a8c79aa2695c5e5fb288.png" alt="Share on StumbleUpon" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Digg" style="margin-right:5px;" href="http://digg.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/joins-for-nosql-databases-via-django-dbindexer-first-steps&amp;amp;title=JOINs%20for%20NoSQL%20databases%20via%20django-dbindexer%20-%20First%20steps"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/digg-8ada117a5610042c9fd592d3f4d3d20be242334d.png" alt="Share on Digg" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Reddit" style="margin-right:5px;" href="http://reddit.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/joins-for-nosql-databases-via-django-dbindexer-first-steps&amp;amp;title=JOINs%20for%20NoSQL%20databases%20via%20django-dbindexer%20-%20First%20steps"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/reddit-f2c9d5792319c1a340054f310f4646642a48fe43.png" alt="Share on Reddit" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Technorati" style="margin-right:5px;" href="http://technorati.com/faves?sub=favthis&amp;amp;add=http%3A//www.allbuttonspressed.com/blog/django/joins-for-nosql-databases-via-django-dbindexer-first-steps"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/technorati-1fa5656a85357eba71c018c718a7b36561a6f71c.png" alt="Share on Technorati" width="32" height="32" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="http://www.allbuttonspressed.com/blog/django/joins-for-nosql-databases-via-django-dbindexer-first-steps#disqus_thread"&gt;Leave a comment&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/AllButtonsPressed/~4/VZ5J1031l_8" height="1" width="1"/&gt;</summary><feedburner:origLink>http://www.allbuttonspressed.com/blog/django/joins-for-nosql-databases-via-django-dbindexer-first-steps</feedburner:origLink></entry><entry><title>Merry Christmas</title><link href="http://feedproxy.google.com/~r/AllButtonsPressed/~3/vrmUn7jL97E/Merry-Christmas" rel="alternate" /><updated>2010-12-25T15:41:00Z</updated><author><name>Waldemar Kornewald</name></author><id>http://www.allbuttonspressed.com/blog/django/2010/12/Merry-Christmas</id><summary type="html">&lt;div class="document"&gt;
&lt;p&gt;Merry Christmas and a happy 2011, everyone! Actually we wanted to finish a little present, but December was just too full with other stuff. Looks like Thomas will have to play Santa in January. ;)&lt;/p&gt;
&lt;p&gt;This blog started more or less one year ago. Back then we still were on a blogspot subdomain. A few months later &lt;a class="reference external" href="http://www.allbuttonspressed.com/projects/django-nonrel"&gt;Django-nonrel&lt;/a&gt; was ready for hosting a simple site, so we moved everything on a custom domain (allbuttonspressed.com) hosted on App Engine with Django-nonrel. Now this blog has more than 700 subscribers and we really need to get some feedback from you, so we can improve our articles in 2011. Please help us by filling out &lt;a class="reference external" href="https://spreadsheets.google.com/viewform?formkey=dDNKZ254T1pmWVRFTnhnSDZUZVBLQ2c6MQ"&gt;this short survey&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thanks a lot! Have a nice Christmas and a successful 2011.&lt;/p&gt;
&lt;/div&gt;

&lt;div class="static-share-buttons" style="overflow:hidden"&gt;
&lt;a class="share-button" target="_blank" title="Tweet this!" style="margin-right:5px;" href="http://twitter.com/share?text=Merry%20Christmas&amp;amp;url=http%3A//www.allbuttonspressed.com/blog/django/2010/12/Merry-Christmas&amp;amp;via=wkornewald"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/twitter-ba4f7679fb49cd4eb99cae4267d48d23c81137d4.png" alt="Tweet this!" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Facebook" style="margin-right:5px;" href="http://www.facebook.com/share.php?u=http%3A//www.allbuttonspressed.com/blog/django/2010/12/Merry-Christmas&amp;amp;t=Merry%20Christmas"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/facebook-4b8233c3eb59d633eacdc5dbd8b98be769b9386d.png" alt="Share on Facebook" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Email a friend" style="margin-right:5px;" href="http://feedburner.google.com/fb/a/emailFlare?itemTitle=Merry%20Christmas&amp;amp;uri=http%3A//www.allbuttonspressed.com/blog/django/2010/12/Merry-Christmas"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/email-e5a5556616278659d7761a5df6c58238d77ec47c.png" alt="Email a friend" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Delicious" style="margin-right:5px;" href="http://del.icio.us/post?url=http%3A//www.allbuttonspressed.com/blog/django/2010/12/Merry-Christmas&amp;amp;title=Merry%20Christmas"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/delicious-400c2ae7e6871479f4bb34a4f8f476784a0992bf.png" alt="Share on Delicious" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on StumbleUpon" style="margin-right:5px;" href="http://www.stumbleupon.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/2010/12/Merry-Christmas&amp;amp;title=Merry%20Christmas"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/stumbleupon-b725d91b84d24fa42787a8c79aa2695c5e5fb288.png" alt="Share on StumbleUpon" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Digg" style="margin-right:5px;" href="http://digg.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/2010/12/Merry-Christmas&amp;amp;title=Merry%20Christmas"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/digg-8ada117a5610042c9fd592d3f4d3d20be242334d.png" alt="Share on Digg" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Reddit" style="margin-right:5px;" href="http://reddit.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/2010/12/Merry-Christmas&amp;amp;title=Merry%20Christmas"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/reddit-f2c9d5792319c1a340054f310f4646642a48fe43.png" alt="Share on Reddit" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Technorati" style="margin-right:5px;" href="http://technorati.com/faves?sub=favthis&amp;amp;add=http%3A//www.allbuttonspressed.com/blog/django/2010/12/Merry-Christmas"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/technorati-1fa5656a85357eba71c018c718a7b36561a6f71c.png" alt="Share on Technorati" width="32" height="32" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="http://www.allbuttonspressed.com/blog/django/2010/12/Merry-Christmas#disqus_thread"&gt;Leave a comment&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/AllButtonsPressed/~4/vrmUn7jL97E" height="1" width="1"/&gt;</summary><feedburner:origLink>http://www.allbuttonspressed.com/blog/django/2010/12/Merry-Christmas</feedburner:origLink></entry><entry><title>Porting from App Engine's webapp to django-nonrel</title><link href="http://feedproxy.google.com/~r/AllButtonsPressed/~3/mGOiN0F14pg/Porting-from-App-Engine-s-webapp-to-django-nonrel" rel="alternate" /><updated>2010-11-30T17:19:00Z</updated><author><name>Thomas Wanschik</name></author><id>http://www.allbuttonspressed.com/blog/django/2010/11/Porting-from-App-Engine-s-webapp-to-django-nonrel</id><summary type="html">&lt;div class="document"&gt;
&lt;p&gt;Hey, you may have noticed it already, we've finally finished our top-secret article called &lt;a class="reference external" href="http://code.google.com/appengine/articles/django-nonrel.html"&gt;Running Pure Django Projects on Google App Engine&lt;/a&gt;  :). We thank Wesley Chun for his help, expertise and time so we could get the article published on &lt;a class="reference external" href="http://code.google.com/intl/de-DE/appengine/articles/"&gt;App Engine's articles section&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Summarized the article goes through several steps explaining in depth how to port an App Engine webapp app over to django-nonrel with some additional notes on useful Django features. At the end of the article we summarize the advantages of using django-nonrel over other approaches and what awaits you in the future. But enough words, just read the article :)&lt;/p&gt;
&lt;/div&gt;

&lt;div class="static-share-buttons" style="overflow:hidden"&gt;
&lt;a class="share-button" target="_blank" title="Tweet this!" style="margin-right:5px;" href="http://twitter.com/share?text=Porting%20from%20App%20Engine%27s%20webapp%20to%20django-nonrel&amp;amp;url=http%3A//www.allbuttonspressed.com/blog/django/2010/11/Porting-from-App-Engine-s-webapp-to-django-nonrel&amp;amp;via=wkornewald"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/twitter-ba4f7679fb49cd4eb99cae4267d48d23c81137d4.png" alt="Tweet this!" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Facebook" style="margin-right:5px;" href="http://www.facebook.com/share.php?u=http%3A//www.allbuttonspressed.com/blog/django/2010/11/Porting-from-App-Engine-s-webapp-to-django-nonrel&amp;amp;t=Porting%20from%20App%20Engine%27s%20webapp%20to%20django-nonrel"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/facebook-4b8233c3eb59d633eacdc5dbd8b98be769b9386d.png" alt="Share on Facebook" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Email a friend" style="margin-right:5px;" href="http://feedburner.google.com/fb/a/emailFlare?itemTitle=Porting%20from%20App%20Engine%27s%20webapp%20to%20django-nonrel&amp;amp;uri=http%3A//www.allbuttonspressed.com/blog/django/2010/11/Porting-from-App-Engine-s-webapp-to-django-nonrel"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/email-e5a5556616278659d7761a5df6c58238d77ec47c.png" alt="Email a friend" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Delicious" style="margin-right:5px;" href="http://del.icio.us/post?url=http%3A//www.allbuttonspressed.com/blog/django/2010/11/Porting-from-App-Engine-s-webapp-to-django-nonrel&amp;amp;title=Porting%20from%20App%20Engine%27s%20webapp%20to%20django-nonrel"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/delicious-400c2ae7e6871479f4bb34a4f8f476784a0992bf.png" alt="Share on Delicious" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on StumbleUpon" style="margin-right:5px;" href="http://www.stumbleupon.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/2010/11/Porting-from-App-Engine-s-webapp-to-django-nonrel&amp;amp;title=Porting%20from%20App%20Engine%27s%20webapp%20to%20django-nonrel"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/stumbleupon-b725d91b84d24fa42787a8c79aa2695c5e5fb288.png" alt="Share on StumbleUpon" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Digg" style="margin-right:5px;" href="http://digg.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/2010/11/Porting-from-App-Engine-s-webapp-to-django-nonrel&amp;amp;title=Porting%20from%20App%20Engine%27s%20webapp%20to%20django-nonrel"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/digg-8ada117a5610042c9fd592d3f4d3d20be242334d.png" alt="Share on Digg" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Reddit" style="margin-right:5px;" href="http://reddit.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/2010/11/Porting-from-App-Engine-s-webapp-to-django-nonrel&amp;amp;title=Porting%20from%20App%20Engine%27s%20webapp%20to%20django-nonrel"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/reddit-f2c9d5792319c1a340054f310f4646642a48fe43.png" alt="Share on Reddit" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Technorati" style="margin-right:5px;" href="http://technorati.com/faves?sub=favthis&amp;amp;add=http%3A//www.allbuttonspressed.com/blog/django/2010/11/Porting-from-App-Engine-s-webapp-to-django-nonrel"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/technorati-1fa5656a85357eba71c018c718a7b36561a6f71c.png" alt="Share on Technorati" width="32" height="32" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="http://www.allbuttonspressed.com/blog/django/2010/11/Porting-from-App-Engine-s-webapp-to-django-nonrel#disqus_thread"&gt;Leave a comment&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/AllButtonsPressed/~4/mGOiN0F14pg" height="1" width="1"/&gt;</summary><feedburner:origLink>http://www.allbuttonspressed.com/blog/django/2010/11/Porting-from-App-Engine-s-webapp-to-django-nonrel</feedburner:origLink></entry><entry><title>HTML5 offline manifests with django-mediagenerator</title><link href="http://feedproxy.google.com/~r/AllButtonsPressed/~3/x9WteRktdno/HTML5-offline-manifests-with-django-mediagenerator" rel="alternate" /><updated>2010-11-23T15:17:00Z</updated><author><name>Waldemar Kornewald</name></author><id>http://www.allbuttonspressed.com/blog/django/2010/11/HTML5-offline-manifests-with-django-mediagenerator</id><summary type="html">&lt;div class="document"&gt;
&lt;p&gt;This is actually part 3 of our &lt;a class="reference external" href="/projects/django-mediagenerator"&gt;django-mediagenerator&lt;/a&gt; Python canvas app series (see &lt;a class="reference external" href="/blog/django/2010/11/Offline-HTML5-canvas-app-in-Python-with-django-mediagenerator-Part-1-pyjs"&gt;part 1&lt;/a&gt; and &lt;a class="reference external" href="/blog/django/2010/11/Offline-HTML5-canvas-app-in-Python-with-django-mediagenerator-Part-2-Drawing"&gt;part 2&lt;/a&gt;), but since it has nothing to do with client-side Python we name it differently. In this part you'll see how to make your web app load without an Internet connection. HTML5 supports offline web apps through &lt;a class="reference external" href="http://www.w3.org/TR/html5/offline.html"&gt;manifest&lt;/a&gt; files.&lt;/p&gt;
&lt;div class="section" id="manifest-files"&gt;
&lt;h2&gt;Manifest files&lt;/h2&gt;
&lt;p&gt;First here's some background, so you know what a manifest file is. A manifest file is really simple. In its most basic form it lists the URLs of the files that should be cached. Here's an &lt;code class="docutils literal"&gt;example.manifest&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;CACHE MANIFEST
/media/main.css
/media/main.js
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The first line is always &lt;code class="docutils literal"&gt;CACHE MANIFEST&lt;/code&gt;. The next lines can list the files that should be cached. In this case we've added the &lt;code class="docutils literal"&gt;main.css&lt;/code&gt; and &lt;code class="docutils literal"&gt;main.js&lt;/code&gt; bundles. Additionally, the main HTML file which includes the manifest is cached, automatically. You can include the manifest in the &lt;code class="docutils literal"&gt;&amp;lt;html&amp;gt;&lt;/code&gt; tag:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="color: #000080"&gt;&amp;lt;html&lt;/span&gt; &lt;span style="color: #008080"&gt;manifest=&lt;/span&gt;&lt;span style="color: #bb8844"&gt;&amp;quot;example.manifest&amp;quot;&lt;/span&gt;&lt;span style="color: #000080"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When the browser sees this it loads the manifest and adds the current HTML and manifest file and all files listed in the manifest to the cache. The next time you visit the page the browser will try to load the manifest file from your server and compare it to the cached version. If the content of the manifest file hasn't changed the browser just loads all files from the cache. If the content of the manifest file has changed the browser refreshes its cache.&lt;/p&gt;
&lt;p&gt;This is important, so I repeat it: The browser updates its cache only when the &lt;strong&gt;content&lt;/strong&gt; of the &lt;strong&gt;manifest&lt;/strong&gt; file is modified. Changes to your JavaScript, CSS, and image files will go unnoticed if the manifest file is not changed! That's exactly where things become annoying. Imagine you've changed the &lt;code class="docutils literal"&gt;main.js&lt;/code&gt; file. Now you have to change your manifest file, too. One possibility is to add a comment to their manifest file which represents the current version number:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;CACHE MANIFEST
# version 2
/media/main.css
/media/main.js
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Whenever you change something in your JS or CSS or image files you have to increment the version number, manually. That's not really nice.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="django-mediagenerator-to-the-rescue"&gt;
&lt;h2&gt;django-mediagenerator to the rescue&lt;/h2&gt;
&lt;p&gt;This is where the media generator comes in. It automatically modifies the manifest file whenever your media files are changed. Since media files are versioned automatically by django-mediagenerator the version hash in the file name serves as a natural and automatic solution to our problem. With the media generator a manifest file could look like this:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;CACHE MANIFEST
/media/main-bf1e7dfbd511baf660e57a1f36048750f1ee660f.css
/media/main-fb16702a27fc6c8073aa4df0b0b5b3dd8057cc12.js
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Whenever you change your media files the version hash of the affected files becomes different and thus the manifest file changes automatically, too.&lt;/p&gt;
&lt;p&gt;Now how do we tell &lt;a class="reference external" href="/projects/django-mediagenerator"&gt;django-mediagenerator&lt;/a&gt; to create such a manifest file? Just add this to your &lt;code class="docutils literal"&gt;settings.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;OFFLINE_MANIFEST &lt;span style="font-weight: bold"&gt;=&lt;/span&gt; &lt;span style="color: #bb8844"&gt;&amp;#39;webapp.manifest&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With this simple snippet the media generator will create a manifest file called &lt;code class="docutils literal"&gt;webapp.manifest&lt;/code&gt;. However, the manifest file will contain &lt;strong&gt;all&lt;/strong&gt; of the assets in your project. In other words, the whole &lt;code class="docutils literal"&gt;_generated_media&lt;/code&gt; folder will be listed in the manifest file.&lt;/p&gt;
&lt;p&gt;Often you only want specific files to be cached. You can do that by specifying a list of regular expressions matching path names (relative to your media directories, exactly like in &lt;code class="docutils literal"&gt;MEDIA_BUNDLES&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;OFFLINE_MANIFEST &lt;span style="font-weight: bold"&gt;=&lt;/span&gt; {
    &lt;span style="color: #bb8844"&gt;&amp;#39;webapp.manifest&amp;#39;&lt;/span&gt;: {
        &lt;span style="color: #bb8844"&gt;&amp;#39;cache&amp;#39;&lt;/span&gt;: (
            &lt;span style="color: #bb8844"&gt;r&amp;#39;main\.css&amp;#39;&lt;/span&gt;,
            &lt;span style="color: #bb8844"&gt;r&amp;#39;main\.js&amp;#39;&lt;/span&gt;,
            &lt;span style="color: #bb8844"&gt;r&amp;#39;webapp/img/.*&amp;#39;&lt;/span&gt;,
        ),
        &lt;span style="color: #bb8844"&gt;&amp;#39;exclude&amp;#39;&lt;/span&gt;: (
            &lt;span style="color: #bb8844"&gt;r&amp;#39;webapp/img/online-only/.*&amp;#39;&lt;/span&gt;,
        )
    },
}
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here we've added the &lt;code class="docutils literal"&gt;main.css&lt;/code&gt; and &lt;code class="docutils literal"&gt;main.js&lt;/code&gt; bundles and all files under the &lt;code class="docutils literal"&gt;webapp/img/&lt;/code&gt; folder, except for files under &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;webapp/img/online-only/&lt;/span&gt;&lt;/code&gt;. Also, you might have guessed it already: You can create multiple manifest files this way. Just add more entries to the &lt;code class="docutils literal"&gt;OFFLINE_MANIFEST&lt;/code&gt; dict.&lt;/p&gt;
&lt;p&gt;Finally, we also have to include the manifest file in our template:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="color: #999999; font-weight: bold"&gt;{%&lt;/span&gt; &lt;span style="font-weight: bold"&gt;load&lt;/span&gt; &lt;span style="color: #008080"&gt;media&lt;/span&gt; &lt;span style="color: #999999; font-weight: bold"&gt;%}&lt;/span&gt;
&amp;lt;html manifest=&amp;quot;&lt;span style="color: #999999; font-weight: bold"&gt;{%&lt;/span&gt; &lt;span style="font-weight: bold"&gt;media_url&lt;/span&gt; &lt;span style="color: #bb8844"&gt;&amp;#39;webapp.manifest&amp;#39;&lt;/span&gt; &lt;span style="color: #999999; font-weight: bold"&gt;%}&lt;/span&gt;&amp;quot;&amp;gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Manifest files actually provide more features than this. For example, you can also specify &lt;code class="docutils literal"&gt;FALLBACK&lt;/code&gt; handlers in case there is no Internet connection. In the following example the &amp;quot;/offline.html&amp;quot; page will be displayed for resources which can't be reached while offline:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;OFFLINE_MANIFEST &lt;span style="font-weight: bold"&gt;=&lt;/span&gt; {
    &lt;span style="color: #bb8844"&gt;&amp;#39;webapp.manifest&amp;#39;&lt;/span&gt;: {
        &lt;span style="color: #bb8844"&gt;&amp;#39;cache&amp;#39;&lt;/span&gt;: (&lt;span style="font-weight: bold"&gt;...&lt;/span&gt;),
        &lt;span style="color: #bb8844"&gt;&amp;#39;fallback&amp;#39;&lt;/span&gt;: {
            &lt;span style="color: #bb8844"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;: &lt;span style="color: #bb8844"&gt;&amp;#39;/offline.html&amp;#39;&lt;/span&gt;,
        },
    },
}
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here &lt;code class="docutils literal"&gt;/&lt;/code&gt; is a pattern that matches all pages. You can also define &lt;code class="docutils literal"&gt;NETWORK&lt;/code&gt; entries which specify allowed URLs that can be accessed even though they're not cached:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;OFFLINE_MANIFEST &lt;span style="font-weight: bold"&gt;=&lt;/span&gt; {
    &lt;span style="color: #bb8844"&gt;&amp;#39;webapp.manifest&amp;#39;&lt;/span&gt;: {
        &lt;span style="color: #bb8844"&gt;&amp;#39;cache&amp;#39;&lt;/span&gt;: (&lt;span style="font-weight: bold"&gt;...&lt;/span&gt;),
        &lt;span style="color: #bb8844"&gt;&amp;#39;network&amp;#39;&lt;/span&gt;: (
            &lt;span style="color: #bb8844"&gt;&amp;#39;*&amp;#39;&lt;/span&gt;,
        ),
    },
}
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here &lt;code class="docutils literal"&gt;*&lt;/code&gt; is a wildcard that allows to access any URL. If you just had an empty &lt;code class="docutils literal"&gt;NETWORK&lt;/code&gt; section you wouldn't be able to load uncached files, even when you're online (however, not all browsers are so strict).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="serving-manifest-files"&gt;
&lt;h2&gt;Serving manifest files&lt;/h2&gt;
&lt;p&gt;Manifest files should be served with the MIME type &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;text/cache-manifest&lt;/span&gt;&lt;/code&gt;. Also it's &lt;strong&gt;critical&lt;/strong&gt; that you disable HTTP caching for manifest files! Otherwise the browser will &lt;strong&gt;never&lt;/strong&gt; load a new version of your app because it always loads the cached manifest! Make sure that you've configured your web server correctly.&lt;/p&gt;
&lt;p&gt;As an example, on App Engine you'd configure your &lt;code class="docutils literal"&gt;app.yaml&lt;/code&gt; like this:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;handlers:
- url: /media/(.*\.manifest)
  static_files: _generated_media/\1
  mime_type: text/cache-manifest
  upload: _generated_media/(.*\.manifest)
  expiration: &lt;span style="color: #bb8844"&gt;&amp;#39;0&amp;#39;&lt;/span&gt;

- url: /media
  static_dir: _generated_media/
  expiration: &lt;span style="color: #bb8844"&gt;&amp;#39;365d&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here we first catch all manifest files and serve them with an expiration of &amp;quot;0&amp;quot; and the correct MIME type. The normal &lt;code class="docutils literal"&gt;/media&lt;/code&gt; handler must be installed &lt;strong&gt;after&lt;/strong&gt; the manifest handler.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="like-a-native-ipad-iphone-app"&gt;
&lt;h2&gt;Like a native iPad/iPhone app&lt;/h2&gt;
&lt;p&gt;Offline-capable web apps have a nice extra advantage: We can put them on the iPad's/iPhone's home screen, so they appear exactly like native apps! All browser bars will disappear and your whole web app will be full-screen (except for the top-most status bar which shows the current time and battery and network status). Just add the following to your template:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;&amp;lt;head&amp;gt;
&amp;lt;meta name=&amp;quot;apple-mobile-web-app-capable&amp;quot; content=&amp;quot;yes&amp;quot; /&amp;gt;
...
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now when you're in the browser you can tap on the &amp;quot;+&amp;quot; icon in the middle of the bottom toolbar (&lt;strong&gt;update:&lt;/strong&gt; I just updated to iOS 4.2.1 and the &amp;quot;+&amp;quot; icon got replaced with some other icon, but it's still in the middle of the bottom toolbar :) and select &amp;quot;Add to Home Screen&amp;quot;:&lt;/p&gt;
&lt;img alt="http://lh3.ggpht.com/_03uxRzJMadw/TOfkL5YEULI/AAAAAAAAAIo/sCOT_u4ymdQ/add-to-home-screen.png" src="http://lh3.ggpht.com/_03uxRzJMadw/TOfkL5YEULI/AAAAAAAAAIo/sCOT_u4ymdQ/add-to-home-screen.png" /&gt;
&lt;p&gt;Then you can enter the name of the home screen icon:&lt;/p&gt;
&lt;img alt="http://lh4.ggpht.com/_03uxRzJMadw/TOfkLpUSIeI/AAAAAAAAAIk/n3IZTgfZyIo/add-to-home.png" src="http://lh4.ggpht.com/_03uxRzJMadw/TOfkLpUSIeI/AAAAAAAAAIk/n3IZTgfZyIo/add-to-home.png" /&gt;
&lt;p&gt;Tapping &amp;quot;Add&amp;quot; will add an icon for your web app to the home screen:&lt;/p&gt;
&lt;img alt="http://lh3.ggpht.com/_03uxRzJMadw/TOfkMLPDyQI/AAAAAAAAAIw/qducGXp4DzE/app-on-home-screen.png" src="http://lh3.ggpht.com/_03uxRzJMadw/TOfkMLPDyQI/AAAAAAAAAIw/qducGXp4DzE/app-on-home-screen.png" /&gt;
&lt;p&gt;When you tap that icon the canvas demo app starts in full-screen:&lt;/p&gt;
&lt;img alt="http://lh5.ggpht.com/_03uxRzJMadw/TOfkLyiW0SI/AAAAAAAAAIs/lOIzhyI6BMQ/app.png" src="http://lh5.ggpht.com/_03uxRzJMadw/TOfkLyiW0SI/AAAAAAAAAIs/lOIzhyI6BMQ/app.png" /&gt;
&lt;p&gt;We can also specify an icon for your web app. For example, if your icon is in &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;img/app-icon.png&lt;/span&gt;&lt;/code&gt; you can add it like this:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="color: #999999; font-weight: bold"&gt;{%&lt;/span&gt; &lt;span style="font-weight: bold"&gt;load&lt;/span&gt; &lt;span style="color: #008080"&gt;media&lt;/span&gt; &lt;span style="color: #999999; font-weight: bold"&gt;%}&lt;/span&gt;
&amp;lt;head&amp;gt;
&amp;lt;link rel=&amp;quot;apple-touch-icon&amp;quot; href=&amp;quot;&lt;span style="color: #999999; font-weight: bold"&gt;{%&lt;/span&gt; &lt;span style="font-weight: bold"&gt;media_url&lt;/span&gt; &lt;span style="color: #bb8844"&gt;&amp;#39;img/app-icon.png&amp;#39;&lt;/span&gt; &lt;span style="color: #999999; font-weight: bold"&gt;%}&lt;/span&gt;&amp;quot; /&amp;gt;
...
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The image should measure 57x57 pixels.&lt;/p&gt;
&lt;p&gt;Finally, you can also add a startup image which is displayed while your app loads. The following snippet assumes that the startup image is in &lt;code class="docutils literal"&gt;img/startup.png&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight" style="padding: 5px; background-color: #F9F7ED; border: 1px solid #36393D;"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="color: #999999; font-weight: bold"&gt;{%&lt;/span&gt; &lt;span style="font-weight: bold"&gt;load&lt;/span&gt; &lt;span style="color: #008080"&gt;media&lt;/span&gt; &lt;span style="color: #999999; font-weight: bold"&gt;%}&lt;/span&gt;
&amp;lt;head&amp;gt;
&amp;lt;link rel=&amp;quot;apple-touch-startup-image&amp;quot; href=&amp;quot;&lt;span style="color: #999999; font-weight: bold"&gt;{%&lt;/span&gt; &lt;span style="font-weight: bold"&gt;media_url&lt;/span&gt; &lt;span style="color: #bb8844"&gt;&amp;#39;img/startup.png&amp;#39;&lt;/span&gt; &lt;span style="color: #999999; font-weight: bold"&gt;%}&lt;/span&gt;&amp;quot; /&amp;gt;
...
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The image dimensions should be 320x460 pixels and it should be in portrait orientation.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="summary"&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The manifest file just lists the files that should be cached&lt;/li&gt;
&lt;li&gt;Files are only reloaded if the manifest file's content has changed&lt;/li&gt;
&lt;li&gt;The manifest file must not be cached (!) or the browser will never reload anything&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="/projects/django-mediagenerator"&gt;django-mediagenerator&lt;/a&gt; automatically maintains the manifest file for you&lt;/li&gt;
&lt;li&gt;Offline web apps can appear like native apps on the iPad and iPhone&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bitbucket.org/wkornewald/offline-canvas-python-web-app/get/tip.zip"&gt;Download&lt;/a&gt; the latest canvas drawing app source which is now offline-capable&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you've seen in this post, it's very easy to make your web app offline-capable with &lt;a class="reference external" href="/projects/django-mediagenerator"&gt;django-mediagenerator&lt;/a&gt;. This is also the foundation for making your app look like a native app on the iPhone and iPad. Offline web apps open up exciting possibilities and allow you to become independent of Apple's slow approval processes for the app store and the iOS platform in general because web apps can run on Android, webOS, and many other mobile platforms. It's also possible to write a little wrapper for the App Store which just opens Safari with your website. That way users can still find your app in the App Store (in addition to the web).&lt;/p&gt;
&lt;p&gt;The next time you want to write a native app for the iOS platform, consider making a web app, instead (unless you're writing e.g. a real-time game, of course).&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="static-share-buttons" style="overflow:hidden"&gt;
&lt;a class="share-button" target="_blank" title="Tweet this!" style="margin-right:5px;" href="http://twitter.com/share?text=HTML5%20offline%20manifests%20with%20django-mediagenerator&amp;amp;url=http%3A//www.allbuttonspressed.com/blog/django/2010/11/HTML5-offline-manifests-with-django-mediagenerator&amp;amp;via=wkornewald"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/twitter-ba4f7679fb49cd4eb99cae4267d48d23c81137d4.png" alt="Tweet this!" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Facebook" style="margin-right:5px;" href="http://www.facebook.com/share.php?u=http%3A//www.allbuttonspressed.com/blog/django/2010/11/HTML5-offline-manifests-with-django-mediagenerator&amp;amp;t=HTML5%20offline%20manifests%20with%20django-mediagenerator"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/facebook-4b8233c3eb59d633eacdc5dbd8b98be769b9386d.png" alt="Share on Facebook" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Email a friend" style="margin-right:5px;" href="http://feedburner.google.com/fb/a/emailFlare?itemTitle=HTML5%20offline%20manifests%20with%20django-mediagenerator&amp;amp;uri=http%3A//www.allbuttonspressed.com/blog/django/2010/11/HTML5-offline-manifests-with-django-mediagenerator"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/email-e5a5556616278659d7761a5df6c58238d77ec47c.png" alt="Email a friend" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Delicious" style="margin-right:5px;" href="http://del.icio.us/post?url=http%3A//www.allbuttonspressed.com/blog/django/2010/11/HTML5-offline-manifests-with-django-mediagenerator&amp;amp;title=HTML5%20offline%20manifests%20with%20django-mediagenerator"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/delicious-400c2ae7e6871479f4bb34a4f8f476784a0992bf.png" alt="Share on Delicious" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on StumbleUpon" style="margin-right:5px;" href="http://www.stumbleupon.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/2010/11/HTML5-offline-manifests-with-django-mediagenerator&amp;amp;title=HTML5%20offline%20manifests%20with%20django-mediagenerator"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/stumbleupon-b725d91b84d24fa42787a8c79aa2695c5e5fb288.png" alt="Share on StumbleUpon" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Digg" style="margin-right:5px;" href="http://digg.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/2010/11/HTML5-offline-manifests-with-django-mediagenerator&amp;amp;title=HTML5%20offline%20manifests%20with%20django-mediagenerator"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/digg-8ada117a5610042c9fd592d3f4d3d20be242334d.png" alt="Share on Digg" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Reddit" style="margin-right:5px;" href="http://reddit.com/submit?url=http%3A//www.allbuttonspressed.com/blog/django/2010/11/HTML5-offline-manifests-with-django-mediagenerator&amp;amp;title=HTML5%20offline%20manifests%20with%20django-mediagenerator"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/reddit-f2c9d5792319c1a340054f310f4646642a48fe43.png" alt="Share on Reddit" width="32" height="32" /&gt;&lt;/a&gt;


&lt;a class="share-button" target="_blank" title="Share on Technorati" style="margin-right:5px;" href="http://technorati.com/faves?sub=favthis&amp;amp;add=http%3A//www.allbuttonspressed.com/blog/django/2010/11/HTML5-offline-manifests-with-django-mediagenerator"&gt;&lt;img src="http://www.allbuttonspressed.com/assets/social/icons32/technorati-1fa5656a85357eba71c018c718a7b36561a6f71c.png" alt="Share on Technorati" width="32" height="32" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="http://www.allbuttonspressed.com/blog/django/2010/11/HTML5-offline-manifests-with-django-mediagenerator#disqus_thread"&gt;Leave a comment&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/AllButtonsPressed/~4/x9WteRktdno" height="1" width="1"/&gt;</summary><feedburner:origLink>http://www.allbuttonspressed.com/blog/django/2010/11/HTML5-offline-manifests-with-django-mediagenerator</feedburner:origLink></entry></feed>

