<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1452693386685860331</id><updated>2024-09-16T23:41:20.354-04:00</updated><category term="Django"/><category term="cherokee"/><category term="tip"/><category term="uWSGI"/><category term="piston"/><category term="GIS"/><category term="PLM"/><category term="django-cms"/><category term="Varnish"/><category term="documentation"/><category term="test"/><category term="Internationalisation"/><category term="N95"/><category term="Online Survey"/><category term="VOIP"/><category term="bpython"/><category term="fabric factory"/><category term="first"/><category term="fring"/><category term="fts"/><category term="jquery"/><category term="project"/><category term="sphinx"/><category term="watch"/><title type='text'>Blog Roll</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>30</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-2129262994458581714</id><published>2010-01-03T21:43:00.000-04:00</published><updated>2010-01-03T21:43:59.630-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cherokee"/><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="uWSGI"/><category scheme="http://www.blogger.com/atom/ns#" term="Varnish"/><title type='text'>ESI using varnish and django</title><content type='html'>In my&amp;nbsp;&lt;a href=&quot;http://yml-blog.blogspot.com/2010/01/response-time-optimisation-with-varnish.html&quot;&gt;previous post [2]&lt;/a&gt;&amp;nbsp;I wrote that&amp;nbsp;&lt;a href=&quot;http://varnish.projects.linpro.no/&quot;&gt;Varnish [1]&lt;/a&gt;&amp;nbsp;has much more to offer than upstream cache. I have decided to explore its&amp;nbsp;&lt;a href=&quot;http://varnish.projects.linpro.no/wiki/ESIfeatures&quot;&gt;ESI supports [3]&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://www.w3.org/TR/esi-lang&quot;&gt;ESI&lt;/a&gt;&amp;nbsp;:&amp;nbsp;&lt;span style=&quot;font-family: sans-serif;&quot;&gt;Edge Side Includes (ESI) is an XML-based markup language that provides a means to assemble resources in HTTP clients. Unlike other in-markup languages, ESI is designed to leverage client tools like caches to improve end-user perceived performance, reduce processing overhead on the origin server, and enhanced availability. ESI allows for dynamic content assembly at the edge of the network.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: sans-serif;&quot;&gt;On of the most common difficulties that leads you to not cache a page for a logged in user is that you want to display some custom information for that user. For example you want to be able to display:&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: sans-serif;&quot;&gt;&amp;nbsp;&amp;nbsp;* &amp;nbsp;&quot; welcome joes, [ link to his profile ]&amp;nbsp;&quot;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: sans-serif;&quot;&gt;However the rest of the page will be common for all the users. The diagram below explains the composition of the page :&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQAF9J44JQTEOvWxSKqDp-Kx8WzWiJ8aTzysU-MC-HgBQnA4hZeAtfGkCGWl_gjjVg72XReADOHZ3b77NmVRQI7h3H3SZJeKaH9vdFdVU2ClFTwNk2Rhffd49hY2tCrhjrDfRDcSODpkE/s1600-h/esi_based_page.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQAF9J44JQTEOvWxSKqDp-Kx8WzWiJ8aTzysU-MC-HgBQnA4hZeAtfGkCGWl_gjjVg72XReADOHZ3b77NmVRQI7h3H3SZJeKaH9vdFdVU2ClFTwNk2Rhffd49hY2tCrhjrDfRDcSODpkE/s200/esi_based_page.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;span style=&quot;font-family: sans-serif;&quot;&gt;The yellow part of the page is common for all the users where the green part of the page should be customized for every user.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: sans-serif;&quot;&gt;This is a very common pattern, you can also have a header, footer and a navigation block that don&#39;t change very often and the rest of the page which is more dynamic like: recent activity, last articles, ...&amp;nbsp;So the idea here is to use varnish to assemble information&amp;nbsp;coming&amp;nbsp;from different urls and having a different lifetime in cache for each item.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: sans-serif;&quot;&gt;So in our example we will cache the &quot;dyn_page&quot; (yellow in the diagram) for five minutes and we will never cache the user info. I am going to start by dumping the code for this toy app and then explain it as we progress.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: sans-serif;&quot;&gt;Here it is the code of the &lt;b&gt;views.py:&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
import time
from django.views.generic.simple import direct_to_template
from django.views.decorators.cache import cache_page, never_cache
@never_cache
def user_info(request):
    return direct_to_template(request, &quot;esi_app/user_info.html&quot;)
@cache_page(60*5)
def view_dyn_page(request):
    #Simulate the fact that this page take a lot of time to be built
    time.sleep(2)
    return direct_to_template(request, &quot;esi_app/dyn_page.html&quot;)
@never_cache
def view_page(request):
    USE_ESI = True
    if not USE_ESI:
        #Simulate the fact that the dynamic part of the page
        # take a lot of time to be built
        time.sleep(2)
    return direct_to_template(request,
                              template=&quot;esi_app/page.html&quot;,
                              extra_context={&#39;USE_ESI&#39;:USE_ESI,})
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: sans-serif;&quot;&gt;The views.py contains 3 views one that can display the information for each&amp;nbsp;individual&amp;nbsp;block (yellow and green in the diagram above) and one that can displays the complete page. Note the &quot;USE_ESI&quot; variable that we will utilize in our template. I have added a sleep of 2 seconds in the code to simulate an operation which is taking a lot of time thus the caching strategies make more sense and my ab test later or will be more&amp;nbsp;meaningful.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: sans-serif;&quot;&gt;Here it is the code of of the &lt;b&gt;urls.py:&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
from django.conf.urls.defaults import *
urlpatterns = patterns(&#39;esi_app.views&#39;,
    url(r&#39;^page/&#39;, &#39;view_page&#39;, name=&#39;view_page&#39;),
    url(r&#39;^dyn_page/&#39;, &#39;view_dyn_page&#39;, name=&#39;viewd_dyn_page&#39;),
    url(r&#39;^user_info/&#39;, &#39;user_info&#39;, name=&#39;user_info&#39;),
)
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: sans-serif;&quot;&gt;Here it is the code for the &lt;b&gt;base.html:&lt;/b&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;

&amp;lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01 Transitional//EN&quot;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;&lt;a href=&quot;http://www.w3.org/TR/html4/loose.dtd&quot;&gt;http://www.w3.org/TR/html4/loose.dtd&lt;/a&gt;&quot;&amp;gt;

&amp;lt;html&amp;gt;

&amp;lt;head&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;title&amp;gt;{% block title %} {%endblock%}&amp;lt;/title&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;style type=&quot;text/css&quot;&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#user_info {

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:lightgreen;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#content {

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:lightyellow;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/style&amp;gt;

&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div id=&quot;user_info&quot;&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{% block user_info %} {%endblock%}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div id=&quot;content&quot;&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{% block content %} {%endblock%}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;

&amp;lt;/body&amp;gt;

&amp;lt;/html&amp;gt;


&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: sans-serif; white-space: normal;&quot;&gt;Here it is the code for the&amp;nbsp;&lt;b&gt;page.html:&lt;/b&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
{% extends &amp;quot;esi_app/base.html&amp;quot; %}&lt;br&gt;
{% block user_info %}&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{% if USE_ESI %}&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;esi:include src=&amp;quot;&lt;a href=&quot;http://192.168.1.18:6081/esi/user_info/&quot;&gt;http://192.168.1.18:6081/esi/user_info/&lt;/a&gt;&amp;quot;/&amp;gt;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{% else %}&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{% include &amp;quot;esi_app/user_info.html&amp;quot; %}&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{% endif %}&lt;br&gt;
{%endblock%}&lt;br&gt;
{% block content %}&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{%if USE_ESI %}&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;esi:include src=&amp;quot;&lt;a href=&quot;http://192.168.1.18:6081/esi/dyn_page/&quot;&gt;http://192.168.1.18:6081/esi/dyn_page/&lt;/a&gt;&amp;quot;/&amp;gt;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{% else %}&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{% include &amp;quot;esi_app/dyn_page.html&amp;quot; %}&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{% endif %}&lt;br&gt;
{%endblock%}&lt;br&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: sans-serif;&quot;&gt;This template use the variables &quot;USE_ESI&quot; to decide&amp;nbsp;whether&amp;nbsp;the page will be built using an ESI server or not. This allows a graceful degradation and will help you to debug your page. In a real life situation this variable might come from a django&#39;s context processor. The idea here is &amp;nbsp;that &quot;/esi/page/&quot; is built using &quot;/esi/dyn_page/&quot; and&amp;nbsp;&quot;/esi/user_info/&quot;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;{% load webdesign %}

&amp;lt;h1&amp;gt;{% lorem 2 w random %}&amp;lt;/h1&amp;gt;

&amp;lt;p&amp;gt;{% lorem 2 p random %}&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;span style=&quot;font-family: sans-serif;&quot;&gt;Here it is the code for the&amp;nbsp;&lt;b&gt;user_info.html:&lt;/b&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;{% if user.is_authenticated %}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Welcome, {{ user }} -- &amp;lt;a href=&quot;/admin/logout&quot;&amp;gt;Logout&amp;lt;/a&amp;gt;

{% else %}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;a href=&quot;/admin/login&quot;&amp;gt;Login&amp;lt;/a&amp;gt;

{% endif %}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: sans-serif;&quot;&gt;Then you will need to configure varnish to make it do the ESI transformation on the page with the url equal to&amp;nbsp;&quot;/esi/page/&quot;.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: sans-serif;&quot;&gt;Here it is the varnish configuration for the&amp;nbsp;&lt;a href=&quot;http://www.blogger.com/goog_1262438952114&quot;&gt;&lt;b&gt;/etc/varnish/default.vcl&lt;/b&gt;&lt;/a&gt;&lt;b&gt;:&lt;/b&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
backend default {
.host = &quot;127.0.0.1&quot;;
.port = &quot;8080&quot;;
}
sub vcl_recv {
     unset req.http.Accept-Encoding;
     #unset req.http.Vary;
}
sub vcl_fetch {
     if (req.url == &quot;/esi/page/&quot;) {
         esi;
     }
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
The code below is rather self explanatory it tells Varnish to do ESI substitution on the page located at &quot;/esi/page/&quot;. has in my previous post Cherokee is located on the port 8080 and Varnish on the port 6081. The trickiest part there is the vcl_recv, in this section varnish&amp;nbsp;explicitly&amp;nbsp;prevents the backend from gzipping the content. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
All the machinery is in place now so you can use your favorite browser to visualize the result :&lt;br /&gt;
&amp;nbsp;&amp;nbsp;* go to&amp;nbsp;http://192.168.1.18:6081/esi/page/ to view the page generated by varnish&lt;br /&gt;
&amp;nbsp;&amp;nbsp;* go to&amp;nbsp;http://192.168.1.18:8080/esi/page/ to view the page returned by Cherokee&lt;br /&gt;
&lt;br /&gt;
Curl is another tools that is useful when playing cached page :&lt;br /&gt;
&amp;nbsp;&amp;nbsp;* curl [url] wil display in the console the html source code&lt;br /&gt;
&amp;nbsp;&amp;nbsp;* curl -I [url]&amp;nbsp;will show the document info in a console.&lt;br /&gt;
&amp;nbsp;&amp;nbsp;*&amp;nbsp;curl [url] -H &quot;Accept-g: gzip,deflate&quot;&lt;br /&gt;
&lt;br /&gt;
Here it is the page rendered by cherokee :&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01 Transitional//EN&quot;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;&lt;a href=&quot;http://www.w3.org/TR/html4/loose.dtd&quot;&gt;http://www.w3.org/TR/html4/loose.dtd&lt;/a&gt;&quot;&amp;gt;

&amp;lt;html&amp;gt;

&amp;lt;head&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;title&amp;gt; &amp;lt;/title&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;style type=&quot;text/css&quot;&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#user_info {

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:lightgreen;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#content {

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:lightyellow;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/style&amp;gt;

&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div id=&quot;user_info&quot;&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;esi:include src=&quot;&lt;a href=&quot;http://192.168.1.18:6081/esi/user_info/&quot;&gt;http://192.168.1.18:6081/esi/user_info/&lt;/a&gt;&quot;/&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div id=&quot;content&quot;&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;esi:include src=&quot;&lt;a href=&quot;http://192.168.1.18:6081/esi/dyn_page/&quot;&gt;http://192.168.1.18:6081/esi/dyn_page/&lt;/a&gt;&quot;/&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;

&amp;lt;/body&amp;gt;

&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;Here it is the page after the substitution done by Varnish :&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
&amp;lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01 Transitional//EN&quot;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;&lt;a href=&quot;http://www.w3.org/TR/html4/loose.dtd&quot;&gt;http://www.w3.org/TR/html4/loose.dtd&lt;/a&gt;&quot;&amp;gt;

&amp;lt;html&amp;gt;

&amp;lt;head&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;title&amp;gt; &amp;lt;/title&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;style type=&quot;text/css&quot;&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#user_info {

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:lightgreen;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#content {

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:lightyellow;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/style&amp;gt;

&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div id=&quot;user_info&quot;&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;a href=&quot;/admin/login&quot;&amp;gt;Login&amp;lt;/a&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div id=&quot;content&quot;&amp;gt;

&amp;lt;h1&amp;gt;ipsa sed&amp;lt;/h1&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;p&amp;gt;Nemo perferendis delectus pariatur aliquid repellendus repellat explicabo facilis, molestiae veritatis odit, accusantium repellat culpa ab laboriosam, iste laborum amet et harum, iusto illum ipsa a quos necessitatibus voluptatem consectetur cumque? Doloremque atque delectus ipsa ad veniam incidunt cum exercitationem voluptates labore, sapiente ducimus deserunt expedita aperiam temporibus omnis magnam qui architecto, pariatur voluptates nesciunt nam ab dolore omnis, quo voluptatem nihil accusamus aperiam excepturi exercitationem? Consectetur mollitia neque quod quas.&amp;lt;/p&amp;gt;



&amp;lt;p&amp;gt;Tempore amet voluptate ipsum suscipit placeat exercitationem labore nam voluptas, debitis esse dignissimos, fugiat illum asperiores suscipit deleniti maiores consequuntur, doloribus architecto repellendus dicta nemo corporis explicabo? A fuga ex, voluptates quam dignissimos aspernatur, reprehenderit accusantium id magni ut debitis adipisci esse voluptas tempora, quas doloribus blanditiis voluptatum veniam nam magni et adipisci fuga pariatur provident? Aut ipsum quam quia earum quod cum sapiente officia inventore delectus, expedita ex quia ipsam consectetur exercitationem ut ad sunt illum minus voluptatum, accusantium maxime facere eos numquam explicabo, rerum ab dolorem repellendus, praesentium debitis tempora aut facere sapiente odit veniam quae?&amp;lt;/p&amp;gt;&amp;lt;/p&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;

&amp;lt;/body&amp;gt;

&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;Some quick and dirty ab test will show you the interest of this&amp;nbsp;Technic. Varnish is very fast at assembling the content coming from different sources. Several months ago &lt;a href=&quot;http://www.holovaty.com/writing/django-two-phased-rendering/&quot;&gt;Adrian Holovaty has written an article&lt;/a&gt; about an alternate approach to this class of problem.&lt;br /&gt;
&lt;br /&gt;
I would be glad to hear from you what other varnish tricks can be used on top of a django web application.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Times New Roman&#39;; white-space: normal;&quot;&gt;[1]&amp;nbsp;&lt;a href=&quot;http://varnish.projects.linpro.no/&quot;&gt;http://varnish.projects.linpro.no/&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Times New Roman&#39;; white-space: normal;&quot;&gt;[2]&lt;a href=&quot;http://yml-blog.blogspot.com/2010/01/response-time-optimisation-with-varnish.html&quot;&gt;http://yml-blog.blogspot.com/2010/01/response-time-optimisation-with-varnish.html&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Times New Roman&#39;; white-space: normal;&quot;&gt;[3]&amp;nbsp;&lt;a href=&quot;http://varnish.projects.linpro.no/wiki/ESIfeatures&quot;&gt;http://varnish.projects.linpro.no/wiki/ESIfeatures&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Times New Roman&#39;; white-space: normal;&quot;&gt;[4]&amp;nbsp;&lt;a href=&quot;http://www.w3.org/TR/esi-lang&quot;&gt;http://www.w3.org/TR/esi-lang&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;[5]&amp;nbsp;&lt;a href=&quot;http://www.holovaty.com/writing/django-two-phased-rendering/&quot;&gt;http://www.holovaty.com/writing/django-two-phased-rendering/&lt;/a&gt;&lt;/span&gt;</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/2129262994458581714/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2010/01/esi-using-varnish-and-django.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/2129262994458581714'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/2129262994458581714'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2010/01/esi-using-varnish-and-django.html' title='ESI using varnish and django'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQAF9J44JQTEOvWxSKqDp-Kx8WzWiJ8aTzysU-MC-HgBQnA4hZeAtfGkCGWl_gjjVg72XReADOHZ3b77NmVRQI7h3H3SZJeKaH9vdFdVU2ClFTwNk2Rhffd49hY2tCrhjrDfRDcSODpkE/s72-c/esi_based_page.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-2227493044866346926</id><published>2010-01-02T08:56:00.000-04:00</published><updated>2010-01-02T08:56:19.775-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cherokee"/><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="django-cms"/><category scheme="http://www.blogger.com/atom/ns#" term="uWSGI"/><category scheme="http://www.blogger.com/atom/ns#" term="Varnish"/><title type='text'>Response time optimisation with Varnish</title><content type='html'>&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: arial, sans-serif; font-size: small;&quot;&gt;&lt;/span&gt;&lt;br /&gt;
This blog post shows you how to optimize the tools chain on your server to improve its performance by an order of magnetude with out changing a single line in your django project. In order to do so I will use&amp;nbsp;&lt;a href=&quot;http://yml-blog.blogspot.com/2009/12/flup-vs-uwsgi-with-cherokee.html&quot;&gt;again&amp;nbsp;django-cms [1]&lt;/a&gt; as guinea pig because there is a fair amount for processing to display a page but it is still easy to install. Note: django-cms example&amp;nbsp;has the&amp;nbsp;cache middleware activated by default.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Then I will run&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot; x=&quot;y&quot;&gt;ab&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&amp;nbsp;testing on a particular page and compare the results. These tests are being performed on my laptop hp&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot; x=&quot;y&quot;&gt;dv6&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;-1030. The important information is not the figures but by&amp;nbsp;them self&amp;nbsp;but rather the variation of the response time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Before starting my test I have moved django-cms to be mounted under &quot;/&quot;. In order to do this you will need to change the configuration into the file called&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot; x=&quot;y&quot;&gt;example_&lt;/span&gt;&lt;a href=&quot;http://uwsgi.py/&quot; style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px; color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;uwsgi&lt;/a&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;http://uwsgi.py/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;.py&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
import os
import django.core.handlers.wsgi

# Set the django settings and define the wsgi app
os.environ[&#39;DJANGO_SETTINGS_MODULE&#39;] = &#39;example.settings&#39;
application = django.core.handlers.wsgi.WSGIHandler()

# Mount the application to the url
applications = {&#39;/&#39;:application, }
&lt;/code&gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
Then you need to change the rule behavior in cherokee admin to reflect this change. Cheorkee admin makes this task a breeze.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: arial, sans-serif; font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJwo3hJMb1PRPehGUj8gn1CjdX_7RIRCTnL24V1GwZGT2FKDPDB9WSiENGYxD7dft2hHgFcO0MzxDUhkLiwOVnKEuSRvOc-colhAK6kLy82JJfQnRSRNK9XU1Rbfd8r4XGtYd5CjxqKdA/s1600-h/uwsgi_new_mount_point.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJwo3hJMb1PRPehGUj8gn1CjdX_7RIRCTnL24V1GwZGT2FKDPDB9WSiENGYxD7dft2hHgFcO0MzxDUhkLiwOVnKEuSRvOc-colhAK6kLy82JJfQnRSRNK9XU1Rbfd8r4XGtYd5CjxqKdA/s640/uwsgi_new_mount_point.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: arial, sans-serif; font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Before diving head first into the the meat of this article here it is a diagram of the architecture that we are going to work with :&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0E0D5QuVUuFYwSfvaT2yP2kmgjG4i6UPc7OYOJvG2wzP1ylAmD0rjx_d5Q9IgsASBve1pyXAY5xDritiNenJ2wSu6rV_KD89-lM3LMvo0IrRZtxA42CCmlkLlkuRXr3zmq3XzHXAkUhQ/s1600-h/architecture.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0E0D5QuVUuFYwSfvaT2yP2kmgjG4i6UPc7OYOJvG2wzP1ylAmD0rjx_d5Q9IgsASBve1pyXAY5xDritiNenJ2wSu6rV_KD89-lM3LMvo0IrRZtxA42CCmlkLlkuRXr3zmq3XzHXAkUhQ/s640/architecture.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: arial, sans-serif; font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;The goal of this article is to show you the incredible boost that varnish&amp;nbsp;can give to&amp;nbsp;certain type of web application.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://varnish.projects.linpro.no/&quot; style=&quot;color: #664d9f; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;varnish [2]&lt;/a&gt;&amp;nbsp;&lt;span style=&quot;font-family: verdana, arial, &#39;Bitstream Vera Sans&#39;, helvetica, sans-serif;&quot; x=&quot;y&quot;&gt;is a state-of-the-art, high-performance HTTP accelerator. It uses the advanced features in Linux 2.6, FreeBSD 6/7 and Solaris 10 to achieve its high performance.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: verdana, arial, &#39;Bitstream Vera Sans&#39;, helvetica, sans-serif;&quot; x=&quot;y&quot;&gt;Some of the features include:&lt;/span&gt;&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://www.blogger.com/wiki/VCL&quot; style=&quot;color: #bb0000; font-family: verdana, arial, &#39;Bitstream Vera Sans&#39;, helvetica, sans-serif; text-decoration: none;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;VCL&lt;/a&gt;&lt;span style=&quot;font-family: verdana, arial, &#39;Bitstream Vera Sans&#39;, helvetica, sans-serif;&quot; x=&quot;y&quot;&gt;&amp;nbsp;- a very flexible configuration language&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: verdana, arial, &#39;Bitstream Vera Sans&#39;, helvetica, sans-serif;&quot; x=&quot;y&quot;&gt;Load balancing with&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;http://www.blogger.com/wiki/BackendPolling&quot; style=&quot;color: #bb0000; font-family: verdana, arial, &#39;Bitstream Vera Sans&#39;, helvetica, sans-serif; text-decoration: none;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;health checking of backends&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: verdana, arial, &#39;Bitstream Vera Sans&#39;, helvetica, sans-serif;&quot; x=&quot;y&quot;&gt;Partial support for&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;http://www.blogger.com/wiki/ESIfeatures&quot; style=&quot;color: #bb0000; font-family: verdana, arial, &#39;Bitstream Vera Sans&#39;, helvetica, sans-serif; text-decoration: none;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;ESI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: verdana, arial, &#39;Bitstream Vera Sans&#39;, helvetica, sans-serif;&quot; x=&quot;y&quot;&gt;URL rewriting&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: verdana, arial, &#39;Bitstream Vera Sans&#39;, helvetica, sans-serif;&quot; x=&quot;y&quot;&gt;Graceful handling of &quot;dead&quot; backends&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: verdana, arial, &#39;Bitstream Vera Sans&#39;, helvetica, sans-serif;&quot; x=&quot;y&quot;&gt;...&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: verdana, arial, &#39;Bitstream Vera Sans&#39;, helvetica, sans-serif;&quot; x=&quot;y&quot;&gt;The bottom line is that just by installing it and using it with a vanilla configuration, on ubuntu, will increase the responsiveness of your site by an order of&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px; font-family: verdana, arial, &#39;Bitstream Vera Sans&#39;, helvetica, sans-serif;&quot; x=&quot;y&quot;&gt;magnitude&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: verdana, arial, &#39;Bitstream Vera Sans&#39;, helvetica, sans-serif;&quot; x=&quot;y&quot;&gt;&amp;nbsp;that is&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px; font-family: verdana, arial, &#39;Bitstream Vera Sans&#39;, helvetica, sans-serif;&quot; x=&quot;y&quot;&gt;hard&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: verdana, arial, &#39;Bitstream Vera Sans&#39;, helvetica, sans-serif;&quot; x=&quot;y&quot;&gt;&amp;nbsp;to believe we are talking here of an improvement factor&lt;/span&gt;&lt;span style=&quot;font-family: verdana, arial, &#39;Bitstream Vera Sans&#39;, helvetica, sans-serif; font-weight: bold;&quot; x=&quot;y&quot;&gt;&amp;nbsp;ranging from 50 to 600 times&lt;/span&gt;&lt;span style=&quot;font-family: verdana, arial, &#39;Bitstream Vera Sans&#39;, helvetica, sans-serif;&quot; x=&quot;y&quot;&gt;.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: verdana, arial, &#39;Bitstream Vera Sans&#39;, helvetica, sans-serif;&quot; x=&quot;y&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;The first thing that you would like to do is to install&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;http://varnish.projects.linpro.no/&quot; style=&quot;color: #664d9f;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;varnish [2].&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;&amp;nbsp;On ubuntu varnish is very easy to install/configure since there is a package that exists. Once this operation is executed you will need to define the backend, this varnish jargon means that you need to tell varnish where Cherokee is located.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
backend default {
.host = &quot;127.0.0.1&quot;;
.port = &quot;8080&quot;;
}
&lt;/code&gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;Here it is some&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot; x=&quot;y&quot;&gt;&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;ab&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;&amp;nbsp;tests that I have done to illustrate this article, 8080 and 6081 are respectively the port for Cherokee and Varnish&lt;/span&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Cherokee&lt;/h2&gt;&lt;h3 style=&quot;font-family: arial, sans-serif; font-size: small;&quot;&gt;ab -n 100 -c 1&amp;nbsp;&lt;a href=&quot;http://192.168.1.18:8080/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://192.168.1.18:8080/&lt;/a&gt;&lt;/h3&gt;This is ApacheBench, Version 2.3 &amp;lt;$Revision: 655654 $&amp;gt;&lt;br /&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd,&amp;nbsp;&lt;a href=&quot;http://www.zeustech.net/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.zeustech.net/&lt;/a&gt;&lt;br /&gt;
Licensed to The Apache Software Foundation,&amp;nbsp;&lt;a href=&quot;http://www.apache.org/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.apache.org/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Benchmarking 192.168.1.18 (be patient).....done&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Server Software: Cherokee/0.99.37&lt;br /&gt;
Server Hostname: 192.168.1.18&lt;br /&gt;
Server Port: 8080&lt;br /&gt;
&lt;br /&gt;
Document Path: /&lt;br /&gt;
Document Length: 3440 bytes&lt;br /&gt;
&lt;br /&gt;
Concurrency Level: 1&lt;br /&gt;
Time taken for tests: 15.285 seconds&lt;br /&gt;
Complete requests: 100&lt;br /&gt;
Failed requests: 0&lt;br /&gt;
Write errors: 0&lt;br /&gt;
Total transferred: 364300 bytes&lt;br /&gt;
HTML transferred: 344000 bytes&lt;br /&gt;
Requests per second: 6.54 [#/sec] (mean)&lt;br /&gt;
Time per request: 152.851 [ms] (mean)&lt;br /&gt;
Time per request: 152.851 [ms] (mean, across all concurrent requests)&lt;br /&gt;
Transfer rate: 23.28 [Kbytes/sec] received&lt;br /&gt;
&lt;br /&gt;
Connection Times (ms)&lt;br /&gt;
min mean[+/-sd] median max&lt;br /&gt;
Connect: 0 0 2.4 0 24&lt;br /&gt;
Processing: 133 153 17.3 149 235&lt;br /&gt;
Waiting: 133 153 17.3 149 235&lt;br /&gt;
Total: 134 153 17.2 149 235&lt;br /&gt;
&lt;br /&gt;
Percentage of the requests served within a certain time (ms)&lt;br /&gt;
50% 149&lt;br /&gt;
66% 158&lt;br /&gt;
75% 164&lt;br /&gt;
80% 166&lt;br /&gt;
90% 172&lt;br /&gt;
95% 175&lt;br /&gt;
98% 230&lt;br /&gt;
99% 235&lt;br /&gt;
100% 235 (longest request)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h3 style=&quot;font-family: arial, sans-serif; font-size: small;&quot;&gt;ab -n 100 -c 50&amp;nbsp;&lt;a href=&quot;http://192.168.1.18:8080/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://192.168.1.18:8080/&lt;/a&gt;&lt;/h3&gt;This is ApacheBench, Version 2.3 &amp;lt;$Revision: 655654 $&amp;gt;&lt;br /&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd,&amp;nbsp;&lt;a href=&quot;http://www.zeustech.net/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.zeustech.net/&lt;/a&gt;&lt;br /&gt;
Licensed to The Apache Software Foundation,&amp;nbsp;&lt;a href=&quot;http://www.apache.org/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.apache.org/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Benchmarking 192.168.1.18 (be patient).....done&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Server Software: Cherokee/0.99.37&lt;br /&gt;
Server Hostname: 192.168.1.18&lt;br /&gt;
Server Port: 8080&lt;br /&gt;
&lt;br /&gt;
Document Path: /&lt;br /&gt;
Document Length: 3440 bytes&lt;br /&gt;
&lt;br /&gt;
Concurrency Level: 50&lt;br /&gt;
Time taken for tests: 8.202 seconds&lt;br /&gt;
Complete requests: 100&lt;br /&gt;
Failed requests: 0&lt;br /&gt;
Write errors: 0&lt;br /&gt;
Total transferred: 364300 bytes&lt;br /&gt;
HTML transferred: 344000 bytes&lt;br /&gt;
Requests per second: 12.19 [#/sec] (mean)&lt;br /&gt;
Time per request: 4101.021 [ms] (mean)&lt;br /&gt;
Time per request: 82.020 [ms] (mean, across all concurrent requests)&lt;br /&gt;
Transfer rate: 43.37 [Kbytes/sec] received&lt;br /&gt;
&lt;br /&gt;
Connection Times (ms)&lt;br /&gt;
min mean[+/-sd] median max&lt;br /&gt;
Connect: 0 1 1.1 1 3&lt;br /&gt;
Processing: 740 3283 1158.0 3906 4438&lt;br /&gt;
Waiting: 740 3283 1158.0 3906 4438&lt;br /&gt;
Total: 743 3284 1157.1 3906 4438&lt;br /&gt;
&lt;br /&gt;
Percentage of the requests served within a certain time (ms)&lt;br /&gt;
50% 3906&lt;br /&gt;
66% 4048&lt;br /&gt;
75% 4112&lt;br /&gt;
80% 4182&lt;br /&gt;
90% 4285&lt;br /&gt;
95% 4341&lt;br /&gt;
98% 4359&lt;br /&gt;
99% 4438&lt;br /&gt;
100% 4438 (longest request)&lt;br /&gt;
&lt;br /&gt;
&lt;h3 style=&quot;font-family: arial, sans-serif; font-size: small;&quot;&gt;ab -n 100 -c 100&amp;nbsp;&lt;a href=&quot;http://192.168.1.18:8080/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://192.168.1.18:8080/&lt;/a&gt;&lt;/h3&gt;This is ApacheBench, Version 2.3 &amp;lt;$Revision: 655654 $&amp;gt;&lt;br /&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd,&amp;nbsp;&lt;a href=&quot;http://www.zeustech.net/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.zeustech.net/&lt;/a&gt;&lt;br /&gt;
Licensed to The Apache Software Foundation,&amp;nbsp;&lt;a href=&quot;http://www.apache.org/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.apache.org/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Benchmarking 192.168.1.18 (be patient).....done&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Server Software: Cherokee/0.99.37&lt;br /&gt;
Server Hostname: 192.168.1.18&lt;br /&gt;
Server Port: 8080&lt;br /&gt;
&lt;br /&gt;
Document Path: /&lt;br /&gt;
Document Length: 3440 bytes&lt;br /&gt;
&lt;br /&gt;
Concurrency Level: 100&lt;br /&gt;
Time taken for tests: 8.236 seconds&lt;br /&gt;
Complete requests: 100&lt;br /&gt;
Failed requests: 0&lt;br /&gt;
Write errors: 0&lt;br /&gt;
Total transferred: 364300 bytes&lt;br /&gt;
HTML transferred: 344000 bytes&lt;br /&gt;
Requests per second: 12.14 [#/sec] (mean)&lt;br /&gt;
Time per request: 8235.626 [ms] (mean)&lt;br /&gt;
Time per request: 82.356 [ms] (mean, across all concurrent requests)&lt;br /&gt;
Transfer rate: 43.20 [Kbytes/sec] received&lt;br /&gt;
&lt;br /&gt;
Connection Times (ms)&lt;br /&gt;
min mean[+/-sd] median max&lt;br /&gt;
Connect: 3 4 0.8 4 5&lt;br /&gt;
Processing: 699 4533 2337.0 4650 8228&lt;br /&gt;
Waiting: 699 4533 2337.0 4650 8228&lt;br /&gt;
Total: 704 4537 2336.2 4654 8230&lt;br /&gt;
&lt;br /&gt;
Percentage of the requests served within a certain time (ms)&lt;br /&gt;
50% 4654&lt;br /&gt;
66% 5884&lt;br /&gt;
75% 6717&lt;br /&gt;
80% 7017&lt;br /&gt;
90% 7749&lt;br /&gt;
95% 8115&lt;br /&gt;
98% 8221&lt;br /&gt;
99% 8230&lt;br /&gt;
100% 8230 (longest request)&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Varnish&lt;/h2&gt;&lt;br /&gt;
&lt;h3 style=&quot;font-family: arial, sans-serif; font-size: small;&quot;&gt;ab -n 100 -c 1&amp;nbsp;&lt;a href=&quot;http://192.168.1.18:6081/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://192.168.1.18:6081/&lt;/a&gt;&lt;/h3&gt;This is ApacheBench, Version 2.3 &amp;lt;$Revision: 655654 $&amp;gt;&lt;br /&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd,&amp;nbsp;&lt;a href=&quot;http://www.zeustech.net/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.zeustech.net/&lt;/a&gt;&lt;br /&gt;
Licensed to The Apache Software Foundation,&amp;nbsp;&lt;a href=&quot;http://www.apache.org/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.apache.org/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Benchmarking 192.168.1.18 (be patient).....done&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Server Software: Cherokee/0.99.37&lt;br /&gt;
Server Hostname: 192.168.1.18&lt;br /&gt;
Server Port: 6081&lt;br /&gt;
&lt;br /&gt;
Document Path: /&lt;br /&gt;
Document Length: 3440 bytes&lt;br /&gt;
&lt;br /&gt;
Concurrency Level: 1&lt;br /&gt;
Time taken for tests: 0.030 seconds&lt;br /&gt;
Complete requests: 100&lt;br /&gt;
Failed requests: 0&lt;br /&gt;
Write errors: 0&lt;br /&gt;
Total transferred: 374800 bytes&lt;br /&gt;
HTML transferred: 344000 bytes&lt;br /&gt;
Requests per second: 3320.49 [#/sec] (mean)&lt;br /&gt;
Time per request: 0.301 [ms] (mean)&lt;br /&gt;
Time per request: 0.301 [ms] (mean, across all concurrent requests)&lt;br /&gt;
Transfer rate: 12153.53 [Kbytes/sec] received&lt;br /&gt;
&lt;br /&gt;
Connection Times (ms)&lt;br /&gt;
min mean[+/-sd] median max&lt;br /&gt;
Connect: 0 0 0.0 0 0&lt;br /&gt;
Processing: 0 0 0.9 0 8&lt;br /&gt;
Waiting: 0 0 0.9 0 8&lt;br /&gt;
Total: 0 0 0.9 0 8&lt;br /&gt;
&lt;br /&gt;
Percentage of the requests served within a certain time (ms)&lt;br /&gt;
50% 0&lt;br /&gt;
66% 0&lt;br /&gt;
75% 0&lt;br /&gt;
80% 0&lt;br /&gt;
90% 0&lt;br /&gt;
95% 0&lt;br /&gt;
98% 4&lt;br /&gt;
99% 8&lt;br /&gt;
100% 8 (longest request)&lt;br /&gt;
&lt;br /&gt;
&lt;h3 style=&quot;font-family: arial, sans-serif; font-size: small;&quot;&gt;ab -n 100 -c 50&amp;nbsp;&lt;a href=&quot;http://192.168.1.18:6081/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://192.168.1.18:6081/&lt;/a&gt;&lt;/h3&gt;This is ApacheBench, Version 2.3 &amp;lt;$Revision: 655654 $&amp;gt;&lt;br /&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd,&amp;nbsp;&lt;a href=&quot;http://www.zeustech.net/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.zeustech.net/&lt;/a&gt;&lt;br /&gt;
Licensed to The Apache Software Foundation,&amp;nbsp;&lt;a href=&quot;http://www.apache.org/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.apache.org/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Benchmarking 192.168.1.18 (be patient).....done&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Server Software: Cherokee/0.99.37&lt;br /&gt;
Server Hostname: 192.168.1.18&lt;br /&gt;
Server Port: 6081&lt;br /&gt;
&lt;br /&gt;
Document Path: /&lt;br /&gt;
Document Length: 3440 bytes&lt;br /&gt;
&lt;br /&gt;
Concurrency Level: 50&lt;br /&gt;
Time taken for tests: 0.012 seconds&lt;br /&gt;
Complete requests: 100&lt;br /&gt;
Failed requests: 0&lt;br /&gt;
Write errors: 0&lt;br /&gt;
Total transferred: 378548 bytes&lt;br /&gt;
HTML transferred: 347440 bytes&lt;br /&gt;
Requests per second: 8522.97 [#/sec] (mean)&lt;br /&gt;
Time per request: 5.866 [ms] (mean)&lt;br /&gt;
Time per request: 0.117 [ms] (mean, across all concurrent requests)&lt;br /&gt;
Transfer rate: 31507.35 [Kbytes/sec] received&lt;br /&gt;
&lt;br /&gt;
Connection Times (ms)&lt;br /&gt;
min mean[+/-sd] median max&lt;br /&gt;
Connect: 0 1 1.2 0 3&lt;br /&gt;
Processing: 0 3 2.0 3 8&lt;br /&gt;
Waiting: 0 3 2.0 2 8&lt;br /&gt;
Total: 0 4 2.8 4 11&lt;br /&gt;
&lt;br /&gt;
Percentage of the requests served within a certain time (ms)&lt;br /&gt;
50% 4&lt;br /&gt;
66% 5&lt;br /&gt;
75% 6&lt;br /&gt;
80% 7&lt;br /&gt;
90% 9&lt;br /&gt;
95% 10&lt;br /&gt;
98% 11&lt;br /&gt;
99% 11&lt;br /&gt;
100% 11 (longest request)&lt;br /&gt;
&lt;br /&gt;
&lt;h3 style=&quot;font-family: arial, sans-serif; font-size: small;&quot;&gt;ab -n 100 -c 100&amp;nbsp;&lt;a href=&quot;http://192.168.1.18:6081/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://192.168.1.18:6081/&lt;/a&gt;&lt;/h3&gt;This is ApacheBench, Version 2.3 &amp;lt;$Revision: 655654 $&amp;gt;&lt;br /&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd,&amp;nbsp;&lt;a href=&quot;http://www.zeustech.net/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.zeustech.net/&lt;/a&gt;&lt;br /&gt;
Licensed to The Apache Software Foundation,&amp;nbsp;&lt;a href=&quot;http://www.apache.org/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.apache.org/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Benchmarking 192.168.1.18 (be patient).....done&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Server Software: Cherokee/0.99.37&lt;br /&gt;
Server Hostname: 192.168.1.18&lt;br /&gt;
Server Port: 6081&lt;br /&gt;
&lt;br /&gt;
Document Path: /&lt;br /&gt;
Document Length: 3440 bytes&lt;br /&gt;
&lt;br /&gt;
Concurrency Level: 100&lt;br /&gt;
Time taken for tests: 0.013 seconds&lt;br /&gt;
Complete requests: 100&lt;br /&gt;
Failed requests: 0&lt;br /&gt;
Write errors: 0&lt;br /&gt;
Total transferred: 374800 bytes&lt;br /&gt;
HTML transferred: 344000 bytes&lt;br /&gt;
Requests per second: 7662.25 [#/sec] (mean)&lt;br /&gt;
Time per request: 13.051 [ms] (mean)&lt;br /&gt;
Time per request: 0.131 [ms] (mean, across all concurrent requests)&lt;br /&gt;
Transfer rate: 28045.03 [Kbytes/sec] received&lt;br /&gt;
&lt;br /&gt;
Connection Times (ms)&lt;br /&gt;
min mean[+/-sd] median max&lt;br /&gt;
Connect: 2 3 0.6 3 4&lt;br /&gt;
Processing: 7 7 0.7 7 9&lt;br /&gt;
Waiting: 5 6 0.6 6 7&lt;br /&gt;
Total: 9 10 1.2 10 13&lt;br /&gt;
&lt;br /&gt;
Percentage of the requests served within a certain time (ms)&lt;br /&gt;
50% 10&lt;br /&gt;
66% 10&lt;br /&gt;
75% 10&lt;br /&gt;
80% 11&lt;br /&gt;
90% 12&lt;br /&gt;
95% 13&lt;br /&gt;
98% 13&lt;br /&gt;
99% 13&lt;br /&gt;
100% 13 (longest request)&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;Installing varnish in front of your web server is propably this first step you should take in the end less journey of optimising your web application. It is interesting to note that in addition of dramatically improving the response time Varnish will also reduce the load on your application server stack [ uWSGI + django +db].&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;This blog post barely scratches the surface of how&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot; x=&quot;y&quot;&gt;&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;django&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;&amp;nbsp;can take&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px;&quot; x=&quot;y&quot;&gt;&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;advantage&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;&amp;nbsp;of of caches, django gives you the possibility to cache information at different&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px;&quot; x=&quot;y&quot;&gt;&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;stages&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;&amp;nbsp;during the request/response cycle.&amp;nbsp;&lt;/span&gt;&lt;span x=&quot;y&quot;&gt;&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;You can cache the output of specific views, you can cache only the pieces that are difficult to produce, you can cache a portion of template, or you can cache your entire site. Django also works well with &quot;upstream&quot; caches, such as varnish and browser-based caches. These are the types of caches that you don&#39;t directly control but to which you can provide hints (via HTTP headers) about which parts of your site should be cached, and how. If you want more information about this you can read the &lt;a href=&quot;http://docs.djangoproject.com/en/1.1/topics/cache/#topics-cach&quot;&gt;django&#39;s cache documentation&lt;/a&gt;.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span x=&quot;y&quot;&gt;&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;Varnish is also a beast by itself, you can fine&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px;&quot; x=&quot;y&quot;&gt;&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;tuned&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span x=&quot;y&quot;&gt;&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;&amp;nbsp;it&lt;/span&gt;&lt;/span&gt;&lt;span x=&quot;y&quot;&gt;&lt;span style=&quot;font-family: Verdana, sans-serif;&quot;&gt;&amp;nbsp;to suit your particular situation and you can used it to do much more in your infrastructure than just upstream cache of your dynamic web site.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Verdana, sans-serif;&quot; x=&quot;y&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[1]&amp;nbsp;&lt;a href=&quot;http://yml-blog.blogspot.com/2009/12/flup-vs-uwsgi-with-cherokee.html&quot; style=&quot;color: #664d9f; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://yml-blog.blogspot.com/2009/12/flup-vs-uwsgi-with-cherokee.html&lt;/a&gt;&lt;br /&gt;
[2]&amp;nbsp;&lt;a href=&quot;http://varnish.projects.linpro.no/&quot; style=&quot;color: #664d9f; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://varnish.projects.linpro.no/&lt;/a&gt;&lt;br /&gt;
[3]&amp;nbsp;&lt;a href=&quot;http://docs.djangoproject.com/en/1.1/topics/cache/#topics-cache&quot; style=&quot;color: #664d9f; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://docs.djangoproject.com/en/1.1/topics/cache/#topics-cache&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/2227493044866346926/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2010/01/response-time-optimisation-with-varnish.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/2227493044866346926'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/2227493044866346926'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2010/01/response-time-optimisation-with-varnish.html' title='Response time optimisation with Varnish'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJwo3hJMb1PRPehGUj8gn1CjdX_7RIRCTnL24V1GwZGT2FKDPDB9WSiENGYxD7dft2hHgFcO0MzxDUhkLiwOVnKEuSRvOc-colhAK6kLy82JJfQnRSRNK9XU1Rbfd8r4XGtYd5CjxqKdA/s72-c/uwsgi_new_mount_point.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-8390263704516052798</id><published>2009-12-29T12:54:00.001-04:00</published><updated>2009-12-30T18:17:23.436-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cherokee"/><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="uWSGI"/><title type='text'>flup vs uWSGI with cherokee</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;Cherokee is one of this many web servers that supports both deployment strategies. Since I have recently blogs about&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;uwsgi&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;&amp;nbsp;&amp;nbsp;Several people asked me how do they compare performance wise. Up to now my answer was I don&#39;t know and I don&#39;t really care because this is not the most critical aspect in my opinion.&lt;br /&gt;
&lt;br /&gt;
However since this question keep comming I have decided to give it a more accurate answer. This blog post takes you on how to setup cherokee with both alternatives and compare their performance. The guinea pig application I have chosen to do this comparison is django-cms because I think it represents fairly well the overhead introduced by the dynamic generation of the page.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-weight: bold; text-decoration: underline;&quot;&gt;Note :&lt;/span&gt;&amp;nbsp;in this example django-cms will not&amp;nbsp;be&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;mounted&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;under &quot;/&quot; this will cause all&amp;nbsp;sort of issues if you try to do this but none of them impact the particular page that we try to access.&lt;br /&gt;
&lt;br /&gt;
One of the nice Cherokee&#39;s feature is its documentation, here it is the 2 pages related to this blog post :&lt;br /&gt;
*&amp;nbsp;&lt;a href=&quot;http://www.cherokee-project.com/doc/cookbook_django.html&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;flup [1]&lt;/a&gt;&lt;br /&gt;
*&amp;nbsp;&lt;a href=&quot;http://www.cherokee-project.com/doc/cookbook_uwsgi.html&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;uswgi [2]&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
In this blog post I will assume that django-cms is installed, its example project properly configured and that you have added a page to test test against it. I will aslo assume that cherokee,&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;flup&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;and&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;uwsgi&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;are setup.&lt;br /&gt;
During this test I have used uwsgi changeset: 145:&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;db356717823c&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;.&lt;br /&gt;
&lt;br /&gt;
Cherokee provides 2 wizard that enables you to get running very fast. First thing first you will need to start cherokee admin :&lt;br /&gt;
&lt;span style=&quot;font-family: monospace;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;white-space: pre;&quot;&gt;&lt;span style=&quot;font-family: &#39;Times New Roman&#39;;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;white-space: normal;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;sudo cherokee-admin -u
&lt;/code&gt;
&lt;/pre&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: 24px; font-weight: bold;&quot;&gt;media admin&lt;/span&gt;&lt;br /&gt;
Add a rule to serve the &quot;/media/admin&quot; in my case these files are in &quot;/opt/www/django-&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;cms_tutorial&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;/&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;ve&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;/lib/python2.6/site-packages/django/contrib/admin/media&quot;&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhhRP1fAocWgK3TeOAs5Tux_aBhUzyxEmMBblZW_yyDpCejWdzL9XQqxzK83DFQR0McSlHPzN82I5vWayRRJuq77F7qHdrSeE-PfTpTPz9-3PejXFgpwhpVbLR_Rr7yJM7sUoi4m-CWiI/s1600-h/admin_media_rule.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhhRP1fAocWgK3TeOAs5Tux_aBhUzyxEmMBblZW_yyDpCejWdzL9XQqxzK83DFQR0McSlHPzN82I5vWayRRJuq77F7qHdrSeE-PfTpTPz9-3PejXFgpwhpVbLR_Rr7yJM7sUoi4m-CWiI/s640/admin_media_rule.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;div class=&quot;CJ&quot; contenteditable=&quot;false&quot; style=&quot;display: inline-table; font-family: arial, sans-serif; font-size: small; height: auto; margin-bottom: 0px; margin-left: 2px; margin-right: 0px; margin-top: 5px; position: relative; white-space: normal; width: auto;&quot;&gt;&lt;/div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: 24px; font-weight: bold;&quot;&gt;media cms&lt;/span&gt;&lt;br /&gt;
Add a rule to serve the &quot;/media/cms&quot; in my case these files are in&amp;nbsp;&quot;/opt/www/django-&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;cms_tutorial&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;/&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;ve&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;/src/django-cms/cms/media/&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;cms&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&quot;&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjW4acu8z_3SglzXcJPmv9rv8WRgZgBQp0U0RXjKg3zFDMgqA49ribx-IGYfZZmhcnSc4p7FI0Tz4sSmIEmnbGoIWnc040PYDZTyx8VtrvHIyM0NSHj3HEO6_c_s0HgbeS7lup3dieFBBE/s1600-h/media_cms_rule.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjW4acu8z_3SglzXcJPmv9rv8WRgZgBQp0U0RXjKg3zFDMgqA49ribx-IGYfZZmhcnSc4p7FI0Tz4sSmIEmnbGoIWnc040PYDZTyx8VtrvHIyM0NSHj3HEO6_c_s0HgbeS7lup3dieFBBE/s640/media_cms_rule.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: 24px; font-weight: bold;&quot;&gt;Flup&lt;/span&gt;&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: 24px; font-weight: bold;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
In this case you will need to use the wizard called &quot;Plateforms &amp;gt; django&quot;. This wizard takes the web directory and the project directory. In our case :&lt;br /&gt;
&lt;br /&gt;
web directory : /flup&lt;br /&gt;
project directory : /opt/www/django-&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;cms_tutorial&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;/&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;ve&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;/src/django-cms/example&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: arial, sans-serif; font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjn3Tra8VdC9HEypdPmEzMzvulc1Ti0XxrQOADRi-GU1IfJMnVyNpft2dDWxeJEPJRwFyZLDQA8jaRzsBW9pQcU1xLCmHu_-jUlEPN3fRMbVS6WiDJUdMr1-Ho9C3Br-YZ0UPwgt4JVk_Q/s1600-h/django_wizard.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjn3Tra8VdC9HEypdPmEzMzvulc1Ti0XxrQOADRi-GU1IfJMnVyNpft2dDWxeJEPJRwFyZLDQA8jaRzsBW9pQcU1xLCmHu_-jUlEPN3fRMbVS6WiDJUdMr1-Ho9C3Br-YZ0UPwgt4JVk_Q/s640/django_wizard.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;Then we need to modify the interpreter command from the &quot;django 1&quot; source because we used virtualenv instead of the global python.&lt;br /&gt;
So we need to change it from :&lt;br /&gt;
&lt;span style=&quot;font-family: monospace;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;white-space: pre;&quot;&gt;&lt;span style=&quot;font-family: &#39;Times New Roman&#39;;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;white-space: normal;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;python /opt/www/django-cms_tutorial/ve/src/django-cms/example/&lt;a href=&quot;http://manage.py/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;manage.py&lt;/a&gt;&amp;nbsp;runfcgi protocol=scgi host=127.0.0.1 port=37134
&lt;/code&gt;&lt;/pre&gt;to:&lt;br /&gt;
&lt;span style=&quot;font-family: monospace;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;white-space: pre;&quot;&gt;&lt;span style=&quot;font-family: &#39;Times New Roman&#39;;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;white-space: normal;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;/opt/www/django-cms_tutorial/ve/src/django-cms/example/start_&lt;a href=&quot;http://fcgi.sh/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;fcgi.sh&lt;/a&gt;
&lt;/code&gt;
&lt;/pre&gt;&lt;br /&gt;
Then you need to create this file called&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;start_&lt;/span&gt;&lt;a href=&quot;http://fcgi.sh/&quot; style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px; color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;fcgi&lt;/a&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&lt;a href=&quot;http://fcgi.sh/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;.sh&lt;/a&gt;&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
example$ cat &amp;gt; start_&lt;a href=&quot;http://fcgi.sh/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;fcgi.sh&lt;/a&gt;&amp;nbsp;&amp;lt;&amp;lt;&amp;nbsp;EOF
&amp;gt; source /opt/www/django-cms_tutorial/ve/bin/activate;
&amp;gt; python /opt/www/django-cms_tutorial/ve/src/django-cms/example/&lt;a href=&quot;http://manage.py/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;manage.py&lt;/a&gt;&amp;nbsp;runfcgi protocol=scgi host=127.0.0.1 port=37134
&amp;gt; EOF
&lt;/code&gt;
&lt;/pre&gt;&lt;br /&gt;
chmod&amp;nbsp;+x&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;start_&lt;/span&gt;&lt;a href=&quot;http://fcgi.sh/&quot; style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px; color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;fcgi&lt;/a&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&lt;a href=&quot;http://fcgi.sh/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;.sh&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Restarts cherokee and direct your browser to &quot;/flup/&quot;.&lt;br /&gt;
&lt;h2&gt;uWSGI&lt;/h2&gt;&lt;br /&gt;
In order to setup the&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;uwsgi&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;server we are going to use the cherokee&#39;s wizard and then modify the result to adapt it to our particular use case.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;uwsgi&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;wizard&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;takes&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;only one argument the path to the&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;uwsgi&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;configuration file :&lt;br /&gt;
&lt;br /&gt;
/opt/www/django-cms_tutorial/ve/src/django-cms/example/example_&lt;a href=&quot;http://uwsgi.py/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;uwsgi.py&lt;/a&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: arial, sans-serif;&quot;&gt;&lt;span style=&quot;font-family: &#39;Times New Roman&#39;;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7K_bZDzORx6xPll3fbEGHXRIPjPGLhXMqHxiuad7RCEMmyYH_IDHtacs3sGYQa_Eg5chlLEc6MwnjrxzRYEPy8S9L5ItTy-C3PB8KMsepQ8Qbnfgt2htH1WeKGoInPgcANpxmknuoGFg/s1600-h/uwsgi_wizard.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7K_bZDzORx6xPll3fbEGHXRIPjPGLhXMqHxiuad7RCEMmyYH_IDHtacs3sGYQa_Eg5chlLEc6MwnjrxzRYEPy8S9L5ItTy-C3PB8KMsepQ8Qbnfgt2htH1WeKGoInPgcANpxmknuoGFg/s640/uwsgi_wizard.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;span style=&quot;font-family: arial, sans-serif; font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
We are going to edit the&amp;nbsp;interpreter command to adapt it a bit to&amp;nbsp;our use case :&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
/usr/local/bin/uwsgi -s 127.0.0.1:42597 -t 10 -M -p 1 -C -w example.example_uwsgi -H /opt/www/django-cms_tutorial/ve/src/django-cms
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
to :&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
/usr/local/bin/uwsgi -s 127.0.0.1:46075 -t 10 -M -p 10 -C -w example.example_uwsgi&amp;nbsp;-H&amp;nbsp;/opt/www/django-cms_tutorial/ve/
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
The&amp;nbsp;file called&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;example_uwsgi&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;looks like this :&lt;br /&gt;
&lt;span style=&quot;font-family: monospace;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;white-space: pre;&quot;&gt;&lt;span style=&quot;font-family: &#39;Times New Roman&#39;;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;white-space: normal;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;import os
import django.core.handlers.wsgi
# Set the django settings and define the wsgi app
os.environ[&#39;DJANGO_SETTINGS_MODULE&#39;] = &#39;example.settings&#39;
application = django.core.handlers.wsgi.WSGIHandler()
# Mount the application to the url
applications = {&#39;/uwsgi&#39;:application,&amp;nbsp;}
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Comparison:&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;uwsgi&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;vs&amp;nbsp;flup&lt;/h2&gt;&lt;h3 style=&quot;font-family: arial, sans-serif; font-size: small;&quot;&gt;flup&lt;/h3&gt;&lt;br /&gt;
Resource usage for concurency equal to 50 :&lt;br /&gt;
&lt;span style=&quot;font-family: arial, sans-serif; font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgq8dIjYoSrosEQssttLAxuy81UUlb2l5zgNdieeCjDiercpJOidSTzyE0UUdpi1jXkieEUKbMayxzSYALZIVq2DTRh908o6063WB3vZ2gcTVRdui1NTfd9Kj_QI069XRinPxMPQOca7CU/s1600-h/flup_resource_usage.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgq8dIjYoSrosEQssttLAxuy81UUlb2l5zgNdieeCjDiercpJOidSTzyE0UUdpi1jXkieEUKbMayxzSYALZIVq2DTRh908o6063WB3vZ2gcTVRdui1NTfd9Kj_QI069XRinPxMPQOca7CU/s640/flup_resource_usage.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;span style=&quot;font-family: arial, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
(ve)yml@yml-laptop:django-cms_tutorial$ ab -n 1000 -c&amp;nbsp;50&amp;nbsp;&lt;a href=&quot;http://192.168.1.18:8080/flup/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://192.168.1.18:8080/flup/&lt;/a&gt;&lt;br /&gt;
This is ApacheBench, Version 2.3 &amp;lt;$Revision: 655654 $&amp;gt;&lt;br /&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd,&amp;nbsp;&lt;a href=&quot;http://www.zeustech.net/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.zeustech.net/&lt;/a&gt;&lt;br /&gt;
Licensed to The Apache Software Foundation,&amp;nbsp;&lt;a href=&quot;http://www.apache.org/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.apache.org/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Benchmarking 192.168.1.18 (be patient)&lt;br /&gt;
Completed 100 requests&lt;br /&gt;
Completed 200 requests&lt;br /&gt;
Completed 300 requests&lt;br /&gt;
Completed 400 requests&lt;br /&gt;
Completed 500 requests&lt;br /&gt;
Completed 600 requests&lt;br /&gt;
Completed 700 requests&lt;br /&gt;
Completed 800 requests&lt;br /&gt;
Completed 900 requests&lt;br /&gt;
Completed 1000 requests&lt;br /&gt;
Finished 1000 requests&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Server Software: Cherokee/0.99.37&lt;br /&gt;
Server Hostname: 192.168.1.18&lt;br /&gt;
Server Port: 8080&lt;br /&gt;
&lt;br /&gt;
Document Path: /flup/&lt;br /&gt;
Document Length: 3485 bytes&lt;br /&gt;
&lt;br /&gt;
Concurrency Level: 50&lt;br /&gt;
Time taken for tests: 82.385 seconds&lt;br /&gt;
Complete requests: 1000&lt;br /&gt;
Failed requests: 0&lt;br /&gt;
Write errors: 0&lt;br /&gt;
Total transferred: 3688000 bytes&lt;br /&gt;
HTML transferred: 3485000 bytes&lt;br /&gt;
Requests per second: 12.14 [#/sec] (mean)&lt;br /&gt;
Time per request: 4119.256 [ms] (mean)&lt;br /&gt;
Time per request: 82.385 [ms] (mean, across all concurrent requests)&lt;br /&gt;
Transfer rate: 43.72 [Kbytes/sec] received&lt;br /&gt;
&lt;br /&gt;
Connection Times (ms)&lt;br /&gt;
min mean[+/-sd] median max&lt;br /&gt;
Connect: 0 0 0.4 0 3&lt;br /&gt;
Processing: 750 4042 1424.0 3791 11665&lt;br /&gt;
Waiting: 750 4041 1424.0 3791 11665&lt;br /&gt;
Total: 753 4042 1424.2 3791 11666&lt;br /&gt;
&lt;br /&gt;
Percentage of the requests served within a certain time (ms)&lt;br /&gt;
50% 3791&lt;br /&gt;
66% 4098&lt;br /&gt;
75% 4350&lt;br /&gt;
80% 4509&lt;br /&gt;
90% 4972&lt;br /&gt;
95% 5566&lt;br /&gt;
98% 10388&lt;br /&gt;
99% 10974&lt;br /&gt;
100% 11666 (longest request)&lt;br /&gt;
&lt;br /&gt;
(ve)yml@yml-laptop:django-cms_tutorial$ ab -n 1000 -c 100&amp;nbsp;&lt;a href=&quot;http://192.168.1.18:8080/flup/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://192.168.1.18:8080/flup/&lt;/a&gt;&lt;br /&gt;
This is ApacheBench, Version 2.3 &amp;lt;$Revision: 655654 $&amp;gt;&lt;br /&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd,&amp;nbsp;&lt;a href=&quot;http://www.zeustech.net/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.zeustech.net/&lt;/a&gt;&lt;br /&gt;
Licensed to The Apache Software Foundation,&amp;nbsp;&lt;a href=&quot;http://www.apache.org/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.apache.org/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Benchmarking 192.168.1.18 (be patient)&lt;br /&gt;
Completed 100 requests&lt;br /&gt;
Completed 200 requests&lt;br /&gt;
Completed 300 requests&lt;br /&gt;
Completed 400 requests&lt;br /&gt;
Completed 500 requests&lt;br /&gt;
Completed 600 requests&lt;br /&gt;
Completed 700 requests&lt;br /&gt;
Completed 800 requests&lt;br /&gt;
Completed 900 requests&lt;br /&gt;
Completed 1000 requests&lt;br /&gt;
Finished 1000 requests&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Server Software: Cherokee/0.99.37&lt;br /&gt;
Server Hostname: 192.168.1.18&lt;br /&gt;
Server Port: 8080&lt;br /&gt;
&lt;br /&gt;
Document Path: /flup/&lt;br /&gt;
Document Length: 3485 bytes&lt;br /&gt;
&lt;br /&gt;
Concurrency Level: 100&lt;br /&gt;
Time taken for tests: 82.614 seconds&lt;br /&gt;
Complete requests: 1000&lt;br /&gt;
Failed requests: 0&lt;br /&gt;
Write errors: 0&lt;br /&gt;
Total transferred: 3688000 bytes&lt;br /&gt;
HTML transferred: 3485000 bytes&lt;br /&gt;
Requests per second: 12.10 [#/sec] (mean)&lt;br /&gt;
Time per request: 8261.424 [ms] (mean)&lt;br /&gt;
Time per request: 82.614 [ms] (mean, across all concurrent requests)&lt;br /&gt;
Transfer rate: 43.59 [Kbytes/sec] received&lt;br /&gt;
&lt;br /&gt;
Connection Times (ms)&lt;br /&gt;
min mean[+/-sd] median max&lt;br /&gt;
Connect: 0 0 0.8 0 3&lt;br /&gt;
Processing: 1209 7920 1519.3 7694 14118&lt;br /&gt;
Waiting: 1209 7920 1518.1 7694 14118&lt;br /&gt;
Total: 1213 7920 1519.5 7694 14120&lt;br /&gt;
&lt;br /&gt;
Percentage of the requests served within a certain time (ms)&lt;br /&gt;
50% 7694&lt;br /&gt;
66% 8161&lt;br /&gt;
75% 8405&lt;br /&gt;
80% 8574&lt;br /&gt;
90% 9215&lt;br /&gt;
95% 10456&lt;br /&gt;
98% 13048&lt;br /&gt;
99% 13983&lt;br /&gt;
100% 14120 (longest request)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Server Software: Cherokee/0.99.37&lt;br /&gt;
Server Hostname: 192.168.1.18&lt;br /&gt;
Server Port: 8080&lt;br /&gt;
&lt;br /&gt;
Document Path: /flup/&lt;br /&gt;
Document Length: 3485 bytes&lt;br /&gt;
&lt;br /&gt;
Concurrency Level: 100&lt;br /&gt;
Time taken for tests: 81.737 seconds&lt;br /&gt;
Complete requests: 1000&lt;br /&gt;
Failed requests: 0&lt;br /&gt;
Write errors: 0&lt;br /&gt;
Total transferred: 3688000 bytes&lt;br /&gt;
HTML transferred: 3485000 bytes&lt;br /&gt;
Requests per second: 12.23 [#/sec] (mean)&lt;br /&gt;
Time per request: 8173.722 [ms] (mean)&lt;br /&gt;
Time per request: 81.737 [ms] (mean, across all concurrent requests)&lt;br /&gt;
Transfer rate: 44.06 [Kbytes/sec] received&lt;br /&gt;
&lt;br /&gt;
Connection Times (ms)&lt;br /&gt;
min mean[+/-sd] median max&lt;br /&gt;
Connect: 0 0 1.2 0 5&lt;br /&gt;
Processing: 830 7843 1455.0 7574 14385&lt;br /&gt;
Waiting: 830 7843 1455.0 7574 14385&lt;br /&gt;
Total: 835 7843 1455.3 7574 14388&lt;br /&gt;
&lt;br /&gt;
Percentage of the requests served within a certain time (ms)&lt;br /&gt;
50% 7574&lt;br /&gt;
66% 7912&lt;br /&gt;
75% 8137&lt;br /&gt;
80% 8335&lt;br /&gt;
90% 8893&lt;br /&gt;
95% 10624&lt;br /&gt;
98% 13435&lt;br /&gt;
99% 13670&lt;br /&gt;
100% 14388 (longest request)&lt;br /&gt;
&lt;br /&gt;
Resource usage for concurency equal to 100 :&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhc1pLbE2zf5Zzu7yyUt0FdvnkCOGgUkSSKKniGb0P9KbkQPEuvpU3k38H0wXeB-L_9YHvMH-YfOBtejWPCjIVickSsAJ6db2hzN9dmpGWlOQAfVHV-zR0eJvBwm5WCg7lwsJyDScs_YqU/s1600-h/flup_ressource_usage_c100.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhc1pLbE2zf5Zzu7yyUt0FdvnkCOGgUkSSKKniGb0P9KbkQPEuvpU3k38H0wXeB-L_9YHvMH-YfOBtejWPCjIVickSsAJ6db2hzN9dmpGWlOQAfVHV-zR0eJvBwm5WCg7lwsJyDScs_YqU/s640/flup_ressource_usage_c100.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;h3 style=&quot;font-family: arial, sans-serif; font-size: small;&quot;&gt;uwsgi&lt;/h3&gt;&lt;br /&gt;
uWSGI&amp;nbsp;resource usage for&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;concurrency&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;equal to 50&amp;nbsp;:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-DnEbuD-m98EmFmj3PTxtBZKHPGkn2TpU9JSgrkExgtiDwtXDGVbYW1nzk77y2VmMyEk0u5MWZQDRI9CQ1939J5b_XoG_7GjsMwz33H0B_JuVFd_d6nyGdO-hLaaCaSDSSQ-8zp9sV_Y/s1600-h/uwsgi_resource_usage.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-DnEbuD-m98EmFmj3PTxtBZKHPGkn2TpU9JSgrkExgtiDwtXDGVbYW1nzk77y2VmMyEk0u5MWZQDRI9CQ1939J5b_XoG_7GjsMwz33H0B_JuVFd_d6nyGdO-hLaaCaSDSSQ-8zp9sV_Y/s640/uwsgi_resource_usage.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
(ve)yml@yml-laptop:django-cms_tutorial$ ab -n 1000 -c&amp;nbsp;50&amp;nbsp;&lt;a href=&quot;http://192.168.1.18:8080/uwsgi/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://192.168.1.18:8080/uwsgi/&lt;/a&gt;&lt;br /&gt;
This is ApacheBench, Version 2.3 &amp;lt;$Revision: 655654 $&amp;gt;&lt;br /&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd,&amp;nbsp;&lt;a href=&quot;http://www.zeustech.net/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.zeustech.net/&lt;/a&gt;&lt;br /&gt;
Licensed to The Apache Software Foundation,&amp;nbsp;&lt;a href=&quot;http://www.apache.org/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.apache.org/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Benchmarking 192.168.1.18 (be patient)&lt;br /&gt;
Completed 100 requests&lt;br /&gt;
Completed 200 requests&lt;br /&gt;
Completed 300 requests&lt;br /&gt;
Completed 400 requests&lt;br /&gt;
Completed 500 requests&lt;br /&gt;
Completed 600 requests&lt;br /&gt;
Completed 700 requests&lt;br /&gt;
Completed 800 requests&lt;br /&gt;
Completed 900 requests&lt;br /&gt;
Completed 1000 requests&lt;br /&gt;
Finished 1000 requests&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Server Software: Cherokee/0.99.37&lt;br /&gt;
Server Hostname: 192.168.1.18&lt;br /&gt;
Server Port: 8080&lt;br /&gt;
&lt;br /&gt;
Document Path: /uwsgi/&lt;br /&gt;
Document Length: 3494 bytes&lt;br /&gt;
&lt;br /&gt;
Concurrency Level: 50&lt;br /&gt;
Time taken for tests: 84.013 seconds&lt;br /&gt;
Complete requests: 1000&lt;br /&gt;
Failed requests: 0&lt;br /&gt;
Write errors: 0&lt;br /&gt;
Total transferred: 3697000 bytes&lt;br /&gt;
HTML transferred: 3494000 bytes&lt;br /&gt;
Requests per second: 11.90 [#/sec] (mean)&lt;br /&gt;
Time per request: 4200.629 [ms] (mean)&lt;br /&gt;
Time per request: 84.013 [ms] (mean, across all concurrent requests)&lt;br /&gt;
Transfer rate: 42.97 [Kbytes/sec] received&lt;br /&gt;
&lt;br /&gt;
Connection Times (ms)&lt;br /&gt;
min mean[+/-sd] median max&lt;br /&gt;
Connect: 0 0 0.3 0 2&lt;br /&gt;
Processing: 682 4115 472.7 4189 4707&lt;br /&gt;
Waiting: 682 4115 472.7 4189 4707&lt;br /&gt;
Total: 684 4115 472.5 4189 4707&lt;br /&gt;
&lt;br /&gt;
Percentage of the requests served within a certain time (ms)&lt;br /&gt;
50% 4189&lt;br /&gt;
66% 4246&lt;br /&gt;
75% 4286&lt;br /&gt;
80% 4316&lt;br /&gt;
90% 4378&lt;br /&gt;
95% 4440&lt;br /&gt;
98% 4515&lt;br /&gt;
99% 4544&lt;br /&gt;
100% 4707 (longest request)&lt;br /&gt;
(ve)yml@yml-laptop:django-cms_tutorial$ ab -n 1000 -c 100&amp;nbsp;&lt;a href=&quot;http://192.168.1.18:8080/uwsgi/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://192.168.1.18:8080/uwsgi/&lt;/a&gt;&lt;br /&gt;
This is ApacheBench, Version 2.3 &amp;lt;$Revision: 655654 $&amp;gt;&lt;br /&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd,&amp;nbsp;&lt;a href=&quot;http://www.zeustech.net/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.zeustech.net/&lt;/a&gt;&lt;br /&gt;
Licensed to The Apache Software Foundation,&amp;nbsp;&lt;a href=&quot;http://www.apache.org/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.apache.org/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Benchmarking 192.168.1.18 (be patient)&lt;br /&gt;
Completed 100 requests&lt;br /&gt;
Completed 200 requests&lt;br /&gt;
Completed 300 requests&lt;br /&gt;
Completed 400 requests&lt;br /&gt;
Completed 500 requests&lt;br /&gt;
Completed 600 requests&lt;br /&gt;
Completed 700 requests&lt;br /&gt;
Completed 800 requests&lt;br /&gt;
apr_socket_recv: Connection reset by peer (104)&lt;br /&gt;
Total of 839 requests completed&lt;br /&gt;
&lt;br /&gt;
This&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;reveals&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;the&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;conservative&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;nature&amp;nbsp;of&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;uwsgi&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;. I&amp;nbsp;had a discussion about this with Roberto De Ioris.&amp;nbsp;He&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;mades&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;a&amp;nbsp;&lt;a href=&quot;http://lists.unbit.it/pipermail/uwsgi/2009-December/000040.html&quot; style=&quot;color: #664d9f; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;detailled answer&lt;/a&gt;&amp;nbsp;detailled&amp;nbsp;answer explaining the situation. So the bottom line is that we will need to modify our configuration and increase the socket timeout &quot;-z&quot; and the socket listen queue &quot;-l&quot;&lt;br /&gt;
&lt;span style=&quot;font-family: monospace;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;white-space: pre;&quot;&gt;&lt;span style=&quot;font-family: &#39;Times New Roman&#39;;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;white-space: normal;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;/usr/local/bin/uwsgi -i -l 120 -z 60 -p 10 -M -s 127.0.0.1:46075 -w example.example_uwsgi -H /opt/www/django-cms_tutorial/ve/
&lt;/code&gt;
&lt;/pre&gt;(ve)yml@yml-laptop:django-cms_tutorial$ ab -n 1000 -c 100&amp;nbsp;&lt;a href=&quot;http://192.168.1.18:8080/uwsgi/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://192.168.1.18:8080/uwsgi/&lt;/a&gt;&lt;br /&gt;
This is ApacheBench, Version 2.3 &amp;lt;$Revision: 655654 $&amp;gt;&lt;br /&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd,&amp;nbsp;&lt;a href=&quot;http://www.zeustech.net/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.zeustech.net/&lt;/a&gt;&lt;br /&gt;
Licensed to The Apache Software Foundation,&amp;nbsp;&lt;a href=&quot;http://www.apache.org/&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.apache.org/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Benchmarking 192.168.1.18 (be patient)&lt;br /&gt;
Completed 100 requests&lt;br /&gt;
Completed 200 requests&lt;br /&gt;
Completed 300 requests&lt;br /&gt;
Completed 400 requests&lt;br /&gt;
Completed 500 requests&lt;br /&gt;
Completed 600 requests&lt;br /&gt;
Completed 700 requests&lt;br /&gt;
Completed 800 requests&lt;br /&gt;
Completed 900 requests&lt;br /&gt;
Completed 1000 requests&lt;br /&gt;
Finished 1000 requests&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Server Software: Cherokee/0.99.37&lt;br /&gt;
Server Hostname: 192.168.1.18&lt;br /&gt;
Server Port: 8080&lt;br /&gt;
&lt;br /&gt;
Document Path: /uwsgi/&lt;br /&gt;
Document Length: 3494 bytes&lt;br /&gt;
&lt;br /&gt;
Concurrency Level: 100&lt;br /&gt;
Time taken for tests: 86.246 seconds&lt;br /&gt;
Complete requests: 1000&lt;br /&gt;
Failed requests: 0&lt;br /&gt;
Write errors: 0&lt;br /&gt;
Total transferred: 3697000 bytes&lt;br /&gt;
HTML transferred: 3494000 bytes&lt;br /&gt;
Requests per second: 11.59 [#/sec] (mean)&lt;br /&gt;
Time per request: 8624.594 [ms] (mean)&lt;br /&gt;
Time per request: 86.246 [ms] (mean, across all concurrent requests)&lt;br /&gt;
Transfer rate: 41.86 [Kbytes/sec] received&lt;br /&gt;
&lt;br /&gt;
Connection Times (ms)&lt;br /&gt;
min mean[+/-sd] median max&lt;br /&gt;
Connect: 0 1 1.7 0 7&lt;br /&gt;
Processing: 762 8248 1460.7 8509 9706&lt;br /&gt;
Waiting: 762 8248 1460.7 8509 9706&lt;br /&gt;
Total: 766 8248 1459.5 8509 9706&lt;br /&gt;
&lt;br /&gt;
Percentage of the requests served within a certain time (ms)&lt;br /&gt;
50% 8509&lt;br /&gt;
66% 8669&lt;br /&gt;
75% 8852&lt;br /&gt;
80% 8998&lt;br /&gt;
90% 9182&lt;br /&gt;
95% 9360&lt;br /&gt;
98% 9474&lt;br /&gt;
99% 9566&lt;br /&gt;
100% 9706 (longest request)&lt;br /&gt;
&lt;br /&gt;
uWSGI resource usage for concurrency equal to 100 :&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: arial, sans-serif; font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEA2i-6KNFIUUn1oe4a_KbR4m5ab5oeUko8sNx_iBwQxvZ6vDPMf2cgdLJP3AhaizTn-NdBADABQkOCcYx9CrEAQnpwwvKRkH5iiL3oiG5jivJVQUJbPp92qydk5HKuGu4o5kmXAV1q1E/s1600-h/uwsgi_ressource_usage_c100.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEA2i-6KNFIUUn1oe4a_KbR4m5ab5oeUko8sNx_iBwQxvZ6vDPMf2cgdLJP3AhaizTn-NdBADABQkOCcYx9CrEAQnpwwvKRkH5iiL3oiG5jivJVQUJbPp92qydk5HKuGu4o5kmXAV1q1E/s640/uwsgi_ressource_usage_c100.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;flup&amp;nbsp;is&amp;nbsp;slightly faster than&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;uWSGI&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;at this point&amp;nbsp;but&amp;nbsp;this&amp;nbsp;has to be put&amp;nbsp;into&amp;nbsp;perspective and you need&amp;nbsp;to&amp;nbsp;take&amp;nbsp;into consideration the&amp;nbsp;features&amp;nbsp;that&amp;nbsp;come&amp;nbsp;with uWSGI, an&amp;nbsp;exhaustive&amp;nbsp;list can be found &lt;a href=&quot;http://projects.unbit.it/uwsgi/&quot;&gt;here&lt;/a&gt;.&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;u&lt;/span&gt;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;WSGI&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;is&amp;nbsp;unable to complete the&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;ab&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;test with the following argument -n&amp;nbsp;1000 -c 100 with&amp;nbsp;its&amp;nbsp;default settings you will need to adjust the timeout socket and&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;socket&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;listen queue. However it is interesting to note that the memory footprint for&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;uwsgi&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;is&amp;nbsp;lower by an order of magnitude and that&amp;nbsp;my laptop&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;re&lt;/span&gt;&lt;span style=&quot;border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;mains&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;responsive during the&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;ab&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;test using&amp;nbsp;&lt;span style=&quot;border-bottom-color: rgb(255, 0, 0); border-bottom-style: dotted; border-bottom-width: 1px;&quot;&gt;uWSGI&lt;/span&gt;&lt;span style=&quot;-webkit-user-modify: read-only;&quot;&gt;&lt;/span&gt;&amp;nbsp;where it was&amp;nbsp;almost&amp;nbsp;taken down by same test with flup.&lt;br /&gt;
&lt;br /&gt;
[1]&amp;nbsp;&lt;a href=&quot;http://www.cherokee-project.com/doc/cookbook_django.html&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.cherokee-project.com/doc/cookbook_django.html&lt;/a&gt;&lt;br /&gt;
[2]&amp;nbsp;&lt;a href=&quot;http://www.cherokee-project.com/doc/cookbook_uwsgi.html&quot; style=&quot;color: #003ea8; font-family: arial, sans-serif;&quot; target=&quot;_blank&quot; x=&quot;y&quot;&gt;http://www.cherokee-project.com/doc/cookbook_uwsgi.html&lt;/a&gt;&lt;br /&gt;
[3]&amp;nbsp;&lt;a href=&quot;http://projects.unbit.it/uwsgi/&quot;&gt;http://projects.unbit.it/uwsgi/&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/8390263704516052798/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2009/12/flup-vs-uwsgi-with-cherokee.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/8390263704516052798'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/8390263704516052798'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2009/12/flup-vs-uwsgi-with-cherokee.html' title='flup vs uWSGI with cherokee'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhhRP1fAocWgK3TeOAs5Tux_aBhUzyxEmMBblZW_yyDpCejWdzL9XQqxzK83DFQR0McSlHPzN82I5vWayRRJuq77F7qHdrSeE-PfTpTPz9-3PejXFgpwhpVbLR_Rr7yJM7sUoi4m-CWiI/s72-c/admin_media_rule.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-774029655161357915</id><published>2009-12-28T22:30:00.001-04:00</published><updated>2009-12-28T22:30:58.419-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="bpython"/><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><title type='text'>Add bpython to django&#39;s shell management command</title><content type='html'>Like many of us I am spending a lot of time in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Read-eval-print_loop&quot;&gt;REPL&lt;/a&gt;&amp;nbsp;loop to develop django code and this &lt;a href=&quot;http://bugs.python.org/issue5833&quot;&gt;bug&lt;/a&gt;&amp;nbsp;in readline cost me an extra backspace each time&amp;nbsp;&amp;nbsp;I hit &quot;tab&quot; in &lt;a href=&quot;http://ipython.scipy.org/moin/&quot;&gt;ipython&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
This &lt;a href=&quot;http://uswaretech.com/blog/2009/12/using-bpython-shell-with-django-and-some-ipython-features-you-should-know/&quot;&gt;blog post&lt;/a&gt;&amp;nbsp;capture my attention so I have decided to give &lt;a href=&quot;http://bpython-interpreter.org/&quot;&gt;bpython&lt;/a&gt; a try and so far I have been impressed by what I have seen. Here it is a management command that adds the support for bpython to django&#39;s shell management command.&lt;br /&gt;
&lt;span style=&quot;font-family: monospace;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;&lt;span style=&quot;font-family: &#39;Times New Roman&#39;;&quot;&gt;&lt;span style=&quot;white-space: normal;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;import os
from django.core.management.base import NoArgsCommand
from optparse import make_option
from IPython.Shell import IPShell

def start_plain_shell():
    import code
    # Set up a dictionary to serve as the environment for the shell, so
    # that tab completion works on objects that are imported at runtime.
    # See ticket 5082.
    imported_objects = {}
    try: # Try activating rlcompleter, because it&#39;s handy.
        import readline
    except ImportError:
        pass
    else:
        # We don&#39;t have to wrap the following import in a &#39;try&#39;, because
        # we already know &#39;readline&#39; was imported successfully.
        import rlcompleter
        readline.set_completer(rlcompleter.Completer(imported_objects).complete)
        readline.parse_and_bind(&quot;tab:complete&quot;)

    # We want to honor both $PYTHONSTARTUP and .pythonrc.py, so follow system
    # conventions and get $PYTHONSTARTUP first then import user.
    if not use_plain: 
        pythonrc = os.environ.get(&quot;PYTHONSTARTUP&quot;) 
        if pythonrc and os.path.isfile(pythonrc): 
            try: 
                execfile(pythonrc) 
            except NameError: 
                pass
        # This will import .pythonrc.py as a side-effect
        import user
    code.interact(local=imported_objects)

def start_ipython_shell():
    import IPython
    # Explicitly pass an empty list as arguments, because otherwise IPython
    # would use sys.argv from this script.
    shell = IPython.Shell.IPShell(argv=[])
    shell.mainloop()
    
def start_bpython_shell():
    from bpython import cli
    cli.main(args=[])
    

class Command(NoArgsCommand):
    option_list = NoArgsCommand.option_list + (
        make_option(&#39;--plain&#39;, action=&#39;store_true&#39;, dest=&#39;plain&#39;,
            help=&#39;Tells Django to use plain Python, not IPython.&#39;),
        make_option(&#39;--ipython&#39;, action=&#39;store_true&#39;, dest=&#39;ipython&#39;,
            help=&#39;Tells Django to use ipython.&#39;),
        make_option(&#39;--bpython&#39;, action=&#39;store_true&#39;, dest=&#39;bpython&#39;,
            help=&#39;Tells Django to use bpython.&#39;),
    )
    help = &quot;Runs a Python interactive interpreter. Tries to use IPython, if it&#39;s available.&quot;

    requires_model_validation = False

    def handle_noargs(self, **options):
        # XXX: (Temporary) workaround for ticket #1796: force early loading of all
        # models from installed apps.
        from django.db.models.loading import get_models
        loaded_models = get_models()

        use_plain = options.get(&#39;plain&#39;, False)
        use_ipython = options.get(&#39;ipython&#39;, False)
        use_bpython = options.get(&#39;bpython&#39;, False)
        
        try:
            if use_plain:
                # Don&#39;t bother loading IPython, because the user wants plain Python.
                raise ImportError
            elif use_ipython:
                start_ipython_shell()
            elif use_bpython:
                start_bpython_shell()
            else:
                # backward compatible behavior.
                start_ipython_shell()

        except ImportError:
            # fallback to plain shell if we encounter an ImportError
            start_plain_shell()&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Is there an exiting debugger that use bpython ?  I am looking for something equivalent to ipdb but for bpython. I am looking for something that could replace :&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
import ipdb; ipdb.set_trace()
&lt;/code&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/774029655161357915/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2009/12/add-bpython-to-djangos-shell-management.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/774029655161357915'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/774029655161357915'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2009/12/add-bpython-to-djangos-shell-management.html' title='Add bpython to django&#39;s shell management command'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-2775934495723817623</id><published>2009-12-14T18:00:00.000-04:00</published><updated>2009-12-14T18:00:09.439-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="django-cms"/><title type='text'>Paginated feed for cmsplugin_feed</title><content type='html'>In my &lt;a href=&quot;http://yml-blog.blogspot.com/2009/12/feed-extension-for-django-cms.html&quot;&gt;previous post [1]&lt;/a&gt; I wrote about my experience of writing a new plugins for &lt;a href=&quot;http://www.django-cms.org/&quot;&gt;django-cms [2]&lt;/a&gt;. This plugin gives you &amp;nbsp;the capability to add a feed to a django-cms&#39; Page.&lt;br /&gt;
&lt;br /&gt;
I ended my post by asking about the best practice used to paginate the content of a plugin. Since I didn&#39;t get flooded by the answers I assumed that this shouldn&#39;t be different than doing it on a django app. Here it is what the fine django documentation says about this &lt;a href=&quot;http://docs.djangoproject.com/en/dev/topics/pagination/&quot;&gt;topic&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
So I have decided to implement this approach, the key point here is to understand that django-cms&#39; plugin has access to the context :&lt;br /&gt;
&lt;span style=&quot;font-family: monospace;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;white-space: pre;&quot;&gt;&lt;span style=&quot;font-family: &#39;Times New Roman&#39;;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;white-space: normal;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;class FeedPlugin(CMSPluginBase):
    [...]
    def render(self, context, instance, placeholder):
        feed = get_cached_feed(instance)
        if instance.paginate_by:
            is_paginated =True
            request = context[&#39;request&#39;]
        [...]

&lt;/code&gt;&lt;/pre&gt;This capability lets your plugin react to the GET and POST parameters. Once I have understood this the only thing that I had to do is to use the django&#39;s Paginator on the list of feed entries. &lt;br /&gt;
&lt;br /&gt;
As you can see in the code below there is nothing specific to django-cms there. &lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
            is_paginated =True
            request = context[&#39;request&#39;]
            feed_page_param = &quot;feed_%s_page&quot; %str(instance.id)

            feed_paginator = Paginator(feed[&quot;entries&quot;], instance.paginate_by) 
            # Make sure page request is an int. If not, deliver first page.
            try:
                page = int(request.GET.get(feed_page_param, &#39;1&#39;))
            except ValueError:
                page = 1
            # If page request (9999) is out of range, deliver last page of results.
            try:
                entries = feed_paginator.page(page)
            except (EmptyPage, InvalidPage):
                entries = feed_paginator.page(paginator.num_pages)
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
The complete implementation of this feature is available in the bitbucket repository of &lt;a href=&quot;http://bitbucket.org/yml/cmsplugin-feed/changeset/7d0644c7668f/&quot;&gt;cmsplugin_feed&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
I would be glad to hear from you if this implementation could be enhanced.&lt;br /&gt;
&lt;br /&gt;
[1]&amp;nbsp;http://yml-blog.blogspot.com/2009/12/feed-extension-for-django-cms.html&lt;br /&gt;
[2]&amp;nbsp;http://www.django-cms.org/&lt;br /&gt;
[3]&amp;nbsp;http://docs.djangoproject.com/en/dev/topics/pagination/&lt;br /&gt;
[4]&amp;nbsp;http://bitbucket.org/yml/cmsplugin-feed/changeset/7d0644c7668f/</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/2775934495723817623/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2009/12/paginated-feed-for-cmspluginfeed.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/2775934495723817623'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/2775934495723817623'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2009/12/paginated-feed-for-cmspluginfeed.html' title='Paginated feed for cmsplugin_feed'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-9055893248400950160</id><published>2009-12-12T16:33:00.001-04:00</published><updated>2009-12-12T16:37:19.826-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="django-cms"/><title type='text'>feed extension for django-cms</title><content type='html'>I have been lately looking at the open source CMS alternatives based on django. After some times investigating I have decided to give &lt;a href=&quot;http://www.django-cms.org/&quot;&gt;django-cms [1]&lt;/a&gt; a try.&lt;br /&gt;
&lt;br /&gt;
django-cms provides an API to write plugin it is &lt;a href=&quot;http://www.django-cms.org/en/documentation/2.0/custom_plugins/&quot;&gt;well documented&lt;/a&gt; [2] and you can also look the implementation of &lt;a href=&quot;http://www.django-cms.org/en/extensions/&quot;&gt;plugins listed there [3]&lt;/a&gt;. I have decided to experiment with it by writing a plugin that displays the content of a feed into a page. The source code is available on my bitbucket account in a project called &lt;a href=&quot;http://bitbucket.org/yml/cmsplugin-feed/&quot;&gt;cmsplugin-feed&lt;/a&gt;. The&amp;nbsp;development&amp;nbsp;of this plugin has been relatively straight forward and once you get to know the conventions defined to implement an extension. You feel like you are writing python code for a django application. The CMS part does not stand on your way at least it didn&#39;t in my experimentation.&lt;br /&gt;
&lt;br /&gt;
Here it is 2 screenshots showing this plugin in action :&lt;br /&gt;
In order to add a feed you need give a name and an URL&amp;nbsp;to your feed&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKTK-V-FeW-f3oD23MuM3KqwDuq_4wEaA4764V_MUOMc2veE-f0KtKPmCSMBzjIE7b7_nQj2o9G3yQ7fHDmTmCRM6z9CUbOaU2VwwF5SdYlpFE_WXkORlED6QyIM64-OlkBwbOSEdIBnI/s1600-h/cmsplugin_feed_edit.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKTK-V-FeW-f3oD23MuM3KqwDuq_4wEaA4764V_MUOMc2veE-f0KtKPmCSMBzjIE7b7_nQj2o9G3yQ7fHDmTmCRM6z9CUbOaU2VwwF5SdYlpFE_WXkORlED6QyIM64-OlkBwbOSEdIBnI/s320/cmsplugin_feed_edit.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
Then the plugin displays the field on a page.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjG0ZJZUl9bi_o0iUh5dBK1iGgmklhpFQQVzrGa1a0Hm8RJ_RpzzgnfNuYHvRzFFl7z49KE7WB3UM1AF0N2Fz2y0y882nQ5ujAQuwo7iShxcWNiGLWK-UTOfO0Ykc3rM3ZqedhYIeOehUk/s1600-h/cmsplugin_feed_view.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjG0ZJZUl9bi_o0iUh5dBK1iGgmklhpFQQVzrGa1a0Hm8RJ_RpzzgnfNuYHvRzFFl7z49KE7WB3UM1AF0N2Fz2y0y882nQ5ujAQuwo7iShxcWNiGLWK-UTOfO0Ykc3rM3ZqedhYIeOehUk/s320/cmsplugin_feed_view.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
I would be interested to read from someone the &quot;best practice&quot; to paginate the content of a plugin. Ideally I would like to add a parameter for every feed indicating how many items I want per page. Then I would like to paginate the feed in the placeholder of every page and this will allow the user to navigate between the pages.&lt;br /&gt;
&lt;br /&gt;
[1]&amp;nbsp;http://www.django-cms.org&lt;br /&gt;
[2]&amp;nbsp;http://www.django-cms.org/en/documentation/2.0/custom_plugins/&lt;br /&gt;
[3]&amp;nbsp;http://www.django-cms.org/en/extensions/&lt;br /&gt;
[4]&amp;nbsp;http://bitbucket.org/yml/cmsplugin-feed/</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/9055893248400950160/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2009/12/feed-extension-for-django-cms.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/9055893248400950160'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/9055893248400950160'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2009/12/feed-extension-for-django-cms.html' title='feed extension for django-cms'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKTK-V-FeW-f3oD23MuM3KqwDuq_4wEaA4764V_MUOMc2veE-f0KtKPmCSMBzjIE7b7_nQj2o9G3yQ7fHDmTmCRM6z9CUbOaU2VwwF5SdYlpFE_WXkORlED6QyIM64-OlkBwbOSEdIBnI/s72-c/cmsplugin_feed_edit.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-9025386640229948079</id><published>2009-11-30T10:30:00.000-04:00</published><updated>2009-11-30T10:30:13.261-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cherokee"/><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="uWSGI"/><title type='text'>uWSGI reaches the 0.9.3 Milestone</title><content type='html'>&lt;a href=&quot;http://projects.unbit.it/uwsgi/&quot;&gt;uWSGI project&lt;/a&gt; [1] reaches the 0.9.3 milestone. You can review the complete &lt;a href=&quot;http://lists.unbit.it/pipermail/uwsgi/2009-November/000020.html&quot;&gt;announcement&lt;/a&gt;&amp;nbsp;[2] on the mailing list &amp;nbsp;and download the code &lt;a href=&quot;http://projects.unbit.it/downloads/uwsgi-0.9.3.tar.gz&quot;&gt;here&lt;/a&gt; [3]&lt;br /&gt;
&lt;br /&gt;
This new release brings some new exiting features :&lt;br /&gt;
&lt;br /&gt;
- Nginx 0.7.x module&lt;br /&gt;
- configuration via python module&lt;br /&gt;
- support (non-standard) for Python 3.x&lt;br /&gt;
- Twisted client resource adapter&lt;br /&gt;
- graceful restart of worker processes and hot-plug substitution/upgrade&lt;br /&gt;
of the uWSGI server&lt;br /&gt;
- shared memory area to share data between workers/processes&lt;br /&gt;
- Tomcat handler&lt;br /&gt;
- support for virtualenv&lt;br /&gt;
&lt;br /&gt;
In &lt;a href=&quot;http://yml-blog.blogspot.com/2009/11/setting-up-django-project-with-cherokee.html&quot;&gt;this post&lt;/a&gt; [4] I have covered the usage of both the &quot;configuration via a python module&quot; and the &quot;virtualenv support&quot;.&lt;br /&gt;
&lt;br /&gt;
uWSGI allows you to run your favorite wsgi application on top of prefered web server (apache, cherokee or nginx).&lt;br /&gt;
&lt;br /&gt;
[1] http://projects.unbit.it/uwsgi/&lt;br /&gt;
[2] http://lists.unbit.it/pipermail/uwsgi/2009-November/000020.html&lt;br /&gt;
[3] http://projects.unbit.it/downloads/uwsgi-0.9.3.tar.gz&lt;br /&gt;
[4] http://yml-blog.blogspot.com/2009/11/setting-up-django-project-with-cherokee.html</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/9025386640229948079/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2009/11/uwsgi-reaches-093-milestone.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/9025386640229948079'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/9025386640229948079'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2009/11/uwsgi-reaches-093-milestone.html' title='uWSGI reaches the 0.9.3 Milestone'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-7322093563145384328</id><published>2009-11-17T20:50:00.000-04:00</published><updated>2009-11-17T20:50:07.565-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="documentation"/><category scheme="http://www.blogger.com/atom/ns#" term="sphinx"/><category scheme="http://www.blogger.com/atom/ns#" term="tip"/><category scheme="http://www.blogger.com/atom/ns#" term="watch"/><title type='text'>Continous sphinx build</title><content type='html'>I have always found the cycle : Edit the documentation source =&gt; build the &lt;a href=&quot;http://sphinx.pocoo.org/&quot;&gt;sphinx based documentation [1]&lt;/a&gt; =&gt; open a browser pointing the updated documentation and re iterate until your are satisfied a bit painful.&lt;br /&gt;
&lt;br /&gt;
Today I have finally found an automated version of this workflow that &lt;br /&gt;
gave me one of this nice WAHOUUUU feeling that happens after you remove this little stone from your shoe.&lt;br /&gt;
&lt;br /&gt;
The idea is to continuously build the documentation using &lt;a href=&quot;http://linux.die.net/man/1/watch&quot;&gt;watch [2]&lt;/a&gt;&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
watch -n 2 make htlm
&lt;/code&gt;&lt;/pre&gt;This will update the generated documentation every 2 seconds. This is nice in itself and it leads me to discover that &lt;a href=&quot;http://projects.gnome.org/epiphany/&quot;&gt;Epiphany&lt;/a&gt; automatically reload the page when it changes.&lt;br /&gt;
&lt;br /&gt;
[1] &lt;a href=&quot;http://sphinx.pocoo.org/&quot;&gt;http://sphinx.pocoo.org/&lt;/a&gt;&lt;br /&gt;
[2] &lt;a href=&quot;http://linux.die.net/man/1/watch&quot;&gt;http://linux.die.net/man/1/watch&lt;/a&gt;&lt;br /&gt;
[3] &lt;a href=&quot;http://projects.gnome.org/epiphany/&quot;&gt;http://projects.gnome.org/epiphany/&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/7322093563145384328/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2009/11/continous-sphinx-build.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/7322093563145384328'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/7322093563145384328'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2009/11/continous-sphinx-build.html' title='Continous sphinx build'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-8409590911731590050</id><published>2009-11-12T22:35:00.001-04:00</published><updated>2009-11-12T23:11:40.200-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cherokee"/><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="uWSGI"/><title type='text'>Setting up a django project with Cherokee, uWSGI and vitualenv (continued)</title><content type='html'>In my previous &lt;a href=&quot;http://yml-blog.blogspot.com/2009/11/how-to-set-up-cherokee-and-uwsgi.html&quot;&gt;post [1]&lt;/a&gt; I have started to talk about this stack but I have avoided to mention that the django project was indeed encapsulated into a virtualenv. This brought a couple small quirks and questions to the surface. So I spent some times investigating and exchanging with the community behind uWSGI and cherokee in order to solve them. The enhancements I discuss here require that you use the tip of &lt;a href=&quot;http://projects.unbit.it/uwsgi/changeset/87%3Ae78ef65c833e&quot;&gt;uWSGI [2]&lt;/a&gt; because some of them are new capability added in the past days or small improvements to my recipe of using these products.&lt;br /&gt;
&lt;br /&gt;
Before I start to dive into the enhancements let me sum up what we had to do in order to serve our WSGI project, in our case a django project.&lt;br /&gt;
&lt;br /&gt;
1&amp;gt; I had to add 2 configurations files : uwsgi.conf, django_wsgi.py&lt;br /&gt;
The first one is an XML file where you define the PYTHONPATH  and the mount point for your application, the second one is a python module where you define your WSGI configuration.&lt;br /&gt;
&lt;br /&gt;
2&amp;gt; I had to use the cherokee&#39;s wizard to create the appropriate configuration to serve my django app using uWSGI. This step add an &quot;Information Sources&quot; with the following settings in the interpreter section :&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
/usr/local/bin/uwsgi -s /tmp/cherokee-source1.sock -C -x /opt/webapps/example-ve/src/project/uwsgi.conf
&lt;/code&gt;&lt;/pre&gt;The first thing that can be improved in this recipe is that you can reduce the configuration to only one python module. This allows you to avoid the uwsgi.conf file. uWSGI comes with an option that allows you to directly specify a python module.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
-w &lt;path&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; name of wsgi config module (no ROCK_SOLID)
&lt;/path&gt;&lt;/code&gt;&lt;/pre&gt;This would give us the following command :&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
uwsgi -s /tmp/cherokee-source1.sock -C -w project.django_wsgi
&lt;/code&gt;&lt;/pre&gt;The django_wsgi file is very simple and looks like this :&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
import os
import django.core.handlers.wsgi

# Set the django settings and define the wsgi app
os.environ[&#39;DJANGO_SETTINGS_MODULE&#39;] = &#39;project.settings&#39;
application = django.core.handlers.wsgi.WSGIHandler()

# Mount the application to the url
applications = {&#39;/&#39;:&#39;application&#39;, }
&lt;/code&gt;&lt;/pre&gt;This is all good and simple but it only works if &quot;project&quot; is in the PYHTONPATH. I handle all my projects in a dedicated virtualenv. uWSGI handle this gracefully now, there is a new option that allows you to define your Python Home.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
-H &lt;path&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; set python home/virtualenv
&lt;/path&gt;&lt;/code&gt;&lt;/pre&gt;Here it the improved settings for the interpreter section of the information sources :&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
uwsgi -s /tmp/cherokee-source1.sock -C -w project.django_wsgi -H /opt/webapps/example-ve
&lt;/path&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
I would be glad to hear from you and very happy if you give this setup a spin on your django project and let me know how it performs compare to your actual setup.&lt;br /&gt;
&lt;br /&gt;
[1] http://yml-blog.blogspot.com/2009/11/how-to-set-up-cherokee-and-uwsgi.html &lt;br /&gt;
[2] http://projects.unbit.it/uwsgi/changeset/87%3Ae78ef65c833e</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/8409590911731590050/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2009/11/setting-up-django-project-with-cherokee.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/8409590911731590050'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/8409590911731590050'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2009/11/setting-up-django-project-with-cherokee.html' title='Setting up a django project with Cherokee, uWSGI and vitualenv (continued)'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-8734374581474642672</id><published>2009-11-03T21:11:00.003-04:00</published><updated>2009-11-08T10:10:44.142-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cherokee"/><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="uWSGI"/><title type='text'>How to set up cherokee and uWSGI</title><content type='html'>A couple of post ago I have &quot;blogged&quot; about cherokee which is a light weight web server, fast and extremely simple to configure thanks to its great admin interface.&lt;br /&gt;
&lt;br /&gt;
The most common configuration to use cherokee with django is described &lt;a href=&quot;http://www.cherokee-project.com/doc/cookbook_django.html&quot;&gt;here&lt;/a&gt; and to put is in a nutshell it was based on flup using SCGI or FASTCGI protocol.&lt;br /&gt;
&lt;br /&gt;
However a couple of weeks ago a new contender pop up on my radar. It is called uWSGI and it might become in the near future very popular to power django application. You can find more information about it on on the pages below :&lt;br /&gt;
* &lt;a href=&quot;http://projects.unbit.it/uwsgi/wiki/Example&quot;&gt;example [2]&lt;/a&gt;&lt;br /&gt;
* &lt;a href=&quot;http://projects.unbit.it/uwsgi/wiki/Doc&quot;&gt;Documentation [3]&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
From an architecture stand point it  might provide us python web developer an interesting piece of software  that could operate with virtually any webserver. There is already an apache module and cherokee handler. The documentation mentions that the core developers have started to develop an nginx adaptor.&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-size: large;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Software prerequisites &lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
cherokee 0.99.26&lt;br /&gt;
uWSGI (changeset:   85:61dcfd718023)&lt;br /&gt;
&lt;br /&gt;
I have installed cherokee using the &lt;a href=&quot;https://launchpad.net/%7Echerokee-webserver/+archive/ppa&quot;&gt; PPA  on launchpad [1]&lt;/a&gt; and uWSGI directly from the mercurial repository.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
hg clone http://projects.unbit.it/hg/uwsgi
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;Installation&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Installation of both product is relatively straight forward and well documented. I will not detail it more here but rather point you to &lt;a href=&quot;http://www.cherokee-project.com/doc/basics_installation.html&quot;&gt;cherokee [4&lt;/a&gt;] and &lt;a href=&quot;http://projects.unbit.it/uwsgi/wiki/Install&quot;&gt;uWSGI website [5]&lt;/a&gt;. &lt;br /&gt;
Here it is how you would compile uWSGI on Ubuntu 9.04&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
make -f Makefile.Linux.Py26
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Note: The compilation will create a binairy called uwsgi26. It is important to add it into your PATH under the name of uwsgi. If you don&#39;t do this cherokee&#39;s wizard&amp;nbsp; will complain about not being able to find uWSGI server. I have created a symbolic link but you could also copy it into /usr/local/bin.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;Configuration&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
The amazing admin interface of cherokee make it dead simple once you know what need to be done. In the latest version of cherokee a wizzard has been added to make the configuration even simpler.&lt;br /&gt;
&lt;br /&gt;
First thing first you need to create 2 configurations file into your django project directory called respectively : django_wsgi.py and uwsgi.conf &lt;br /&gt;
&lt;br /&gt;
------ django_wsgi.py ------------------ &lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
  import os import django.core.handlers.wsgi  
  os.environ[&#39;DJANGO_SETTINGS_MODULE&#39;] = &#39;project.settings&#39; 
  application = django.core.handlers.wsgi.WSGIHandler() 
 &lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
------ uwsgi.conf --------------------&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
&amp;lt;uwsgi&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;pythonpath&amp;gt;/home/yml/workdir/webdev/fabric_factory/20091023/fabric_factory/ve/lib/python2.6/site-packages/&amp;lt;/pythonpath&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;pythonpath&amp;gt;/home/yml/workdir/webdev/fabric_factory/20091023/fabric_factory/src/&amp;lt;/pythonpath&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;app mountpoint=&quot;/&quot;&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;script&amp;gt;project.django_wsgi&amp;lt;/script&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/app&amp;gt;

&amp;lt;/uwsgi&amp;gt;

&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
Once this is done you can launch cherokee admin : &#39;cherokee-admin -u&#39; and point your browser to this URL : &lt;br /&gt;
* &lt;a href=&quot;http://127.0.0.1:9090/&quot;&gt;http://127.0.0.1:9090/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Once in cherokee you can use the uWSGI wizard to create automatically your information sources and add a new rule. In order to do so you need to go into the virtual server section on the &lt;a href=&quot;http://127.0.0.1:9090/vserver/10&quot;&gt;behavior tab [6]&lt;/a&gt;. Below the table there is an hyperlink called wizards. It will take you to a list of wizards, click on uWSGI.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8EELWWOG3miQbkfo8rMDlNQzR0EtJ2PxAdihIG_kj0UCeCQW7J53CEbko0xeHJyqswOEw5EAkHXnWv6b2AqjLtWJ65nZTyHD4NIpn589NcHN6wNsAm-z9Myp_dVYtQrkTjpQ8DaXC3Vw/s1600-h/wizzard_list.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8EELWWOG3miQbkfo8rMDlNQzR0EtJ2PxAdihIG_kj0UCeCQW7J53CEbko0xeHJyqswOEw5EAkHXnWv6b2AqjLtWJ65nZTyHD4NIpn589NcHN6wNsAm-z9Myp_dVYtQrkTjpQ8DaXC3Vw/s320/wizzard_list.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
Cherokee asks for the path to the uwsgi.conf file that we have created earlier. The last bit you might want to do is to add a rule to serve the admin media.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4pKBmvsFiqtDMg9V7XJSXsJrVg0wDUTWgLguuL-Rffj2tnOin-hHHvDKTlJPk3ZGbUvXo0YPLRQz2wr9HywbuGs1rAVMiG2FwmV23lRwAqLjYGnSRyfDy44EDilXXDfhwt9mQK7SdwRU/s1600-h/virtual_server_behavior.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4pKBmvsFiqtDMg9V7XJSXsJrVg0wDUTWgLguuL-Rffj2tnOin-hHHvDKTlJPk3ZGbUvXo0YPLRQz2wr9HywbuGs1rAVMiG2FwmV23lRwAqLjYGnSRyfDy44EDilXXDfhwt9mQK7SdwRU/s320/virtual_server_behavior.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-size: large;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Troubleshooting&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Here it is some basic technics to understand what is going on and solve your issues. Look at cherokee&#39;s log files :&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
sudo tail -f /var/log/cherokee/cherokee.error
and
sudo tail -f /var/log/cherokee/cherokee.access 
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Look into the section called &quot;information sources&quot; in cherokee-admin. Explore the information sources created by the uWSGI wizard. Copy the text in the interpreter field, in my case it is :&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
/usr/local/bin/uwsgi -s /tmp/cherokee-source1.sock -C -x /[Path to my django project]/uwsgi.conf
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Then you can paste it in a terminal this will give you a way to see what it is going on.&lt;br /&gt;
&lt;br /&gt;
This new combination is still very young but it looks promising to me. I am sure that if we get enough people to hammer on it can quickly becomes popular.&lt;br /&gt;
&lt;br /&gt;
[1] https://launchpad.net/~cherokee-webserver/+archive/ppa&lt;br /&gt;
[2] http://projects.unbit.it/uwsgi/wiki/Example&lt;br /&gt;
[3] http://projects.unbit.it/uwsgi/wiki/Doc&lt;br /&gt;
[4] http://www.cherokee-project.com/doc/basics_installation.html&lt;br /&gt;
[5] http://projects.unbit.it/uwsgi/wiki/Install&lt;br /&gt;
[6] http://127.0.0.1:9090/vserver/10</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/8734374581474642672/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2009/11/how-to-set-up-cherokee-and-uwsgi.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/8734374581474642672'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/8734374581474642672'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2009/11/how-to-set-up-cherokee-and-uwsgi.html' title='How to set up cherokee and uWSGI'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8EELWWOG3miQbkfo8rMDlNQzR0EtJ2PxAdihIG_kj0UCeCQW7J53CEbko0xeHJyqswOEw5EAkHXnWv6b2AqjLtWJ65nZTyHD4NIpn589NcHN6wNsAm-z9Myp_dVYtQrkTjpQ8DaXC3Vw/s72-c/wizzard_list.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-2895002088828864883</id><published>2009-10-22T20:12:00.001-04:00</published><updated>2009-10-22T20:13:19.473-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="piston"/><title type='text'>django-piston authentication against django auth -- part 4</title><content type='html'>The previous post ends with one known &quot;Open Issue&quot;. The authentication for the ajax call was sitll &lt;a href=&quot;http://bitbucket.org/yml/django-piston/src/a8bcb7f9756e/examples/blogserver/templates/edit_ajaxy_post.html#cl-6&quot;&gt;hard coded [1]&lt;/a&gt;. In this post I am going to show you how you can extend django-piston to authenticate your users against django.contrib.auth. This might be important if your web app already take advantage of this module to manage and authenticate users.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A bit of reading of the source code and the &lt;a href=&quot;http://bitbucket.org/jespern/django-piston/wiki/Documentation#authentication&quot;&gt;documentation of django-piston [2]&lt;/a&gt; leads me to understand that django-piston has been designed from the ground to enable you to easily write your own authentication handler. Out of the box it comes with &quot;HttpBasicAuthentication&quot; and OAuthAuthentication. &quot;HttpBasicAuthentication&quot; is a very good example how to implement a DjangoAuthentication.&lt;br /&gt;
&lt;br /&gt;
An authentication handler is a class, which must have 2 methods: is_authenticated, challenge&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
class DjangoAuthentication(object):
    &quot;&quot;&quot;
    Django authentication. 
    &quot;&quot;&quot;
    def __init__(self, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
        if not login_url:
            login_url = settings.LOGIN_URL
        self.login_url = login_url
        self.redirect_field_name = redirect_field_name
        self.request = None
    
    def is_authenticated(self, request):
        &quot;&quot;&quot;
        This method call the `is_authenticated` method of django
        User in django.contrib.auth.models.
        
        `is_authenticated`: Will be called when checking for
        authentication. It returns True if the user is authenticated
        False otherwise.
        &quot;&quot;&quot;
        self.request = request
        return request.user.is_authenticated()
        
    def challenge(self):
        &quot;&quot;&quot;
        `challenge`: In cases where `is_authenticated` returns
        False, the result of this method will be returned.
        This will usually be a `HttpResponse` object with
        some kind of challenge headers and 401 code on it.
        &quot;&quot;&quot;
        path = urlquote(self.request.get_full_path())
        tup = self.login_url, self.redirect_field_name, path 
        return HttpResponseRedirect(&#39;%s?%s=%s&#39; %tup)

&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
The implementation is relatively simple, is_authenticated uses the method with the same name available from a django User and the challenge redirect the user to the login page.&lt;br /&gt;
&lt;br /&gt;
This post will end the series on django-piston it was for me a pretext to get familliar with this great app that make creating a RESTFUL api for django easy. You can find all the modifications I did into my &lt;a href=&quot;http://bitbucket.org/yml/django-piston/&quot;&gt;fork on bitbucket [3]&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[1] http://bitbucket.org/yml/django-piston/src/a8bcb7f9756e/examples/blogserver/templates/edit_ajaxy_post.html#cl-6&lt;br /&gt;
[2] http://bitbucket.org/jespern/django-piston/wiki/Documentation#authentication&lt;br /&gt;
[3] http://bitbucket.org/yml/django-piston/</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/2895002088828864883/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2009/10/django-piston-authentication-against.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/2895002088828864883'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/2895002088828864883'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2009/10/django-piston-authentication-against.html' title='django-piston authentication against django auth -- part 4'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-791141679031579301</id><published>2009-10-18T23:50:00.003-04:00</published><updated>2009-10-18T23:56:47.412-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="jquery"/><category scheme="http://www.blogger.com/atom/ns#" term="piston"/><title type='text'>django-piston form validation -- part 3</title><content type='html'>In my previous post titled &lt;a href=&quot;http://yml-blog.blogspot.com/2009/10/exploration-of-django-piston-part-2.html&quot;&gt;&quot;Exploration of django-piston -- part 2&quot; [1]&lt;/a&gt; I end the post by 2 open issues. This post will propose a solution to the first one:&lt;br /&gt;
&lt;br /&gt;
The validation errors are returned as a pseudo xml string.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
Bad Request &amp;lt;ul class=&quot;errorlist&quot;&amp;gt;&amp;lt;li&amp;gt;content&amp;lt;ul class=&quot;errorlist&quot;&amp;gt;&amp;lt;li&amp;gt;This field is required.&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;title&amp;lt;ul class=&quot;errorlist&quot;&amp;gt;&amp;lt;li&amp;gt;This field is required.&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;

&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
To put it in other words I would prefer if django-piston return the validation errors of the form in the format specified in the request. &lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;&lt;b&gt;JSON&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
$ curl -u testuser:foobar -X POST -d content=&quot;This post is created using the api&quot; http://127.0.0.1:8000/api/posts/?format=json
Bad Request {&quot;title&quot;: [&quot;This field is required.&quot;]}
&lt;/code&gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;b&gt;yaml&lt;/b&gt;&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
$ curl -u testuser:foobar -X POST -d content=&quot;This post is created using the api&quot; http://127.0.0.1:8000/api/posts/?format=yaml
Bad Request title: [!!python/unicode &#39;This field is required.&#39;]
&lt;/code&gt;
&lt;/pre&gt;&lt;b&gt;xml&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
$ curl -u testuser:foobar -X POST -d content=&quot;This post is created using the api&quot; http://127.0.0.1:8000/api/posts/?format=xml
Bad Request &amp;lt;ul class=&quot;errorlist&quot;&amp;gt;&amp;lt;li&amp;gt;title&amp;lt;ul class=&quot;errorlist&quot;&amp;gt;&amp;lt;li&amp;gt;This field is required.&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;
&lt;/code&gt;
&lt;/pre&gt;&lt;br /&gt;
After a while reading the source of django-piston and poking around I have implemented this feature in my branch.&lt;br /&gt;
&lt;br /&gt;
The biggest hurdle was that for some reasons &quot;form.errors&quot; cannot be serialized directly.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
simplejson.dumps(form.errors)
*** TypeError: &lt;django.utils.functional.__proxy__ 0x1e0ba50=&quot;&quot; at=&quot;&quot; object=&quot;&quot;&gt; is not JSON serializable
&lt;/django.utils.functional.__proxy__&gt;&lt;/code&gt;
&lt;/pre&gt;&lt;br /&gt;
In order to work around this issue you need to force python to evaluate this proxy object.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
dict((key, [unicode(v) for v in values]) for key,values in form.errors.items())
&lt;/code&gt;
&lt;/pre&gt;&lt;br /&gt;
The last bit is to change the JS in order to adapt it to the fact that now the validation errors are returned as a json string. &lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;response = response.substring(12, response.length);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;errors = eval(&amp;quot;(&amp;quot;+response+&amp;quot;)&amp;quot;);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$.each($(&#39;:input&#39;), function(index, value) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;field =$(value).attr(&#39;id&#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;field = field.substring(3, field.length);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;field_errors = errors[field];
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (field_errors) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ul = &amp;quot;&amp;lt;ul class=\&amp;quot;errorlist\&amp;quot;&amp;gt;\n&amp;quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$.each(field_errors, function(index, value){
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ul += &amp;quot;&amp;lt;li&amp;gt;&amp;quot;+value+&amp;quot;&amp;lt;/li&amp;gt;\n&amp;quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ul += &amp;quot;&amp;lt;/ul&amp;gt;\n&amp;quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$(value).parent().prepend(ul);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
The complete code related to the implementation of this feature is available in my &lt;a href=&quot;http://bitbucket.org/yml/django-piston/&quot;&gt;branch [3&lt;/a&gt;].&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Open Issue&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Authentication is still hardcoded into the html pages. What is the best way to keep HttpBasicAuthentication to the external api that could easily be used by curl and add single sign on with django admin interface ?&lt;br /&gt;
&lt;br /&gt;
The example is still very incomplete. It is missing among other things the capability to update/delete a blog post&lt;br /&gt;
&lt;br /&gt;
I would be glad to read from you on how to improve this example or to get pointer of reusable application that have been built on top of django-piston.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[1] &lt;a href=&quot;http://yml-blog.blogspot.com/2009/10/exploration-of-django-piston-part-2.html&quot;&gt;http://yml-blog.blogspot.com/2009/10/exploration-of-django-piston-part-2.html&lt;/a&gt;&lt;br /&gt;
[2] &lt;a href=&quot;http://bitbucket.org/yml/django-piston/changeset/a8bcb7f9756e/&quot;&gt;http://bitbucket.org/yml/django-piston/changeset/a8bcb7f9756e/&lt;/a&gt;&lt;br /&gt;
[3] &lt;a href=&quot;http://bitbucket.org/yml/django-piston/&quot;&gt;http://bitbucket.org/yml/django-piston/&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/791141679031579301/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2009/10/django-piston-form-validation-part-3.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/791141679031579301'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/791141679031579301'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2009/10/django-piston-form-validation-part-3.html' title='django-piston form validation -- part 3'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-6497805744070055969</id><published>2009-10-14T20:55:00.002-04:00</published><updated>2009-11-14T23:19:17.200-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="piston"/><title type='text'>exploration of django-piston -- part 2</title><content type='html'>Djangocon 09 was really food for thought and this post follow my previous post about django-piston. I found the talk from Avi Bryant about his experience building &lt;a href=&quot;http://trendly.com/&quot;&gt;Trendly [1]&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
My take away of this talk was that they have a single HTML page with a lot of JS. The JS is the used to get the JSON data the JSON from the server. Unfortunately I am unable to find a link to a better description of its talk.&lt;br /&gt;
&lt;br /&gt;
My &lt;a href=&quot;http://yml-blog.blogspot.com/2009/09/add-web-api-to-your-app-with-django.html&quot;&gt;last post&lt;/a&gt; hopefully convince you that it is easy relatively easy to create a web api to your &lt;a href=&quot;http://bitbucket.org/yml/django-piston/src/tip/examples/blogserver/README.txt&quot;&gt;django application [2]&lt;/a&gt;. This Post aims to show you how you can create a page to list and create a blogpost.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-weight: bold;font-size:130%;&quot; &gt;List and create a blogpost&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
The idea here is to reuse the web api that we have created in the previous article to build a web page. One of the interesting feature of django-piston is the decorator @validate(BlogpostForm, &#39;PUT&#39;). It enables you to validate the data send to your server and it takes as input a django form. A longuer description of this feature is available &lt;a href=&quot;http://bitbucket.org/jespern/django-piston/wiki/Documentation#form-validation&quot;&gt;here [4]&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
The &lt;a href=&quot;http://bitbucket.org/yml/django-piston/src/tip/examples/blogserver/blog/views.py&quot;&gt;view cannot be simpler [5]&lt;/a&gt; :&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
def create_ajaxy_post(request):
    form = BlogpostForm()
    return direct_to_template(request,
                              template=&#39;edit_ajaxy_post.html&#39;,
                              extra_context={&#39;form&#39;:form} )
&lt;/code&gt;&lt;/pre&gt;The template is using &lt;a href=&quot;http://jquery.com/&quot;&gt;jQuery [6]&lt;/a&gt; which ease the ajax call and the DOM manipulation. &lt;a href=&quot;http://bitbucket.org/yml/django-piston/src/72c72e0b4b7e/examples/blogserver/templates/edit_ajaxy_post.html&quot;&gt;The template [7]&lt;/a&gt; has a very simple structure :&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
&lt;font color=&quot;#a020f0&quot;&gt;{% &lt;/font&gt;&lt;font color=&quot;#a52a2a&quot;&gt;&lt;b&gt;block&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#a020f0&quot;&gt;&amp;nbsp;content %}&lt;/font&gt;

&lt;font color=&quot;#008b8b&quot;&gt;&amp;lt;&lt;/font&gt;&lt;font color=&quot;#a52a2a&quot;&gt;&lt;b&gt;h1&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;gt;&lt;/font&gt;&lt;font color=&quot;#ff00ff&quot;&gt;&lt;b&gt;Ajaxy&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;lt;/&lt;/font&gt;&lt;font color=&quot;#a52a2a&quot;&gt;&lt;b&gt;h1&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;gt;&lt;/font&gt;

&lt;font color=&quot;#008b8b&quot;&gt;&amp;lt;&lt;/font&gt;&lt;font color=&quot;#a52a2a&quot;&gt;&lt;b&gt;div&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;nbsp;&lt;/font&gt;&lt;font color=&quot;#2e8b57&quot;&gt;&lt;b&gt;id&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;=&lt;/font&gt;&lt;font color=&quot;#ff00ff&quot;&gt;&amp;quot;content&amp;quot;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;lt;/&lt;/font&gt;&lt;font color=&quot;#a52a2a&quot;&gt;&lt;b&gt;div&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;gt;&lt;/font&gt;

&lt;font color=&quot;#008b8b&quot;&gt;&amp;lt;&lt;/font&gt;&lt;font color=&quot;#a52a2a&quot;&gt;&lt;b&gt;h2&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;gt;&lt;/font&gt;&lt;font color=&quot;#ff00ff&quot;&gt;&lt;b&gt;Ajaxy form&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;lt;/&lt;/font&gt;&lt;font color=&quot;#a52a2a&quot;&gt;&lt;b&gt;h1&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;gt;&lt;/font&gt;

&lt;font color=&quot;#008b8b&quot;&gt;&amp;lt;&lt;/font&gt;&lt;font color=&quot;#a52a2a&quot;&gt;&lt;b&gt;form&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;nbsp;&lt;/font&gt;&lt;font color=&quot;#2e8b57&quot;&gt;&lt;b&gt;method&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;=&lt;/font&gt;&lt;font color=&quot;#ff00ff&quot;&gt;&amp;quot;POST&amp;quot;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;nbsp;&lt;/font&gt;&lt;font color=&quot;#2e8b57&quot;&gt;&lt;b&gt;action&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;=&lt;/font&gt;&lt;font color=&quot;#ff00ff&quot;&gt;&amp;quot;&lt;/font&gt;&lt;font color=&quot;#a020f0&quot;&gt;{% &lt;/font&gt;&lt;font color=&quot;#a52a2a&quot;&gt;&lt;b&gt;url&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#a020f0&quot;&gt;&amp;nbsp;posts %}&lt;/font&gt;&lt;font color=&quot;#ff00ff&quot;&gt;&amp;quot;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;nbsp;&lt;/font&gt;&lt;font color=&quot;#2e8b57&quot;&gt;&lt;b&gt;id&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;=&lt;/font&gt;&lt;font color=&quot;#ff00ff&quot;&gt;&amp;quot;post_form&amp;quot;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;gt;&lt;/font&gt;

&lt;font color=&quot;#ff00ff&quot;&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;lt;&lt;/font&gt;&lt;font color=&quot;#a52a2a&quot;&gt;&lt;b&gt;div&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;nbsp;&lt;/font&gt;&lt;font color=&quot;#2e8b57&quot;&gt;&lt;b&gt;id&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;=&lt;/font&gt;&lt;font color=&quot;#ff00ff&quot;&gt;&amp;quot;post_form_error&amp;quot;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;lt;/&lt;/font&gt;&lt;font color=&quot;#a52a2a&quot;&gt;&lt;b&gt;div&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;gt;&lt;/font&gt;

&lt;font color=&quot;#ff00ff&quot;&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#a020f0&quot;&gt;{{ form.as_p }}&lt;/font&gt;

&lt;font color=&quot;#ff00ff&quot;&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;lt;&lt;/font&gt;&lt;font color=&quot;#a52a2a&quot;&gt;&lt;b&gt;input&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;nbsp;&lt;/font&gt;&lt;font color=&quot;#2e8b57&quot;&gt;&lt;b&gt;type&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;=&lt;/font&gt;&lt;font color=&quot;#ff00ff&quot;&gt;&#39;submit&#39;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;nbsp;&lt;/font&gt;&lt;font color=&quot;#2e8b57&quot;&gt;&lt;b&gt;name&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;=&lt;/font&gt;&lt;font color=&quot;#ff00ff&quot;&gt;&#39;create&#39;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;nbsp;&lt;/font&gt;&lt;font color=&quot;#6a5acd&quot;&gt;onclick=&amp;quot;&lt;/font&gt;&lt;font color=&quot;#a52a2a&quot;&gt;&lt;b&gt;return&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#6a5acd&quot;&gt;&amp;nbsp;send_form&lt;/font&gt;()&lt;font color=&quot;#6a5acd&quot;&gt;;&lt;/font&gt;&lt;font color=&quot;#6a5acd&quot;&gt;&amp;quot;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;nbsp;&lt;/font&gt;&lt;font color=&quot;#2e8b57&quot;&gt;&lt;b&gt;value&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;=&lt;/font&gt;&lt;font color=&quot;#ff00ff&quot;&gt;&#39;Create&#39;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;lt;/&lt;/font&gt;&lt;font color=&quot;#a52a2a&quot;&gt;&lt;b&gt;input&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;gt;&lt;/font&gt;

&lt;font color=&quot;#008b8b&quot;&gt;&amp;lt;/&lt;/font&gt;&lt;font color=&quot;#a52a2a&quot;&gt;&lt;b&gt;form&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#008b8b&quot;&gt;&amp;gt;&lt;/font&gt;

&lt;font color=&quot;#a020f0&quot;&gt;{% &lt;/font&gt;&lt;font color=&quot;#a52a2a&quot;&gt;&lt;b&gt;endblock&lt;/b&gt;&lt;/font&gt;&lt;font color=&quot;#a020f0&quot;&gt;&amp;nbsp;%}&lt;/font&gt;
&lt;/code&gt;&lt;/pre&gt;In order to give life to this page you need some JS which will get from the JSON string of the blogposts and POST the DATA to create a new blogpost and add it to the list.&lt;br /&gt;
&lt;br /&gt;
If you try to POST an empty blogpost the data will be validated on the server and the validation errors will be displayed to the user :&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
* content
  o This field is required.
* title
  o This field is required.
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size:130%;&quot;&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Open Issue&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
The validation error are returned as a pseudo xml string :&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
Bad Request &amp;lt;ul class=&amp;quot;errorlist&amp;quot;&amp;gt;&amp;lt;li&amp;gt;content&amp;lt;ul class=&amp;quot;errorlist&amp;quot;&amp;gt;&amp;lt;li&amp;gt;This field is required.&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;title&amp;lt;ul class=&amp;quot;errorlist&amp;quot;&amp;gt;&amp;lt;li&amp;gt;This field is required.&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
It will be much more convenient to get a json dict with the error. Any one has an idea on how to get this ?&lt;br /&gt;
&lt;br /&gt;
Authentication is still hardcoded into the html pages. What is the best way to keep HttpBasicAuthentication to the external api that could easily be used by curl and add single sign on wiht the admin ?&lt;br /&gt;
&lt;br /&gt;
The example is still very incomplete. It is missing among other things the capability to update/delete a blog post&lt;br /&gt;
&lt;br /&gt;
I would be glad to read from you on how to improve this example or to get pointer of reusable application that have been built on top of django-piston.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[1] &lt;a href=&quot;http://trendly.com/&quot;&gt;http://trendly.com/&lt;/a&gt;&lt;br /&gt;
[2]&lt;a href=&quot;http://bitbucket.org/yml/django-piston/src/tip/examples/blogserver/README.txt&quot;&gt; http://bitbucket.org/yml/django-piston/src/tip/examples/blogserver/README.txt&lt;/a&gt;&lt;br /&gt;
[3] &lt;a href=&quot;http://bitbucket.org/yml/django-piston/src/72c72e0b4b7e/examples/blogserver/templates/edit_ajaxy_post.html&quot;&gt;http://bitbucket.org/yml/django-piston/src/72c72e0b4b7e/examples/blogserver/templates/edit_ajaxy_post.html&lt;/a&gt;&lt;br /&gt;
[4] &lt;a href=&quot;http://bitbucket.org/jespern/django-piston/wiki/Documentation#form-validation&quot;&gt;http://bitbucket.org/jespern/django-piston/wiki/Documentation#form-validation&lt;/a&gt;&lt;br /&gt;
[5] &lt;a href=&quot;http://bitbucket.org/yml/django-piston/src/tip/examples/blogserver/blog/views.py&quot;&gt;http://bitbucket.org/yml/django-piston/src/tip/examples/blogserver/blog/views.py&lt;/a&gt;&lt;br /&gt;
[6] &lt;a href=&quot;http://jquery.com/&quot;&gt;http://jquery.com/&lt;/a&gt;&lt;br /&gt;
[7] &lt;a href=&quot;http://bitbucket.org/yml/django-piston/src/72c72e0b4b7e/examples/blogserver/templates/edit_ajaxy_post.html&quot;&gt;http://bitbucket.org/yml/django-piston/src/72c72e0b4b7e/examples/blogserver/templates/edit_ajaxy_post.html&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/6497805744070055969/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2009/10/exploration-of-django-piston-part-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/6497805744070055969'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/6497805744070055969'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2009/10/exploration-of-django-piston-part-2.html' title='exploration of django-piston -- part 2'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-8555715915372171113</id><published>2009-09-21T17:04:00.002-04:00</published><updated>2009-10-15T18:30:12.499-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="piston"/><title type='text'>Add a web api to your app with django-piston</title><content type='html'>More and more often I found myself in a situation where I would like to add a web api to my django applications. I have recently tried to used django-piston http://bitbucket.org/jespern/django-piston for this and I found the learning curve a bit steep. It is not particularly hard but it requires you to understand few things before being able to enjoy it.&lt;br /&gt;
&lt;br /&gt;
This post should help you to understand how to create the handlers to read, create, update, delete an object and see how you can call this web api from the command line using curl. I have forked django-piston to extend the example &quot;blogserver&quot;. I would recommend you to also read the &lt;a href=&quot;https://bitbucket.org/yml/django-piston/src/tip/examples/blogserver/README.txt&quot;&gt;README&lt;/a&gt; inside the example&lt;br /&gt;
&lt;br /&gt;
You can grab the code like this :&lt;br /&gt;
&lt;code&gt;&lt;br /&gt;
hg clone http://bitbucket.org/yml/django-piston/&lt;br /&gt;
&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
curl is a command line tool to transfer data from or to a server, using one of the supported protocols (HTTP, HTTPS, FTP, FTPS, SCP, SFTP, TFTP, DICT, TELNET, LDAP or FILE).  The command is designed to work without user interaction.&lt;br /&gt;
&lt;br /&gt;
Here it is the 4 things that I found a bit hard to understand.&lt;br /&gt;
&lt;span style=&quot;font-weight: bold;font-size:130%;&quot; &gt;&lt;br /&gt;
1. @require_extended&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
This decorator is handy if you want to restrict the access to your handler to only the request that have have one of the header listed below :&lt;br /&gt;
• application/json&lt;br /&gt;
• application/x-yaml&lt;br /&gt;
• text/xml&lt;br /&gt;
• application/python-pickle&lt;br /&gt;
&lt;br /&gt;
The direct effect on the curl command line is that you will need to add a -H &#39;Content-Type:&lt;insert&gt;&#39;, this will give you something like :&lt;br /&gt;
&lt;code&gt;&lt;br /&gt;
$ curl -u testuser:foobar -H &#39;Content-Type:application/json&#39; http://127.0.0.1:8000/api/posts/?format=json&lt;br /&gt;
&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
If the header is omitted django-piston will return a &#39;Bad Request&#39;.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-weight: bold;font-size:130%;&quot; &gt;2. Request Method&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
The handler for your resource can be composed of the following method : read, create, update, delete that are respectively mapped to the following request method : GET, POST, PUT, DELETE. &quot;-X &lt;method&gt;&quot; is used to specify the method you want to use. Here it is an example that execute the update method of the blogserver :&lt;br /&gt;
&lt;code&gt;&lt;br /&gt;
$ curl -u testuser:foobar -H &#39;Content-Type:application/json&#39; -X PUT -d &#39;{&quot;content&quot;: &quot;Update test&quot;, &quot;title&quot;: &quot;Update test&quot;}&#39; http://127.0.0.1:8000/api/post/1/&lt;br /&gt;
&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-weight: bold;font-size:130%;&quot; &gt;3. Passing data to your handler&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
In this example I am going to demonstrate how to pass JSON string with curl. In order to do this you should use the -d followed by the JSON string :&lt;br /&gt;
&lt;code&gt;&lt;br /&gt;
$ curl -u testuser:foobar -H &#39;Content-Type:application/json&#39; -X PUT -d &#39;{&quot;content&quot;: &quot;Update test&quot;, &quot;title&quot;: &quot;Update test&quot;}&#39; http://127.0.0.1:8000/api/post/1/&lt;br /&gt;
&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
django-piston will automatically turn this string into a python dictionary that is ready to be used by your handler. You will find this data in &#39;request.data&#39; the raw string is available in &quot;request.raw_post_data&quot;&lt;br /&gt;
&lt;span style=&quot;font-weight: bold;font-size:130%;&quot; &gt;&lt;br /&gt;
4. How to pass parameter to your handler&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
It took me a while to understand that the same handler can be mounted to several urls this will allow you to pass additional parameters like the primary key ID or the slug :&lt;br /&gt;
&lt;code&gt;&lt;br /&gt;
....&lt;br /&gt;
blogposts = Resource(handler=BlogpostHandler, authentication=auth)&lt;br /&gt;
urlpatterns = patterns(&#39;&#39;,&lt;br /&gt;
url(r&#39;^posts/$&#39;, blogposts),&lt;br /&gt;
url(r&#39;^post/(?P&lt;pk&gt;.+)/$&#39;, blogposts),&lt;br /&gt;
.....&lt;br /&gt;
&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
In the example above &quot;blockposts&quot; is mapped to 2 different urls, the second one will be particularly handy to read, update, delete a particular post. Additional parameters from the URL will be passed to the method : read, create, update, delete. I encourage you to check out the code from bitbucket to see how you could take advantage of this technique.&lt;br /&gt;
&lt;br /&gt;
This post barely scratch the surface of how to use django-piston, to put it in a nutshell this reusable application makes creating a web api for your own project simple. It avoids you to write a lot of boilerplate code because it abstracts all the machinery and let you focus on important things.&lt;br /&gt;
&lt;br /&gt;
I would be glad to hear from you how you use it and what is your favorite trick.&lt;br /&gt;
&lt;br /&gt;
&lt;/method&gt;&lt;/insert&gt;</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/8555715915372171113/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2009/09/add-web-api-to-your-app-with-django.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/8555715915372171113'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/8555715915372171113'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2009/09/add-web-api-to-your-app-with-django.html' title='Add a web api to your app with django-piston'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-3246149012286372593</id><published>2009-09-13T22:04:00.001-04:00</published><updated>2009-10-15T18:37:58.513-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="fabric factory"/><category scheme="http://www.blogger.com/atom/ns#" term="project"/><category scheme="http://www.blogger.com/atom/ns#" term="test"/><title type='text'>Fabric factory</title><content type='html'>This is a project I have been working on recently after I spent a day to look at the existing solution to run periodically a test suite. Most of the project I look at were either difficult to setup or require to learn yet another specific domain specific language or had dependency on a larger software stack.&lt;br /&gt;
&lt;br /&gt;
As a reaction to this situation I have decided to see if I could write something simple that achieves gracefully this task. I also try to make it as easy to setup as possible. &lt;br /&gt;
&lt;br /&gt;
I have decided to use cpython as platform, django as web framework for the server and Fabric as library to automate the task execution.&lt;br /&gt;
&lt;br /&gt;
The result of this mix can be found on bitbucket in a project called &lt;a href=&quot;http://bitbucket.org/yml/fabric_factory/&quot;&gt;Fabric Factory&lt;/a&gt;. This will eventually become a complete Job Server that could be used to distribute any kind of task scripted in Fabric.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-weight: bold;font-size:130%;&quot; &gt;Installation&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
This assumes that python is installed on your computer and that you have an internet conection.&lt;br /&gt;
&lt;br /&gt;
You can download the code using mercurial:&lt;br /&gt;
* hg clone http://bitbucket.org/yml/fabric_factory/&lt;br /&gt;
A fabfile will help you to quickly setup your environment.&lt;br /&gt;
* fab quickstart&lt;br /&gt;
&lt;br /&gt;
Note : In order to run the command above you will need the latest version of Fabric the following command will take care of this:&lt;br /&gt;
&lt;code&gt;&lt;br /&gt;
pip install -e git://github.com/bitprophet/fabric.git#egg=Fabric&lt;br /&gt;
&lt;/code&gt;&lt;br /&gt;
&lt;span style=&quot;font-weight: bold;font-size:130%;&quot; &gt;Usage&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&quot;quickstart&quot; has created a virtualenv which must be actived before you continue.&lt;br /&gt;
&lt;code&gt;&lt;br /&gt;
. ve/bin/activate&lt;br /&gt;
&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
Once the virtualenv is activated you can go inside &quot;src/project&quot;. This is a django project so from there you can do several things :&lt;br /&gt;
&lt;br /&gt;
* create an sqlite db :  python manage.py syncdb&lt;br /&gt;
* run the server : python manage.py runserver&lt;br /&gt;
* run the test suite : python manage.py test&lt;br /&gt;
&lt;br /&gt;
The main app of this django project called fabric factory is called &quot;factory&quot;.&lt;br /&gt;
&lt;br /&gt;
Once the server is started and that you have created some &quot;Build&quot; in django&#39;s admin interface you can open a new terminal and run the client side of the project:&lt;br /&gt;
&lt;code&gt;&lt;br /&gt;
cd src/worker&lt;br /&gt;
python run_worker.py --daemon=start&lt;br /&gt;
python run_worker.py --daemon=stop&lt;br /&gt;
&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size:130%;&quot;&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Use case&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Now that you have understood the layout of the project. Let us see how we can achieve something useful with it.&lt;br /&gt;
&lt;br /&gt;
We are going to create a Build that will :&lt;br /&gt;
* download the Fabric Factory&lt;br /&gt;
* setup the environement&lt;br /&gt;
* run the test suite&lt;br /&gt;
* Report the result&lt;br /&gt;
&lt;br /&gt;
1&gt; Direct your browser to that url http://127.0.0.1:8000/admin/ and key in the username/password you have chosen for your administrator.&lt;br /&gt;
2&gt; Add the fabfile recipe store in docs http://127.0.0.1:8000/admin/factory/fabfilerecipe/add/ and call it &quot;fabric factory use case&quot;&lt;br /&gt;
3&gt; Replace example.com by 127.0.0.1:8000 in sites : http://127.0.0.1:8000/admin/sites/site/1/&lt;br /&gt;
4&gt; Create a Build that will download setup and run the test here : http://127.0.0.1:8000/admin/factory/build/add/&lt;br /&gt;
&lt;br /&gt;
The fabfile recipe that we have downloaded earlier contains a task called : &#39;download_setup_and_test&#39; This task as been writen to do what we want.&lt;br /&gt;
&lt;br /&gt;
We are now going to configure the client to run this task. However before doing this let us see how the server publish the tasks that need to be executed. Point your browser to this url : http://127.0.0.1:8000/factory/build/oldest_not_executed/&lt;br /&gt;
&lt;br /&gt;
5&gt; Open a new terminal and move into the the worker directory then you can start the worker in daemon mode :&lt;br /&gt;
&lt;code&gt;&lt;br /&gt;
. ve/bin/activate&lt;br /&gt;
cd src/worker&lt;br /&gt;
python run_worker.py --daemon=start&lt;br /&gt;
&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
If you want to look at what is happening in the background you can watch at the log file in realtime&lt;br /&gt;
&lt;code&gt;&lt;br /&gt;
tail -f worker.log&lt;br /&gt;
&lt;/code&gt;&lt;br /&gt;
6&gt; You can see the status of the build here : http://127.0.0.1:8000/admin/factory/build/ Keep in mind this task is pretty long to run because we are downloading all the dependancies (django, fabric). It took me almost 5 minutes to execute this task and to see the result in the admin.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-weight: bold;font-size:130%;&quot; &gt;Conclusion&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
This project which is still very immature seems to prove that this stack is well suited to build this kind of tool. I would be glad to hear your experience about this kind of tool. Please do not hesitate to copy, fork, contribute to this project to make sure that soon we have a simple easy to setup yet flexible tool to distribute tasks.</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/3246149012286372593/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2009/09/fabric-factory.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/3246149012286372593'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/3246149012286372593'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2009/09/fabric-factory.html' title='Fabric factory'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-4054173695716403839</id><published>2009-06-22T19:28:00.000-04:00</published><updated>2009-06-22T21:38:19.321-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="documentation"/><title type='text'>sphinx autodoc and django app</title><content type='html'>Today I have been getting my foot wet with &lt;a href=&quot;http://sphinx.pocoo.org/ext/autodoc.html&quot;&gt;autodoc&lt;/a&gt; extension from &lt;a href=&quot;http://sphinx.pocoo.org/&quot;&gt;sphinx&lt;/a&gt;. Here it is what the documentation of sphinx say about it :&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(102, 102, 102);&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(102, 102, 102);&quot;&gt;Sphinx is a tool that makes it easy to create intelligent and beautiful     documentation, written by Georg Brandl and licensed under the BSD license.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(102, 102, 102);&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I have to say that before today my experience with sphinx as been excellent. It is relatively straightforward to get started once you pass the first little annoyances that come with the fact that you are learning a new tool.&lt;br /&gt;&lt;br /&gt;The module I have been using to conduct this experiment is &lt;a href=&quot;https://code.launchpad.net/%7Eyml/django-geotagging/geotags&quot;&gt;django-geotagging&lt;/a&gt; this reusable app enables you to geotag any object in the database. If you want to know more about this you can read the sphinx based documentation. This is the goal of the documentation, isn&#39;t it ?  :-)&lt;br /&gt;If adding a manually written documentation is very easy and well documented. The modification required to move from a very primitive documentation written using some rst file to sphinx is shown &lt;a href=&quot;http://bazaar.launchpad.net/%7Eyml/django-geotagging/geotags/revision/52&quot;&gt;here&lt;/a&gt;. Most of it is automatically generated by : &lt;a href=&quot;http://sphinx.pocoo.org/intro.html?highlight=quickstart&quot;&gt;sphinx-quickstart&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Taking it a step further have been more difficult than anticipated and this for several reasons :  lack of example, and a bug&lt;br /&gt;Today I wanted to add to the existing documentation in django-geotagging and API section. In order to do so I have spoted in the sphinx documentation an extension called &lt;a href=&quot;http://sphinx.pocoo.org/ext/autodoc.html?highlight=autodoc#module-sphinx.ext.autodoc&quot;&gt;autodoc&lt;/a&gt;. It seems to be exactly what I need, here it is an extract from its doc :&lt;br /&gt;&lt;br /&gt;&quot;&quot;&quot;&lt;br /&gt;&lt;blockquote style=&quot;color: rgb(102, 102, 102);&quot;&gt;This extension can import the modules you are documenting, and pull in documentation from docstrings in a semi-automatic way.&lt;/blockquote&gt;&quot;&quot;&quot;&lt;br /&gt;&lt;br /&gt;The first modification I add to do in sphinx&#39;s conf.py was to setup the settings in my environment. This can be done by adding the following 3 lines to conf.py&lt;br /&gt;&lt;br /&gt;&quot;&quot;&quot;&lt;br /&gt;&lt;span style=&quot;color: rgb(102, 102, 102);&quot;&gt;from geotagging_demo_project import settings&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(102, 102, 102);&quot;&gt;from django.core.management import setup_environ&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(102, 102, 102);&quot;&gt;setup_environ(settings)&lt;/span&gt;&lt;br /&gt;&quot;&quot;&quot;&lt;br /&gt;&lt;br /&gt;Once this is done you should just be able to create a file that will be used as placeholder to describe the documentation you want to extract. Let us take an example now, since I want to describe the API of &quot;models.py&quot; I am going to create a file called &quot;model.rst&quot;. In this file I need to add the following lines :&lt;br /&gt;&lt;br /&gt;&quot;&quot;&quot;&lt;br /&gt;:mod:`models` --  geotag models&lt;br /&gt;==========================================&lt;br /&gt;&lt;br /&gt;.. automodule:: geotags.models&lt;br /&gt;   :members:&lt;br /&gt;   :show-inheritance:&lt;br /&gt;&lt;br /&gt;.. autoclass:: Point&lt;br /&gt;&quot;&quot;&quot;&lt;br /&gt;&lt;br /&gt;I will let you read the documentation for each of this directive to understand what they are doing. This is were the bug come into play because once you have done this you should be able to enjoy the automatically extrated documentation the next time you build it. Instead of this I have observed this &lt;a href=&quot;http://code.djangoproject.com/ticket/11353#preview&quot;&gt;bug&lt;/a&gt;. To put it in a nutshell sphinx is complaining about the line 27 of&lt;br /&gt;&quot;/usr/lib/python2.6/django/contrib/gis/db/models/proxy.py&quot;&lt;br /&gt;&lt;br /&gt;I am not really sure where the bug is however it seems that doing a small modification there enable me to build the documentation.&lt;br /&gt;&lt;br /&gt;I hope that this feedback about my experience will help you to get started with using sphinx and its autodoc extension. If you have an opinion about the django ticket #11353 I am very interested to hear it.</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/4054173695716403839/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2009/06/sphinx-autodoc-and-django-app.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/4054173695716403839'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/4054173695716403839'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2009/06/sphinx-autodoc-and-django-app.html' title='sphinx autodoc and django app'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-3920621440264561288</id><published>2009-02-27T13:31:00.001-04:00</published><updated>2009-11-03T21:12:36.653-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cherokee"/><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="tip"/><title type='text'>Serving Django via CherryPy behind cherokee</title><content type='html'>Almost one year ago &quot;Peter Baumgartner&quot; wrote about &lt;a href=&quot;http://lincolnloop.com/blog/2008/mar/25/serving-django-cherrypy/&quot;&gt;Serving Django via cherrypy&lt;/a&gt;  I am going to explain in this post how to take this approach one step further and to add load and balancing of the requests to several instances of &lt;a href=&quot;http://www.cherrypy.org/&quot;&gt;cherrypy&lt;/a&gt;. This sounds like a lot of &quot;*.conf&quot; editing, isn&#39;t it ? In fact the nice thing about this approach is that the only file you will have to edit is your settings.py to add one line. To do so I am going to use &lt;a href=&quot;http://www.cherokee-project.com/&quot;&gt;cherokee&lt;/a&gt; mainly because it has a user friendly interface called       &lt;b&gt;cherokee-admin&lt;/b&gt; which provides a very easy way to       configure your server. For this article I have used cherokee  Version 0.98.1.&lt;br /&gt;
&lt;br /&gt;
I will assume in this article that you have a working django project in a virtualenv. First you will need to install cherrypy and django-cpserver. You have several way to do this the easier is probably to use pip&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: #999999;&quot;&gt;pip install cherrypy&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: #999999;&quot;&gt;pip install -e git://github.com/lincolnloop/django-cpserver.git#egg=django-cpserver&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Then you need to edit you settings.py to add &quot;django_cpserver&quot; in the list of your INSTALLED_APPS. This will give you a convenient django management command to start cherrypy server.&lt;br /&gt;
&lt;span style=&quot;color: #666666;&quot;&gt;./manage.py runcpserver port=8089&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Believe it or not this was the hardest part of the recipe from now to the end we will use a nice web interface. In order to launch cherokee-admin on ubuntu I use the following command :&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: #666666;&quot;&gt;sudo cherokee-admin&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
We  need to define 2 remote sources in the &lt;a href=&quot;http://localhost:9090/&quot;&gt;admin interface&lt;/a&gt; :&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiDu-cXiFsaaenAoCux5UamSji5qaTTrXr8xJLwjJWNFSQa82PRGg0KHYwszy05HiKAZJQXyhqYlNIkD1tRSGn1Czml9RfnM4r4yvSxHwRNP088p2zyKWQ6qP0RSQZFLI_cSeib3KQiEM/s1600-h/information_sources.png&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; id=&quot;BLOGGER_PHOTO_ID_5307541501458773506&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiDu-cXiFsaaenAoCux5UamSji5qaTTrXr8xJLwjJWNFSQa82PRGg0KHYwszy05HiKAZJQXyhqYlNIkD1tRSGn1Czml9RfnM4r4yvSxHwRNP088p2zyKWQ6qP0RSQZFLI_cSeib3KQiEM/s320/information_sources.png&quot; style=&quot;cursor: pointer; display: block; height: 94px; margin: 0px auto 10px; text-align: center; width: 320px;&quot; /&gt;&lt;/a&gt;127.0.0.1:8088 and 127.0.0.1:8088 are the addresses on which cherokee can contact the cherrypy instances. Several interested things to note here, the adresses can be spread on several computer and several ports.&lt;br /&gt;
&lt;br /&gt;
Then we need to define a new target &quot;/django&quot; (alias)  that will load and balance the requests to cherrypy instances.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSpRF_Jt7z5VbQVfnm1FHGXCAx-U3TKvdvYJmGvMkkaPKzTSw0ePbLlkCNMSqFfTgRnxVu34kyqXfkQv8Y_v2Tiy57-m6ivg_nTyLOH9TeiqY4Xh1dBkfkSV_niWlc30UZrwYp_4z5Tvo/s1600-h/targets.png&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; id=&quot;BLOGGER_PHOTO_ID_5307543529131824482&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSpRF_Jt7z5VbQVfnm1FHGXCAx-U3TKvdvYJmGvMkkaPKzTSw0ePbLlkCNMSqFfTgRnxVu34kyqXfkQv8Y_v2Tiy57-m6ivg_nTyLOH9TeiqY4Xh1dBkfkSV_niWlc30UZrwYp_4z5Tvo/s320/targets.png&quot; style=&quot;cursor: pointer; height: 150px; width: 320px;&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Then for this target we need to set the handler to &quot;HTTP reverse proxy&quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7p6m3cGlsS_udgNjLQWbdnBPvYJVqXxl8cEwheR-D-3s-hXI8FH_c0iyxLJ6u1-09V4d_2d7Xb6Cpjb0ALreCrU88tT9IfqWZifLqNT6ITRKEzw_tNemltH4RhhL5b5lE86CEgZcKi34/s1600-h/targets.png&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; id=&quot;BLOGGER_PHOTO_ID_5307545530044270834&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7p6m3cGlsS_udgNjLQWbdnBPvYJVqXxl8cEwheR-D-3s-hXI8FH_c0iyxLJ6u1-09V4d_2d7Xb6Cpjb0ALreCrU88tT9IfqWZifLqNT6ITRKEzw_tNemltH4RhhL5b5lE86CEgZcKi34/s320/targets.png&quot; style=&quot;cursor: pointer; display: block; height: 150px; margin: 0px auto 10px; text-align: center; width: 320px;&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
It is time to use the  remote sources we have defined earlier.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEic53ROwQMoHswCWEpSC8bSoI6TR0ccaE-mZlNZxdIHjxQSQqqz6AF1sN7D8UbZZXGjQteHiSbk3iuCY1wVJSfXnyA34-YhX-EoKcuo3WKo9l4xWNEx9GRI5GlciGAUHDHAFlPwXDgtcZQ/s1600-h/balancer_and_information_sources.png&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; id=&quot;BLOGGER_PHOTO_ID_5307545522310409074&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEic53ROwQMoHswCWEpSC8bSoI6TR0ccaE-mZlNZxdIHjxQSQqqz6AF1sN7D8UbZZXGjQteHiSbk3iuCY1wVJSfXnyA34-YhX-EoKcuo3WKo9l4xWNEx9GRI5GlciGAUHDHAFlPwXDgtcZQ/s320/balancer_and_information_sources.png&quot; style=&quot;cursor: pointer; display: block; height: 112px; margin: 0px auto 10px; text-align: center; width: 320px;&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
The last bit is to rewrite the url before passing it to the cherrypy instances&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWpBeeX5wrqonrVztlQTuyofWvcwvY-kwIW9cix53gWj2wlZozJwHwvaTpRbVn40TeSlUIOzAm14Hpku-sCq36GcTgMUF5jFZayOq55neoJaI9b9E_zonRZAVeQyBZfS3tBNqkIARznq8/s1600-h/url_rewriting.png&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; id=&quot;BLOGGER_PHOTO_ID_5307545525452909378&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWpBeeX5wrqonrVztlQTuyofWvcwvY-kwIW9cix53gWj2wlZozJwHwvaTpRbVn40TeSlUIOzAm14Hpku-sCq36GcTgMUF5jFZayOq55neoJaI9b9E_zonRZAVeQyBZfS3tBNqkIARznq8/s320/url_rewriting.png&quot; style=&quot;cursor: pointer; display: block; height: 90px; margin: 0px auto 10px; text-align: center; width: 320px;&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
This is the end of the recipe you can now save the modification and restart the cherokee. I would be glad to read from you the enhancements that could be added to this recipe.</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/3920621440264561288/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2009/02/serving-django-via-cherrypy-behind.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/3920621440264561288'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/3920621440264561288'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2009/02/serving-django-via-cherrypy-behind.html' title='Serving Django via CherryPy behind cherokee'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiDu-cXiFsaaenAoCux5UamSji5qaTTrXr8xJLwjJWNFSQa82PRGg0KHYwszy05HiKAZJQXyhqYlNIkD1tRSGn1Czml9RfnM4r4yvSxHwRNP088p2zyKWQ6qP0RSQZFLI_cSeib3KQiEM/s72-c/information_sources.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-2696197548858977312</id><published>2009-02-21T13:49:00.000-04:00</published><updated>2009-02-22T19:34:16.676-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="fts"/><title type='text'>django full text search with solango</title><content type='html'>2 months ago I wrote a post titled &quot;&lt;a href=&quot;http://yml-blog.blogspot.com/2008/12/django-fulltext-search-part-1.html&quot;&gt;&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_0&quot;&gt;django&lt;/span&gt; &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_1&quot;&gt;fulltext&lt;/span&gt; search part-1&lt;/a&gt;&quot; this post was explaining how to take advantage of &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_2&quot;&gt;djangosearch&lt;/span&gt; to interface between &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_3&quot;&gt;django&lt;/span&gt; and &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_4&quot;&gt;solr&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;The big advantage of &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_5&quot;&gt;djangosearch&lt;/span&gt; is the fact that it comes with a &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_6&quot;&gt;plugable&lt;/span&gt; &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_7&quot;&gt;backend&lt;/span&gt; architecture. This can be a &lt;span class=&quot;blsp-spelling-corrected&quot; id=&quot;SPELLING_ERROR_8&quot;&gt;strength&lt;/span&gt; since in theory it enables you to abstract  the details of the the full text engine you are using however in practice I ended up writing a &lt;a href=&quot;http://code.google.com/p/djangosearch/issues/detail?id=11&quot;&gt;patch &lt;/a&gt;to bypath the abstraction layer because it was preventing me of doing the query I wanted. So to make a long story short &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_9&quot;&gt;djangosearch&lt;/span&gt; was not working out of the box for my needs.&lt;br /&gt;&lt;br /&gt;However 2 week ago &lt;a href=&quot;http://www.screeley.com/djangosolr/&quot;&gt;Sean &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_10&quot;&gt;Creeley&lt;/span&gt; released &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_11&quot;&gt;solango&lt;/span&gt;&lt;/a&gt; and this significantly changed landscape of full text search in the &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_12&quot;&gt;django&lt;/span&gt; &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_13&quot;&gt;eco&lt;/span&gt;-system.  I will not go in the details about &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_14&quot;&gt;solango&lt;/span&gt; in this post.  It comes with some management commands that are so convenient that I still wonder why I haven&#39;t thought at implementing them on top of &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_15&quot;&gt;djangosearch&lt;/span&gt;. I will copy below a short extract form the documentation which is excellent :&lt;br /&gt;&lt;pre style=&quot;color: rgb(153, 153, 153);&quot;&gt;./manage.&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_16&quot;&gt;py&lt;/span&gt; &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_17&quot;&gt;solr&lt;/span&gt; --help&lt;br /&gt;#&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_18&quot;&gt;solango&lt;/span&gt; schema options&lt;br /&gt;--fields              Prints out the fields the schema.&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_19&quot;&gt;xml&lt;/span&gt; will create&lt;br /&gt;--flush               Will remove the data directory from &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_20&quot;&gt;Solr&lt;/span&gt;.&lt;br /&gt;--&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_21&quot;&gt;reindex&lt;/span&gt;             Will &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_22&quot;&gt;reindex&lt;/span&gt; &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_23&quot;&gt;Solr&lt;/span&gt; from the registry.&lt;br /&gt;--schema              Will create the schema.&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_24&quot;&gt;xml&lt;/span&gt; in &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_25&quot;&gt;SOLR&lt;/span&gt;_SCHEMA_PATH or in the --path.&lt;br /&gt;--start               Start &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_26&quot;&gt;solr&lt;/span&gt; running java -jar start.jar&lt;br /&gt;--path=SCHEMA_PATH    Tells &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_27&quot;&gt;Solango&lt;/span&gt; where to create &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_28&quot;&gt;config&lt;/span&gt; file&lt;/pre&gt;&lt;br /&gt;In the rest of the post I am going to assume that you have installed and configured &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_29&quot;&gt;solango&lt;/span&gt;. When I have done this I have not seen any major obstacle. Once again Sean has done an excellent job at documenting this project. one thing that annoys me while implementing &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_30&quot;&gt;solango&lt;/span&gt; in one of the project I am working on is the fact that you cannot restrict your search in what is often call an &quot;advanced search&quot;. The good news is that &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_31&quot;&gt;solango&lt;/span&gt; has been &lt;a href=&quot;http://code.google.com/p/django-solr-search/source/detail?r=10#&quot;&gt;recently improved&lt;/a&gt; to be easily extended. The only thing you will have to do is to defined a &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_32&quot;&gt;django&lt;/span&gt; form to represent your advanced search and to add an &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_33&quot;&gt;url&lt;/span&gt; that use it. This can literally be done in less than 30 lines of code including the comments and the imports.&lt;br /&gt;&lt;br /&gt;Let us start by the writing the forms.&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_34&quot;&gt;py&lt;/span&gt; :&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;from &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_35&quot;&gt;django&lt;/span&gt; import forms&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;from &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_36&quot;&gt;solango&lt;/span&gt;.&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_37&quot;&gt;solr&lt;/span&gt; import get_model_from_key&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;import &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_38&quot;&gt;solango&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;def model_choices():&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;    &quot;&quot;&quot;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;    Return a list of &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_39&quot;&gt;tuple&lt;/span&gt; with all the models that have been indexed.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;    This &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_40&quot;&gt;tuple&lt;/span&gt; is used in the &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_41&quot;&gt;AdvancedSearchForm&lt;/span&gt; to selects the models&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;    you want to search your term in.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;    &quot;&quot;&quot;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;    models = [(model_key, get_model_from_key(model_key)._meta.verbose_name_plural)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;             for model_key in solango.registry.keys()]&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;    return models&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;class &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_42&quot;&gt;AdvancedSearchForm&lt;/span&gt;(forms.Form):&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;    &quot;&quot;&quot;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;    Form that represents an advanced search&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;    &quot;&quot;&quot;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;    q = forms.&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_43&quot;&gt;CharField&lt;/span&gt;(required=False)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;    model = forms.&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_44&quot;&gt;MultipleChoiceField&lt;/span&gt;(choices=model_choices(), required=False,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;        widget=forms.&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_45&quot;&gt;CheckboxSelectMultiple&lt;/span&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;    def clean_q(self):&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;        q = self.cleaned_data.get(&quot;q&quot;)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;        if q == &#39;&#39;:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;            raise forms.&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_46&quot;&gt;ValidationError&lt;/span&gt;(&quot;You cannot query for an empty string&quot;)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;        return q&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;Nothing really complex there we have defined a form  with a &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_47&quot;&gt;charfield&lt;/span&gt; named &quot;q&quot; that will be used to enter your search terms and a set of check boxes displaying all the models indexed. The user will be able to select one or several models to restrict its query.&lt;br /&gt;Now that the form is defined we are going to use it. In order to do so you need to add the following &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_48&quot;&gt;url&lt;/span&gt; somewhere in your project.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;from django.conf.urls.defaults import *&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;from &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_49&quot;&gt;solango&lt;/span&gt;.views import select&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;from populous.search.forms import &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_50&quot;&gt;AdvancedSearchForm&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_51&quot;&gt;urlpatterns&lt;/span&gt; = patterns(&#39;&#39;,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;            &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_52&quot;&gt;url&lt;/span&gt;(r&#39;^advanced/$&#39;, select,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;                {&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;                    &quot;form_class&quot;:&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_53&quot;&gt;AdvancedSearchForm&lt;/span&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;                    &quot;template_name&quot;:None,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;                },name=&quot;search-advanced&quot;),&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You will recognize there the same pattern used in the generic views. You can customize the &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_54&quot;&gt;solango&#39;s&lt;/span&gt; view, called &quot;select&quot;, by passing a &quot;form_class&quot; and a &quot;template_name&quot;.  In this example I have not over loaded the template_name thus &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_55&quot;&gt;django&lt;/span&gt; will used the &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_56&quot;&gt;solango&lt;/span&gt; default template.&lt;br /&gt;&lt;br /&gt;I would be glad to read from you the customized form you have built on top of &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_57&quot;&gt;solango&lt;/span&gt;. Please do not hesitate to post them as comment.&lt;br /&gt;&lt;br /&gt;Updated the 22th Feb 2009 :  Correct the name of Sean Creeley, sorry for that.</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/2696197548858977312/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2009/02/django-full-text-search-with-solango_21.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/2696197548858977312'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/2696197548858977312'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2009/02/django-full-text-search-with-solango_21.html' title='django full text search with solango'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-1815560795995093185</id><published>2009-01-31T21:41:00.000-04:00</published><updated>2009-01-31T22:27:44.741-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="GIS"/><title type='text'>Enable distutils for a django reusable app</title><content type='html'>As you might already be aware &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_0&quot;&gt;Pinax&lt;/span&gt; move to &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_1&quot;&gt;distutils&lt;/span&gt;, this change has been described by James &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_2&quot;&gt;Tauber&lt;/span&gt; in &lt;a href=&quot;http://jtauber.com/blog/2009/01/31/moving_to_disutils/&quot;&gt;this post.&lt;/a&gt; Today I have decided to make my feet wet with this approach for a &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_3&quot;&gt;django&lt;/span&gt; reusable app. I have been guided by &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_4&quot;&gt;jezdez&lt;/span&gt; on #&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_5&quot;&gt;pinax&lt;/span&gt;.&lt;br /&gt;Before starting I would recommend you to read &lt;a href=&quot;http://peak.telecommunity.com/DevCenter/setuptools&quot;&gt;this page&lt;/a&gt;, yes I know, it is a bit long but very interesting. after this reading you will be well prepared to start to work on you &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_7&quot;&gt;django&lt;/span&gt; reusable app.&lt;br /&gt;&lt;br /&gt;I have used one of my project &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_8&quot;&gt;django&lt;/span&gt;-&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_9&quot;&gt;geotagging&lt;/span&gt; to experiment with this approach. The project can be found on launchpad &lt;a href=&quot;https://launchpad.net/django-geotagging&quot;&gt;there&lt;/a&gt;. The good news about this approach is that all the major &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_10&quot;&gt;rcs&lt;/span&gt; are supported : &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_11&quot;&gt;SVN&lt;/span&gt;, &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_12&quot;&gt;BZR&lt;/span&gt;, HG, ... If your favorite versionning system is missing there is a good chance that I just forget to mention  it.  This list is not exhaustive.&lt;br /&gt;&lt;br /&gt;The core of this approach is a file called &lt;a href=&quot;http://bazaar.launchpad.net/%7Eyml/django-geotagging/geotags/annotate/head%3A/setup.py&quot;&gt;setup.&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_14&quot;&gt;py&lt;/span&gt;&lt;/a&gt; that need to be paced at the root of your repository, most of its argument are self explanatory.&lt;br /&gt;&lt;br /&gt;This file enable you to setup a complete &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_15&quot;&gt;env&lt;/span&gt; in 4 steps  :&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Create a virtual &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_16&quot;&gt;env&lt;/span&gt; : &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_17&quot;&gt;virtualenv&lt;/span&gt; &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_18&quot;&gt;geotagging&lt;/span&gt;_&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_19&quot;&gt;env&lt;/span&gt;&lt;/li&gt;&lt;li&gt; Move into that &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_20&quot;&gt;env&lt;/span&gt; :  &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_21&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_22&quot;&gt;geotagging&lt;/span&gt;_&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_23&quot;&gt;env&lt;/span&gt;&lt;/li&gt;&lt;li&gt; Activate the &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_24&quot;&gt;env&lt;/span&gt; : . bin/activate&lt;/li&gt;&lt;li&gt; Install pip : easy_install pip&lt;/li&gt;&lt;li&gt; Use pip to grab &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_25&quot;&gt;django&lt;/span&gt;-&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_26&quot;&gt;geotagging&lt;/span&gt; : pip install -e &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_27&quot;&gt;bzr&lt;/span&gt;+&lt;a rel=&quot;nofollow&quot; href=&quot;http://bazaar.launchpad.net/%7Eyml/django-geotagging/geotags/#egg=django-geotags&quot;&gt;http://&lt;wbr&gt;bazaar.&lt;wbr&gt;launchpad.&lt;wbr&gt;net/~&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_28&quot;&gt;yml&lt;/span&gt;/&lt;wbr&gt;&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_29&quot;&gt;django&lt;/span&gt;-&lt;wbr&gt;&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_30&quot;&gt;geotagging&lt;/span&gt;/&lt;wbr&gt;&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_31&quot;&gt;geotags&lt;/span&gt;/&lt;wbr&gt;#egg=&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_32&quot;&gt;django&lt;/span&gt;-&lt;wbr&gt;&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_33&quot;&gt;geotags&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;The coolness of this increase with the number of reusable apps you have to install in order to build your web project.&lt;br /&gt;&lt;br /&gt;I would be glad to learn from you what kind of cool things can be done once this infrastructure is in place.</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/1815560795995093185/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2009/01/enable-distutils-for-django-reusable.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/1815560795995093185'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/1815560795995093185'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2009/01/enable-distutils-for-django-reusable.html' title='Enable distutils for a django reusable app'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-490727529431759956</id><published>2009-01-19T20:09:00.000-04:00</published><updated>2009-01-19T20:57:30.174-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="GIS"/><category scheme="http://www.blogger.com/atom/ns#" term="tip"/><title type='text'>How to use the same widget as GeoDjango</title><content type='html'>At the end of this post you will be able to use the same widget than the automatically generated admin interface in geodjango. I spent quite sometimes today to rediscover how to do this. Yes, rediscover because I have already written about this a couple of month ago. My first post on that topic can be read &lt;a href=&quot;http://yml-blog.blogspot.com/2008/10/how-to-create-view-to-add-object.html&quot;&gt;there&lt;/a&gt;. Happily &quot;jbronn&quot; on #geodjango gave the solution to me.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# Getting an instance so we can generate the map widget; also&lt;br /&gt;# getting the geometry field for the model.&lt;br /&gt;admin_instance = PointAdmin(Point, admin.site)&lt;br /&gt;point_field = Point._meta.get_field(&#39;point&#39;)&lt;br /&gt;&lt;br /&gt;# Generating the widget.&lt;br /&gt;PointWidget = admin_instance.get_map_widget(point_field)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In fact all the complication at the moment there is no static widget that widget you could use in your own form. You have to build them dynamically.&lt;br /&gt;&lt;br /&gt;I am now going to break down the 3 lines of code.&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;PointAdmin&lt;/span&gt; is the &lt;tt class=&quot;docutils literal&quot;&gt;&lt;span class=&quot;pre&quot;&gt;ModelAdmin&lt;/span&gt;&lt;/tt&gt; class which is a representation of a model in the admin interface.  Here it is an example :&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from django.contrib.gis import admin&lt;br /&gt;from geotagging.models import Point&lt;br /&gt;&lt;br /&gt;class PointAdmin(admin.GeoModelAdmin):&lt;br /&gt;list_filter = (&#39;content_type&#39;,&#39;point&#39; )&lt;br /&gt;list_display = (&#39;object&#39;, &#39;point&#39;, &#39;content_type&#39;, &#39;object_id&#39;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Point&lt;/span&gt; in the model we are working on  so &lt;span style=&quot;font-weight: bold;&quot;&gt;Point&lt;/span&gt;._meta.get_field(&#39;point&#39;) is accessing the field called point of the Point mode. The code below should help you to understand :&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class Point(models.Model):&lt;br /&gt;  &quot;&quot;&quot;&lt;br /&gt;  &quot;&quot;&quot;&lt;br /&gt;  point = models.PointField(verbose_name=_(&quot;point&quot;),srid=4326)&lt;br /&gt;  content_type   = models.ForeignKey(ContentType,&lt;br /&gt;          related_name=&quot;content_type_set_for_%(class)s&quot;)&lt;br /&gt;  object_id      = models.CharField(_(&#39;object ID&#39;),max_length=50)&lt;br /&gt;  object = generic.GenericForeignKey(ct_field=&quot;content_type&quot;, fk_field=&quot;object_id&quot;)&lt;br /&gt;  objects = models.GeoManager()&lt;br /&gt;&lt;br /&gt;  def __unicode__(self):&lt;br /&gt;      return &#39;Point for %s&#39; % self.object&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The last line is actually where the geodjango specificity is :&lt;br /&gt; * PointWidget = admin_instance.get_map_widget(point_field)&lt;br /&gt;get_map_widget is defined &lt;a href=&quot;http://code.djangoproject.com/browser/django/trunk/django/contrib/gis/admin/options.py#L63&quot;&gt;here&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Now that we have a PointWidget we can use it in our form. Here is is a small example :&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class PointForm(forms.ModelForm):&lt;br /&gt;    point = forms.CharField(widget=PointWidget())&lt;br /&gt;    class Meta:&lt;br /&gt;        model = Point&lt;br /&gt;        exclude = (&quot;content_type&quot;,&quot;object_id&quot;)&lt;br /&gt;    class Media:&lt;br /&gt;        js = (&quot;http://openlayers.org/api/2.6/OpenLayers.js&quot;,)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now you can use geodjango super widgets in your forms.</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/490727529431759956/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2009/01/how-to-use-same-widget-than-geodjango.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/490727529431759956'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/490727529431759956'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2009/01/how-to-use-same-widget-than-geodjango.html' title='How to use the same widget as GeoDjango'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-1667289657643049685</id><published>2008-12-13T21:10:00.000-04:00</published><updated>2008-12-13T22:56:51.256-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="PLM"/><title type='text'>Social Product Development Network</title><content type='html'>I am a bit overwhelmed by the number of excelent PLM related blog posts. Today I am going to try to answer the question asked by Oleg : &lt;a href=&quot;http://plmtwine.com/2008/12/13/how-to-implement-social-networking-for-plm/&quot;&gt;How to implement Social Networking for PLM ?&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;PLM has always been the concept of managing the information produce by a variety of Computer Aided Authoring tools. In a PLM system everything is organized around the BOM, so all this information is linked to Part and the Parts are organized in a tree.&lt;br /&gt;PLM vendor love to claim that they are also capturing the communication thanks to their workflow system.&lt;br /&gt;But the reality is that these workflows capture at best 2 or 3 very well formalized process : RFQ, ECN, ...&lt;br /&gt;&lt;br /&gt;But everybody in the companies knows that the most important discussion about the product developement are done else where : by emails, over phone, meetings... This is even more true with the concept of global engineering team located in several countries. Some companies have a very aggressive objective in term offshore engineering and design, I know a couple of companies that are targeting 50 % of these activities in low cost countries.&lt;br /&gt;&lt;br /&gt;I think that time has come to invent yet another acronym Social Product Development Network -- &lt;span style=&quot;font-weight: bold;&quot;&gt;SPDN.&lt;/span&gt;&lt;span&gt; Thi&lt;/span&gt;&lt;span&gt;s new marketing beast will offer :&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt; an internal blog to every employee with the possibility to publish article on internet.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;a wiki to capture the user generated documentation around the Product&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;An issue tracker to follow the issue related to the product development&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;A twitter clone to exchange short messages&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;gallery to share pictures of the product prototypes, tests , ...&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;BOM management&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;PDM&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Workflow to control the 2 or 3 key processes&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;You are going to tell me that this is impossible because PLM vendor does not have the resources/skills to implement all this software stack. You are right but the good news is that you don&#39;t have to do it. All this and much more is already available out there in many flavors. For example I have been recently contributing and using  such platform recently. It is called &lt;a href=&quot;http://pinaxproject.com/&quot;&gt;Pinax&lt;/a&gt; a video presenting the solution is available&lt;a href=&quot;http://www.youtube.com/watch?v=1J91Ownq-7g&quot;&gt; there&lt;/a&gt;. The only pieces that you will have to worry about are the core of the PLM (BOM, PDM, Workflow) and for each of them you will have only to focuse on implementing the vision on an leveraging a modern infrastructure.&lt;br /&gt;No you don&#39;t have to develop yet another ORM it is there. No you don&#39;t have to invent yet another template language, or an url dispatcher, a cache stragetgy. You use all your ressources to focus on the implementation of the PLM vision rather than the technical details. This technology power web site that receive million hits a day. So I assume it will scale the PLM needs.&lt;br /&gt;&lt;br /&gt;But all this requires a complete shift from selling software licences to sell a vision.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Conclusion :&lt;/span&gt;&lt;br /&gt;If you really have a vision for PLM sell it with the service to implement it rather than the licences.</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/1667289657643049685/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2008/12/social-product-development-network.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/1667289657643049685'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/1667289657643049685'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2008/12/social-product-development-network.html' title='Social Product Development Network'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-800038716578091119</id><published>2008-12-12T15:40:00.000-04:00</published><updated>2008-12-12T16:36:52.991-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="tip"/><title type='text'>Django fulltext search -- part 1</title><content type='html'>I have been recently working on adding &lt;span class=&quot;blsp-spelling-corrected&quot; id=&quot;SPELLING_ERROR_0&quot;&gt;full text&lt;/span&gt; search to a &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_1&quot;&gt;django&lt;/span&gt; based web application since this is not yet particularly well documented. Here it is my little stone to the amazing pyramid already in place waiting for you to use it. In order to add this &quot;must have&quot; feature to your great web application you will need 3 components :&lt;a href=&quot;http://code.google.com/p/djangosearch/&quot;&gt;&lt;br /&gt;&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://code.google.com/p/djangosearch/&quot;&gt;&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_2&quot;&gt;djangosearch&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://code.google.com/p/pysolr/&quot;&gt;&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_3&quot;&gt;pysolr&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://lucene.apache.org/solr/&quot;&gt;&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_4&quot;&gt;solr&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Starting by the latest the first challenge is to get it installed, I haven&#39;t had luck with the usual &quot;apt-get install &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_5&quot;&gt;solr&lt;/span&gt;&quot;. It must be me but after an installation I was only getting a blank page instead of &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_6&quot;&gt;solr&lt;/span&gt; admin interface. On #&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_7&quot;&gt;solr&lt;/span&gt; channel someone advise me to just grab the &lt;a href=&quot;http://mirrors.enquira.com/apache/lucene/solr/1.3.0/&quot;&gt;latest release tar ball&lt;/a&gt; and to run with it. Then I have performed the &lt;a href=&quot;http://lucene.apache.org/solr/tutorial.html&quot;&gt;tutorial&lt;/a&gt; this help me a lot to get started with &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_8&quot;&gt;solr&lt;/span&gt;. Believe it or not when you are there you have done the hardest part of it.&lt;br /&gt;&lt;br /&gt;Now you need to go inside the &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_9&quot;&gt;django&lt;/span&gt; project you want to work on and follow the following recipe :&lt;br /&gt;&lt;ol&gt;&lt;li&gt;download and put into your &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_10&quot;&gt;PYHTONPATH&lt;/span&gt; &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_11&quot;&gt;djangosearch&lt;/span&gt; and &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_12&quot;&gt;pysolr&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Add &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_13&quot;&gt;djangosearch&lt;/span&gt; inside the INSTALLED_APPS &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_14&quot;&gt;tupple&lt;/span&gt;&lt;/li&gt;&lt;li&gt;add the following settings :&lt;/li&gt;&lt;/ol&gt;&lt;ul&gt;&lt;li&gt;SEARCH_ENGINE = &quot;&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_15&quot;&gt;solr&lt;/span&gt;&quot; # &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_16&quot;&gt;solr&lt;/span&gt;, &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_17&quot;&gt;lucene&lt;/span&gt;, &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_18&quot;&gt;xapian&lt;/span&gt;, &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_19&quot;&gt;estraier&lt;/span&gt;&lt;/li&gt;&lt;li&gt;SEARCH_RESULTS_PER_PAGE = 10&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_20&quot;&gt;SOLR&lt;/span&gt;_URL= &quot;http://localhost:8983/solr&quot;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;At this point you are almost done the last operations you will need to do is to define inside models.&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_21&quot;&gt;py&lt;/span&gt; the fields you want to index with &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_22&quot;&gt;solr&lt;/span&gt;. In order to do this &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_23&quot;&gt;djangosearch&lt;/span&gt; come with a class called : &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_24&quot;&gt;ModelIndex&lt;/span&gt;&lt;br /&gt;For example if you want to index : tease, and story fields of your Story model you will do something like this :&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_25&quot;&gt;djangosearch&lt;/span&gt; import &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_26&quot;&gt;ModelIndex&lt;/span&gt;&lt;br /&gt;class Story(models.Model):&lt;br /&gt;  ...&lt;br /&gt; tease = models.&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_27&quot;&gt;TextField&lt;/span&gt;(_(&#39;tease&#39;), blank=True)&lt;br /&gt; story = models.&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_28&quot;&gt;TextField&lt;/span&gt;(_(&#39;story&#39;), blank=True)&lt;br /&gt;  ...&lt;br /&gt;  index = &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_29&quot;&gt;ModelIndex&lt;/span&gt;(fields=[&quot;tease&quot;,&quot;story&quot;])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now you need to tell &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_30&quot;&gt;solr&lt;/span&gt; about this 2 new fields this is &lt;span class=&quot;blsp-spelling-corrected&quot; id=&quot;SPELLING_ERROR_31&quot;&gt;in fact&lt;/span&gt; easier that it seems to be, the theory is explained &lt;a href=&quot;http://wiki.apache.org/solr/SchemaXml&quot;&gt;there&lt;/a&gt;.  In practice what you need to do open this file (apache-solr-1.3.0/example/solr/conf/schema.xml) with your editor of choice and add this 2 lines inside the fields section:&lt;br /&gt;&lt;code&gt;&lt;br /&gt; &lt;field name=&quot;tease&quot; type=&quot;text&quot; indexed=&quot;true&quot; stored=&quot;true&quot;&gt;&lt;br /&gt; &lt;field name=&quot;story&quot; type=&quot;text&quot; indexed=&quot;true&quot; stored=&quot;true&quot;&gt;&lt;br /&gt;&lt;/field&gt;&lt;/field&gt;&lt;/code&gt;&lt;br /&gt;You will now need to restart &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_32&quot;&gt;solr&lt;/span&gt; in order to take this modification into account.&lt;br /&gt;&lt;br /&gt;The meal is now ready you can go and taste it.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Open a browser go to the admin interface and add a new story or update an existing one.&lt;/li&gt;&lt;li&gt;Open a &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_33&quot;&gt;django&lt;/span&gt; shell and enjoy your new full text search&lt;/li&gt;&lt;/ul&gt;&lt;pre&gt;&lt;br /&gt;from &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_34&quot;&gt;dajngosearch&lt;/span&gt; import search&lt;br /&gt;search_results = search(&quot;foo&quot;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I hope this will be useful to someone and will avoid you the pain I had to get all these pieces dancing together.&lt;br /&gt;I would be glad to read from you what can be improve in this raw recipe and also what you are doing with your search and search_results.</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/800038716578091119/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2008/12/django-fulltext-search-part-1.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/800038716578091119'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/800038716578091119'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2008/12/django-fulltext-search-part-1.html' title='Django fulltext search -- part 1'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-113854001113445218</id><published>2008-12-08T08:55:00.000-04:00</published><updated>2008-12-08T09:31:41.875-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="PLM"/><title type='text'>Blindness of closed source software company</title><content type='html'>Since yesterday I am using this blog in a slightly different way than usual. For some reasons it seems that something is happening in PLM realm.&lt;br /&gt;&lt;br /&gt;This post is again a reaction to &lt;a href=&quot;http://plmtwine.com/2008/12/08/who-will-play-role-of-google-maps-in-plm/&quot;&gt;the post &lt;/a&gt;from &lt;a href=&quot;http://www.feedburner.com/fb/a/emailverifySubmit?feedId=2726689&amp;amp;loc=en_US&quot;&gt;Oleg Shilovitsky&#39;s&lt;/a&gt;. The post was titled :&lt;br /&gt;&lt;h2&gt;&lt;a href=&quot;http://plmtwine.com/2008/12/08/who-will-play-role-of-google-maps-in-plm/&quot; rel=&quot;bookmark&quot; title=&quot;Permanent Link: Who will play role of Google Maps in PLM?&quot;&gt;Who will play role of Google Maps in PLM?&lt;/a&gt;&lt;/h2&gt;I think this is an excelent question. I am taking this opportunity to add some information on how this could happen. I have been working closely with both concept, recently I have been  working hard to developed a Geographic Information System (GIS) platform to manage Geo localized content. I will make a post related to this platform as soon as it is ready for prime time.&lt;br /&gt;Let us get back to the question, just a bit of history before google map GIS was complex, expensive, not web based software, closed source, understood and used by a very small subset of the population. Do you see any resemblance with PLM ? :-)&lt;br /&gt;Google maps brings it to the mass.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I agree with you that none of the application or technology below deliver something close to what google maps bring.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Enterprise &lt;a href=&quot;http://en.wikipedia.org/wiki/Mashup_%28web_application_hybrid%29&quot;&gt;Mashups&lt;/a&gt; (application area emerged from ability to mix data coming from different sources and present it to user.&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;a href=&quot;http://office.microsoft.com/en-us/sharepointserver/HA101054761033.aspx&quot;&gt;Microsoft Excel Services&lt;/a&gt; (high acceptance of Excel user interface make it usable everywhere)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;a href=&quot;http://www.3ds.com/products/3dlive/experience-3dlive/&quot;&gt;Dassault Systemes 3DLive&lt;/a&gt; &lt;/span&gt;(very promising lifelike user experience comes last year in new DS products).&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;  My explanation to this is that google maps was just the tip of the iceberg the cherry on the cake. The nice UI that you present to your user. But the truth is that in fact there is now a complete open source stacked available to build geo enabled mashup:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://postgis.refractions.net/&quot;&gt;postgis&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://geodjango.org/&quot;&gt;geodjango&lt;/a&gt;&lt;/li&gt;&lt;li&gt;...&lt;/li&gt;&lt;/ul&gt;This is just to named few but in reality there is plenty of component available with a vibrant community around them.&lt;br /&gt;So if PLM want to get close to that ubiquity PLM vendor will need to open the doors and the windows in order to get some fresh air, idea in. This put them in front of 2 challenges :&lt;br /&gt;  - Ability to deliver code able to compete with other open source software. In my eyes that means improve their quality and documentation&lt;br /&gt;  -  Build a business model to keep up this effort and make benefits from it.&lt;br /&gt;&lt;br /&gt;Afresco manage to do it in a different realm I am sure someone will do it the only questions left are who and when.</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/113854001113445218/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2008/12/blindness-of-closed-source-software.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/113854001113445218'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/113854001113445218'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2008/12/blindness-of-closed-source-software.html' title='Blindness of closed source software company'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-8353204013838923362</id><published>2008-12-07T19:02:00.000-04:00</published><updated>2008-12-07T22:01:06.618-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="PLM"/><title type='text'>What if SMB as a vision for PLM and PLM vendors don&#39;t undertand it.</title><content type='html'>This post has been started as a comment of a &lt;a href=&quot;http://virtualdutchman.com/2008/12/02/implementing-plm-requires-a-vision/&quot;&gt;post from the &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_0&quot;&gt;virtualducthman&lt;/span&gt;&lt;/a&gt; but since it start to be too long I have decided to write my own post. This is not a rant but rather the impression I had after I moved from a famous &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_1&quot;&gt;PLM&lt;/span&gt; Vendor to tier one automotive supplier.&lt;br /&gt;&lt;br /&gt;As always extremely well written post  without any flaw in the reasoning if you buy in the initial hypothesis. However I disagree with the statement that you use as &lt;span class=&quot;blsp-spelling-corrected&quot; id=&quot;SPELLING_ERROR_2&quot;&gt;foundation&lt;/span&gt; : Software vendor are proposing excellent product with a good ROI and &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_3&quot;&gt;SMB&lt;/span&gt; customer don&#39;t understand it because they do not have a vision.&lt;br /&gt;&lt;br /&gt;In fact as &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_4&quot;&gt;PLM&lt;/span&gt; manager for a tier one supplier in automotive I have seen quite the opposite.  I have seen all the big player coming and trying to approach me with  solutions that have all the exact same problem :&lt;br /&gt;&lt;ul&gt;&lt;li&gt;   No interoperability between authoring tools in the Engineering &lt;span class=&quot;blsp-spelling-corrected&quot; id=&quot;SPELLING_ERROR_5&quot;&gt;department&lt;/span&gt; and Plant&lt;/li&gt;&lt;li&gt;   No backward or forward compatibility between version with the tools that they are supposed to integrate with. &lt;/li&gt;&lt;li&gt;   All these tools share an interesting feature, no open &lt;span class=&quot;blsp-spelling-corrected&quot; id=&quot;SPELLING_ERROR_6&quot;&gt;standard&lt;/span&gt; to &lt;span class=&quot;blsp-spelling-corrected&quot; id=&quot;SPELLING_ERROR_7&quot;&gt;exchange&lt;/span&gt; data (&lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_8&quot;&gt;xml&lt;/span&gt;, &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_9&quot;&gt;json&lt;/span&gt;, &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_10&quot;&gt;rss&lt;/span&gt;, atom, ...)&lt;/li&gt;&lt;li&gt;   Impossible to &lt;span class=&quot;blsp-spelling-corrected&quot; id=&quot;SPELLING_ERROR_11&quot;&gt;mash up&lt;/span&gt; the information you put in their repository with other source of information. &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_12&quot;&gt;Ok&lt;/span&gt;, I agree impossible is a bit to strong you can always develop this feature and add it to your application. At least by designed these &lt;span class=&quot;blsp-spelling-corrected&quot; id=&quot;SPELLING_ERROR_13&quot;&gt;applications&lt;/span&gt; are making such thing hard.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;What if rather than saying that &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_14&quot;&gt;SMB&lt;/span&gt; do not have a vision we try to find a different explanation to your observation. I tend to believe that they have an extremely well defined vision. &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_16&quot;&gt;SMB&lt;/span&gt; know that if they want to be in the business in five years from know they need to be agile in order to transform the way they do things to adapt &lt;span class=&quot;blsp-spelling-corrected&quot; id=&quot;SPELLING_ERROR_17&quot;&gt;themselves&lt;/span&gt; to the new constraints of the &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_18&quot;&gt;OEM&lt;/span&gt;. It is interesting to note that nobody is blaming Ford, GM, ... of not being able to see that they have good chance to go bankrupt in some month from now. It is interesting that many people blame now these companies of not being able re-invent/adapt their products to their market. when all of them where using &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_19&quot;&gt;PLM&lt;/span&gt; systems and had huge &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_20&quot;&gt;PLM&lt;/span&gt; projects on going. May be this can be  an interesting post to develop this idea. Why &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_21&quot;&gt;PLM&lt;/span&gt; system only help you to develop faster/cheaper product that you are used to produce ? or Why &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_22&quot;&gt;PLM&lt;/span&gt; system can hurt your creativity in introducing innovative to your customer ?&lt;br /&gt;&lt;br /&gt;In order to develop this agility &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_23&quot;&gt;SMB&lt;/span&gt; need to put very high in the list of capabilities for their information system the following features : Agility, re-configuration, continuous evolution / transformation, &lt;span class=&quot;blsp-spelling-corrected&quot; id=&quot;SPELLING_ERROR_24&quot;&gt;openness&lt;/span&gt;, ease of integration with unknown system, overall strategy of the &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_25&quot;&gt;PLM&lt;/span&gt; vendor.&lt;br /&gt;&lt;br /&gt;That put us in front of a slightly different picture, isn&#39;t it? I am not going to develop in that post why and how each of this term could help to contribute to convince the &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_26&quot;&gt;SMB&lt;/span&gt; to change their mind on the value proposition your are doing but I am convince that the impact will be significant.  Also I hope that you understand that now known on the solution on the market give them such freedom.&lt;br /&gt;&lt;br /&gt;As an example, &lt;a href=&quot;http://www.alfresco.com/&quot;&gt;Alfresco&lt;/a&gt; has proven that this approach can work, I understand that this is not in the &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_27&quot;&gt;PLM&lt;/span&gt; realm but in content management and  web content management but this area shared a couple of similarity with &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_28&quot;&gt;PLM&lt;/span&gt;. The market was dominated by closed source vendors providing the same kind of solution for the last 15 years, with no major evolution nor integration of the new tools introduced by the development of &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_29&quot;&gt;internet&lt;/span&gt; : wiki,  Blog, chat, tag, &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_30&quot;&gt;fulltext&lt;/span&gt; search, low cost, commodity software, ...   Of course each &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_31&quot;&gt;PLM&lt;/span&gt; product have changed several times their names but unfortunately the code, concepts, people behind them are the same it was just a marketing transformation.&lt;br /&gt;&lt;br /&gt;Where is the fresh air, idea, innovation ... May be this industry need to show some code rather than &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_32&quot;&gt;powerpoint&lt;/span&gt;. It would be nice also if they change their approach in trying to force their solution rather than listening to their customer &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_33&quot;&gt;SMB&lt;/span&gt; is not &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_34&quot;&gt;OEM&lt;/span&gt;. They do not have will/money to spend in top/down project they need to be able to scale their solution with a bottom up approach. They need to be able to scale from 1 project to a &lt;span class=&quot;blsp-spelling-corrected&quot; id=&quot;SPELLING_ERROR_35&quot;&gt;department&lt;/span&gt; then a division and at the end to the whole company.&lt;br /&gt;&lt;br /&gt;Any way Mr &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_36&quot;&gt;virtualduchman&lt;/span&gt; thanks for writing your excellent posts. This post shouldn&#39;t be read as &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_37&quot;&gt;PLM&lt;/span&gt; do not bring value to &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_38&quot;&gt;SMB&lt;/span&gt; but rather like &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_39&quot;&gt;PLM&lt;/span&gt; bring value but this can be highly increased by listening to their needs and &lt;span class=&quot;blsp-spelling-error&quot; id=&quot;SPELLING_ERROR_40&quot;&gt;feedbacks&lt;/span&gt;.</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/8353204013838923362/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2008/12/what-if-smb-as-vision-for-plm-and-plm.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/8353204013838923362'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/8353204013838923362'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2008/12/what-if-smb-as-vision-for-plm-and-plm.html' title='What if SMB as a vision for PLM and PLM vendors don&#39;t undertand it.'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1452693386685860331.post-263373956842924831</id><published>2008-11-08T13:22:00.000-04:00</published><updated>2008-11-08T13:35:18.134-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Django"/><category scheme="http://www.blogger.com/atom/ns#" term="tip"/><title type='text'>How to explore dynamically your django code</title><content type='html'>I have been using the methods presented by Simon Willison on &lt;a href=&quot;http://simonwillison.net/2008/May/22/debugging/&quot;&gt;this post.&lt;/a&gt;&lt;br /&gt;For quite sometimes now and I always felt that it would be nice to have the tab completion when using the debugger inside my views or elsewhere in my django code.&lt;br /&gt;&lt;br /&gt;Here it is the magical line:&lt;br /&gt;&lt;span style=&quot;color: rgb(102, 102, 204);&quot;&gt;import ipdb; ipdb.set_trace()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Note&lt;/span&gt; : That &quot;&lt;span style=&quot;font-weight: bold;&quot;&gt;pdb&lt;/span&gt;&quot; as been replace by &quot;&lt;span style=&quot;font-weight: bold;&quot;&gt;ipdb&lt;/span&gt;&quot; which is the IPython debugger.&lt;br /&gt;&lt;br /&gt;You then get all the nice stuff from IPython (tab completion, nice tracebacks) right in pdb. In order to install it you can for example do : easy_python ipdb</content><link rel='replies' type='application/atom+xml' href='http://yml-blog.blogspot.com/feeds/263373956842924831/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yml-blog.blogspot.com/2008/11/how-to-explore-dynamically-your-django.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/263373956842924831'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1452693386685860331/posts/default/263373956842924831'/><link rel='alternate' type='text/html' href='http://yml-blog.blogspot.com/2008/11/how-to-explore-dynamically-your-django.html' title='How to explore dynamically your django code'/><author><name>yml</name><uri>http://www.blogger.com/profile/08053342830710912492</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>