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

<channel>
	<title>Francisco Souza &#187; django</title>
	<atom:link href="http://f.souza.cc/tag/django/feed/" rel="self" type="application/rss+xml" />
	<link>http://f.souza.cc</link>
	<description></description>
	<lastBuildDate>Sun, 05 Feb 2012 01:28:05 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Setting up a Django production environment: compiling and configuring nginx</title>
		<link>http://f.souza.cc/2011/11/setting-up-a-django-production-environment-compiling-and-configuring-nginx/</link>
		<comments>http://f.souza.cc/2011/11/setting-up-a-django-production-environment-compiling-and-configuring-nginx/#comments</comments>
		<pubDate>Sun, 06 Nov 2011 02:15:04 +0000</pubDate>
		<dc:creator>Francisco Souza</dc:creator>
				<category><![CDATA[web development]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[fabric]]></category>
		<category><![CDATA[gunicorn]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[virtualenv]]></category>

		<guid isPermaLink="false">http://f.souza.cc/?p=622</guid>
		<description><![CDATA[Here is another series of posts: now I’m going to write about setting up a Django production environment using nginx and Green Unicorn in a virtual environment. The subject in this first post is nginx, which is my favorite web server. <a href="http://f.souza.cc/2011/11/setting-up-a-django-production-environment-compiling-and-configuring-nginx/"><em>Continue&#160;reading&#160;<span class="meta-nav">&#8594;</span></em></a>]]></description>
			<content:encoded><![CDATA[<p><img src="http://c.souza.cc/s/2011/11/nginx-logo.png" alt="nginx" title="nginx" width="295" height="92" class="alignright size-full wp-image-628" />Here is another series of posts: now I&#8217;m going to write about setting up a <a href="http://djangoproject.com" title="Django" target="_blank" rel="nofollow">Django</a> production environment using <a href="http://nginx.org" title="nginx" target="_blank" rel="nofollow">nginx</a> and <a href="http://gunicorn.org/" title="gunicorn" target="_blank" rel="nofollow">Green Unicorn</a> in a <a href="http://virtualenv.org" title="virtualenv" target="_blank" rel="nofollow">virtual environment</a>. The subject in this first post is nginx, which is my favorite web server.</p>
<p>This post explains how to install nginx from sources, compiling it (on Linux). You might want to use <em>apt</em>, <em>zif</em>, <em>yum</em> or <em>ports</em>, but I prefer building from sources. So, to build from sources, make sure you have all development dependencies (C headers, including the PCRE library headers, nginx rewrite module uses it). If you want to build nginx with SSL support, keep in mind that you will need the libssl headers too.<span id="more-622"></span></p>
<p>Build nginx from source is a straightforward process: all you need to do is download it from the official site and build with some simple options. In our setup, we&#8217;re going to install nginx under <code class="codecolorer text default"><span class="text">/opt/nginx</span></code>, and use it with the <code class="codecolorer text default"><span class="text">nginx</span></code> system user. So, let&#8217;s download and extract the latest stable version (1.0.9) from nginx website:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ curl -O http://nginx.org/download/nginx-1.0.9.tar.gz<br />
$ tar -xzf nginx-1.0.9.tar.gz</div></div>
<p>Once you have extracted it, just configure, compile and install:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ ./configure --prefix=/opt/nginx --user=nginx --group=nginx<br />
$ make<br />
# make install</div></div>
<p>As you can see, we provided the <em>/opt/nginx</em> to configure, make sure the /opt directory exists. Also, make sure that there is a user and a group called <em>nginx</em>, if they don&#8217;t exist, add them:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ [sudo] adduser --system --no-create-home --disabled-login --disabled-password --group nginx</div></div>
<p>After that, you can start nginx using the command line above:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ [sudo] /opt/nginx/sbin/nginx</div></div>
<blockquote><p>Linode provides an <a href="http://library.linode.com/assets/634-init-deb.sh" title="Linode init script" target="_blank" rel="nofollow">init script</a> that uses <a href="http://man.he.net/man8/start-stop-daemon" title="start-stop-daemon man page entry" target="_blank" rel="nofollow">start-stop-daemon</a>, you might want to use it.</p></blockquote>
<h3>nginx configuration</h3>
<p>nginx comes with a default <code class="codecolorer text default"><span class="text">nginx.conf</span></code> file, let&#8217;s change it to reflect the following configuration requirements:</p>
<ul>
<li>nginx should start workers with the <code class="codecolorer text default"><span class="text">nginx</span></code> user</li>
<li>nginx should have two worker processes</li>
<li>the PID should be stored in the <em>/opt/nginx/log/nginx.pid</em> file</li>
<li>nginx must have an access log in <em>/opt/nginx/logs/access.log</em></li>
<li>the configuration for the Django project we&#8217;re going to develop should be versioned with the entire code, so it must be <em>included</em> in the nginx.conf file (assume that the <em>library</em> project is in the directory <em>/opt/projects</em>).</li>
</ul>
<p>So here is the <code class="codecolorer text default"><span class="text">nginx.conf</span></code> for the requirements above:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">user &nbsp;nginx;<br />
worker_processes &nbsp;2;<br />
<br />
pid logs/nginx.pid;<br />
<br />
events {<br />
&nbsp; &nbsp; worker_connections &nbsp;1024;<br />
}<br />
<br />
http {<br />
&nbsp; &nbsp; include &nbsp; &nbsp; &nbsp; mime.types;<br />
&nbsp; &nbsp; default_type &nbsp;application/octet-stream;<br />
<br />
&nbsp; &nbsp; log_format &nbsp;main &nbsp;'$remote_addr - $remote_user [$time_local] &quot;$request&quot; '<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;'$status $body_bytes_sent &quot;$http_referer&quot; '<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;'&quot;$http_user_agent&quot; &quot;$http_x_forwarded_for&quot;';<br />
<br />
&nbsp; &nbsp; access_log &nbsp;logs/access.log &nbsp;main;<br />
<br />
&nbsp; &nbsp; sendfile &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; on;<br />
&nbsp; &nbsp; keepalive_timeout &nbsp;65;<br />
<br />
&nbsp; &nbsp; include /opt/projects/showcase/nginx.conf;<br />
}</div></div>
<p>Now we just need to write the configuration for our Django project. I&#8217;m using an old sample project written while I was working at Giran: the name is <em>lojas giranianas</em>, a nonsense portuguese joke with a famous brazilian store. It&#8217;s an unfinished showcase of products, it&#8217;s like an e-commerce project, but it can&#8217;t sell, so it&#8217;s just a product catalog. The code is available at <a href="https://github.com/fsouza/fast-track-django" title="lojas giranianas" target="_blank" rel="nofollow">Github</a>. The <em>nginx.conf</em> file for the repository is here:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">server {<br />
&nbsp; &nbsp; listen 80;<br />
&nbsp; &nbsp; server_name localhost;<br />
<br />
&nbsp; &nbsp; charset utf-8;<br />
<br />
&nbsp; &nbsp; location / {<br />
&nbsp; &nbsp; &nbsp; &nbsp; proxy_set_header &nbsp; &nbsp;X-Real-IP &nbsp; $remote_addr;<br />
&nbsp; &nbsp; &nbsp; &nbsp; proxy_set_header &nbsp; &nbsp;Host &nbsp; &nbsp; &nbsp; &nbsp;$http_host;<br />
&nbsp; &nbsp; &nbsp; &nbsp; proxy_set_header &nbsp; &nbsp;X-Forwarded-For $proxy_add_x_forwarded_for;<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; proxy_pass http://localhost:8000;<br />
&nbsp; &nbsp; }<br />
<br />
&nbsp; &nbsp; location /static {<br />
&nbsp; &nbsp; &nbsp; &nbsp; root /opt/projects/showcase/;<br />
&nbsp; &nbsp; &nbsp; &nbsp; expires 1d;<br />
&nbsp; &nbsp; }<br />
}</div></div>
<p>The server listens on port 80, responds for the <em>localhost</em> hostname (<a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html" title="HTTP 1.1 headers" target="_blank">read more about the Host header</a>). The <code class="codecolorer text default"><span class="text">location /static</span></code> directive says that nginx will serve the static files of the project. It also includes an <code class="codecolorer text default"><span class="text">expires</span></code> directive for caching control. The <code class="codecolorer text default"><span class="text">location /</span></code> directive makes a <a href="http://wiki.nginx.org/HttpProxyModule#proxy_pass" title="nginx proxy_pass" target="_blank">proxy_pass</a>, forwarding all requisitions to an upstream server listening on port 8000, this server is the subject of the next post of the series: the Green Unicorn (gunicorn) server.</p>
<p>Not only the HTTP request itself is forwarded to the gunicorn server, but also some headers, that helps to properly deal with the request:</p>
<ul>
<li><strong>X-Real-IP</strong>: forwards the remote address to the upstream server, so it can know the real IP of the user. When nginx forwards the request to gunicorn, without this header, all gunicorn will know is that there is a request coming from localhost (or wherever the nginx server is), the remote address is always the IP address of the machine where nginx is running (who actually make the request to gunicorn)</li>
<li><strong>Host</strong>: the <em>Host</em> header is forwarded so gunicorn can treat different requests for different hosts. Without this header, it will be impossible to Gunicorn to have these constraints</li>
<li><strong>X-Forwarded-For</strong>: also known as XFF, this header provide more precise information about the real IP who makes the request. Imagine there are 10 proxies between the user machine and your webserver, the XFF header will all these proxies comma separated. In order to not turn a proxy into an anonymizer, it&#8217;s a good practice to always forward this header.</li>
</ul>
<p>So that is it, in the next post we&#8217;re going to install and run gunicorn. In other posts, we&#8217;ll see how to make automated deploys using <a href="http://fabfile.org" title="Fabric" target="_blank">Fabric</a>, and some tricks on caching (using the <a href="http://wiki.nginx.org/HttpProxyModule#proxy_cache" title="nginx proxy_cache" target="_blank" rel="nofollow">proxy_cache</a> directive and integrating <a href="http://f.souza.cc/tag/django" title="Posts about Django">Django</a>, nginx and <a href="http://f.souza.cc/tag/memcached" title="Posts about memcached">memcached</a>).</p>
<p>See you in the next posts :)</p>
]]></content:encoded>
			<wfw:commentRss>http://f.souza.cc/2011/11/setting-up-a-django-production-environment-compiling-and-configuring-nginx/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Don&#8217;t rely on caching</title>
		<link>http://f.souza.cc/2011/10/dont-rely-on-caching/</link>
		<comments>http://f.souza.cc/2011/10/dont-rely-on-caching/#comments</comments>
		<pubDate>Thu, 06 Oct 2011 23:29:31 +0000</pubDate>
		<dc:creator>Francisco Souza</dc:creator>
				<category><![CDATA[software development]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[memcached]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://f.souza.cc/?p=586</guid>
		<description><![CDATA[Don’t rely on cache. Cache doesn’t exist to make applications work, it exists to make them faster and/or more scalable. <a href="http://f.souza.cc/2011/10/dont-rely-on-caching/"><em>Continue&#160;reading&#160;<span class="meta-nav">&#8594;</span></em></a>]]></description>
			<content:encoded><![CDATA[<p>Extracted from <a href="http://f.souza.cc/tag/django" title="Posts about Django">Django</a> docs: <em>&#8220;memory isn&#8217;t intended for permanent data storage, so don&#8217;t rely on memory-based caching as your only data storage. Without a doubt, none of the Django caching backends should be used for permanent storage&#8221;</em>.</p>
<p>Here is the lesson: don&#8217;t rely on caching. Cache doesn&#8217;t exist to make applications work, it exists to make them faster and/or more scalable. Use a cache system as permanent storage is such a bad idea, except when you&#8217;re not using it for caching (what is not the case here). Cache is a shortcut for a path that might be painful to follow, but you still should not rely on cache. You must be able to traverse the path without the cache.</p>
]]></content:encoded>
			<wfw:commentRss>http://f.souza.cc/2011/10/dont-rely-on-caching/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Speaking at PythonBrasil[7]</title>
		<link>http://f.souza.cc/2011/09/speaking-at-pythonbrasil7/</link>
		<comments>http://f.souza.cc/2011/09/speaking-at-pythonbrasil7/#comments</comments>
		<pubDate>Sat, 24 Sep 2011 03:43:01 +0000</pubDate>
		<dc:creator>Francisco Souza</dc:creator>
				<category><![CDATA[conferences]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[scalability]]></category>
		<category><![CDATA[talks]]></category>

		<guid isPermaLink="false">http://f.souza.cc/?p=568</guid>
		<description><![CDATA[Next weekend I’ll be talking about scaling Django applications at Python Brasil, the brazilian Python conference. It will be my first time at the conference, which is one of the greatest Python conferences in Latin America. Some international dudes are also attending to the conference. <a href="http://f.souza.cc/2011/09/speaking-at-pythonbrasil7/"><em>Continue&#160;reading&#160;<span class="meta-nav">&#8594;</span></em></a>]]></description>
			<content:encoded><![CDATA[<p><img src="http://c.souza.cc/s/2011/09/python-brasil.png" alt="PythonBrasil[7]" title="PythonBrasil[7]" width="179" height="150" class="alignleft size-full wp-image-572" />Next weekend I&#8217;ll be talking about scaling Django applications at <a href="http://www.pythonbrasil.org.br" title="PythonBrasil[7]" target="_blank" rel="nofollow">Python Brasil</a>, the brazilian Python conference. It will be my first time at the conference, which is one of the greatest <a href="http://f.souza.cc/tag/python" title="Posts about Python">Python</a> conferences in Latin America.</p>
<p>Some international dudes are also attending to the conference: <a href="http://cyberwebconsulting.com/" title="Wesley Chun" target="_blank" rel="nofollow">Wesley Chun</a> is going to talk about Python 3 and Google App Engine; <a href="http://www.enfoldsystems.com/" title="Alan Runyan" target="_blank" rel="nofollow">Alan Runyan</a> will talk about free and open source software, and <a href="http://holdenweb.com/" title="Steve Holden" target="_blank" rel="nofollow">Steve Holden</a> will be talking about the issues involved in trying to build a global Python user group.<span id="more-568"></span></p>
<p>There is also <a href="https://twitter.com/#!/fijall" title="Maciej Fijalkowski" target="_blank">Maciej Fijalkowski</a>, PyPy core developer, talking about little things PyPy makes possible.</p>
<p>As I pointed before, I&#8217;m going to talk about scalability, based in some experiences aquired scaling Django applications at <a href="http://globo.com" title="Globo.com" target="_blank" rel="nofollow">Globo.com</a>, like <a href="http://g1.globo.com" title="G1" target="_blank" rel="nofollow">G1</a>, the greatest news portal in the Latin America. </p>
]]></content:encoded>
			<wfw:commentRss>http://f.souza.cc/2011/09/speaking-at-pythonbrasil7/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Splinter sprint on FISL</title>
		<link>http://f.souza.cc/2011/06/splinter-sprint-on-fisl/</link>
		<comments>http://f.souza.cc/2011/06/splinter-sprint-on-fisl/#comments</comments>
		<pubDate>Tue, 28 Jun 2011 16:53:39 +0000</pubDate>
		<dc:creator>Francisco Souza</dc:creator>
				<category><![CDATA[software development]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[splinter]]></category>
		<category><![CDATA[sprints]]></category>

		<guid isPermaLink="false">http://www.franciscosouza.com/?p=465</guid>
		<description><![CDATA[We are going to start tomorrow, on FISL, another splinter sprint. "From June 29 through July 2, 2011, fisl12 will be hosted at the PUC Events Center, in Porto Alegre, Rio Grande do Sul, Brazil.". But don't worry about the location: anyone in anywhere can join us in this sprint. There is an entry in splinter wiki about this sprint, and I'm just replicating the information here... <a href="http://f.souza.cc/2011/06/splinter-sprint-on-fisl/"><em>Continue&#160;reading&#160;<span class="meta-nav">&#8594;</span></em></a>]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft" title="FISL" src="http://c.souza.cc/s/2011/06/fisl-logo1.jpg" alt="FISL" width="200" height="199" />We are going to start tomorrow, on <a title="FISL" href="http://fisl.softwarelivre.org" target="_blank">FISL</a>, another <a title="Splinter" href="http://splinter.cobrateam.info" target="_blank">splinter</a> sprint. <em>&#8220;From June 29 through July 2, 2011, fisl12 will be hosted at the PUC Events Center, in Porto Alegre, Rio Grande do Sul, Brazil&#8221; </em>(copied from FISL website :P). But don&#8217;t worry about the location: anyone in anywhere can join us in this sprint. There is an <a title="splinter sprint on FISL" href="https://github.com/cobrateam/splinter/wiki/sprint29jun2011" target="_blank">entry</a> in splinter wiki about this sprint, and I&#8217;m just replicating the information here&#8230;</p>
<p><span id="more-465"></span></p>
<h3>What is a splinter sprint?</h3>
<p>Basically, a splinter sprint is an excuse for people to focus their undivided attention, for a set time frame, on improving splinter. It&#8217;s a focused, scheduled effort to fix bugs, add new features and improve documentation.</p>
<p>Anybody, anywhere around the world, can participate and contribute. If you&#8217;ve never contributed to splinter before, this is the perfect chance for you to chip in.</p>
<h3>How to contribute</h3>
<ol>
<li>Choose an <a title="splinter issues" href="https://github.com/cobrateam/splinter/issues" target="_blank">issue</a></li>
<li>Create a fork</li>
<li>Send a pull request</li>
</ol>
<p><em>Remember: all new features should be well tested and documented. An issue can&#8217;t be closed if there isn&#8217;t docs for the solution code.</em></p>
<h3>Preparing for the sprint</h3>
<p>Get a <a title="IRC" href="http://en.wikipedia.org/wiki/Internet_Relay_Chat" target="_blank">IRC</a> client, so that you can join us in the channel <a title="cobrateam" href="http://cobrateam.info" target="_blank">#cobrateam</a> on Freenode.</p>
<p>See all you there :)</p>
]]></content:encoded>
			<wfw:commentRss>http://f.souza.cc/2011/06/splinter-sprint-on-fisl/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Splinter: Python tool for acceptance tests on web applications</title>
		<link>http://f.souza.cc/2011/05/splinter-python-tool-for-acceptance-tests-on-web-applications/</link>
		<comments>http://f.souza.cc/2011/05/splinter-python-tool-for-acceptance-tests-on-web-applications/#comments</comments>
		<pubDate>Sat, 14 May 2011 19:04:39 +0000</pubDate>
		<dc:creator>Francisco Souza</dc:creator>
				<category><![CDATA[software development]]></category>
		<category><![CDATA[acceptance tests]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[selenium]]></category>
		<category><![CDATA[splinter]]></category>
		<category><![CDATA[web development]]></category>

		<guid isPermaLink="false">http://www.franciscosouza.com/?p=239</guid>
		<description><![CDATA[Know Splinter, a Python framework for acceptance testing on web applications. <a href="http://f.souza.cc/2011/05/splinter-python-tool-for-acceptance-tests-on-web-applications/"><em>Continue&#160;reading&#160;<span class="meta-nav">&#8594;</span></em></a>]]></description>
			<content:encoded><![CDATA[<p><a title="Capybara" rel="nofollow" href="https://github.com/jnicklas/capybara" target="_blank">Capybara</a> and <a title="Webrat" rel="nofollow" href="https://github.com/brynary/webrat" target="_blank">Webrat</a> are great Ruby tools for acceptance tests. A few months ago, we started a great tool for acceptance tests<img class="alignright size-full wp-image-275" title="Splinter" src="http://c.souza.cc/s/2011/05/splinter1.jpg" alt="Splinter" width="210" height="171" /> on <a title="Posts about Python" href="http://www.franciscosouza.com/tag/python" target="_self">Python</a> web applications, called <a title="Splinter" href="http://splinter.cobrateam.info" target="_blank">Splinter</a>. There are many acceptance test tools on Python world: <a title="Selenium" rel="nofollow" href="http://seleniumhq.org" target="_blank">Selenium</a>, <a title="Alfajor" rel="nofollow" href="https://github.com/idealistdev/alfajor" target="_blank">Alfajor</a>, <a title="Windmill" rel="nofollow" href="http://www.getwindmill.com/" target="_blank">Windmill</a>, <a title="Mechanize" rel="nofollow" href="http://wwwsearch.sourceforge.net/mechanize/" target="_blank">Mechanize</a>, <a title="zope.testbrowser" rel="nofollow" href="http://pypi.python.org/pypi/zope.testbrowser" target="_blank">zope.testbrowser</a>, etc. Splinter was not created to be another acceptance tool, but an abstract layer over other tools, its goal is provide a unique API that make acceptance testing easier and funnier :)</p>
<p>In this post, I will show some basic usage of Splinter for simple web application tests. <a title="Splinter" rel="nofollow" href="http://github.com/cobrateam/splinter" target="_blank">Splinter</a> is a tool useful on tests of any web application. You can even test a Java web application using Splinter. This post example is a &#8220;test&#8221; of a Facebook feature, just because I want to focus on how to use Splinter, not on how to write a web application. The feature to be tested is the creation of an event (the Splinter sprint), following all the flow: first the user will login on Facebook, then click on &#8220;Events&#8221; menu item, then click on &#8220;Create an Event button&#8221;, enter all event informations and click on &#8220;Create event&#8221; button. So, let&#8217;s do it&#8230;<span id="more-239"></span></p>
<p>First step is create a <a title="Browser class" rel="nofollow" href="https://github.com/cobrateam/splinter/blob/master/splinter/browser.py#L18" target="_blank">Browser</a> instance, which will provide method for interactions with browser (where the browser is: Firefox, Chrome, etc.). The code we need for it is very simple:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">browser <span style="color: #66cc66;">=</span> Browser<span style="color: black;">&#40;</span><span style="color: #483d8b;">'webdriver.firefox'</span><span style="color: black;">&#41;</span></div></div>
<p>Browser is a class and its constructor receives the driver to be used with that instance. Nowadays, there are three drivers for Splinter: firefox.webdriver, chrome.webdriver and zope.testbrowser. We are using Firefox, and you can easily use Chrome by simply changing the driver from <em>webdriver.firefox</em> to <em>webdriver.chrome</em>. It&#8217;s also very simple to add another driver to Splinter, and I plan to cover how to do that on another blog post here.</p>
<p>A new browser session is started when we got the <em>browser</em> object, and this is the object used for Firefox interactions. Let&#8217;s start a new event on Facebook, the <em>Splinter Sprint</em>. First of all, we need to <em>visit</em> the Facebook homepage. There is a <em>visit</em> method on Browser class, so we can use it:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">browser.<span style="color: black;">visit</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;http://www.facebook.com&quot;</span><span style="color: black;">&#41;</span></div></div>
<p><em>visit</em> method is blocking: it waits for page to load, then we can navigate, click on links, fill forms, etc. Now we have Facebook homepage opened on browser, and you probably know that we need to login on Facebook page, but what if we are already logged in? So, let&#8217;s create a method that login on Facebook with provided authentication data only the user is not logged in (imagine we are on a TestCase class):</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">def</span> do_login_if_need<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> username<span style="color: #66cc66;">,</span> password<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">self</span>.<span style="color: black;">browser</span>.<span style="color: black;">is_element_present_by_css</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'div.menu_login_container'</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">self</span>.<span style="color: black;">browser</span>.<span style="color: black;">fill</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'email'</span><span style="color: #66cc66;">,</span> username<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">self</span>.<span style="color: black;">browser</span>.<span style="color: black;">fill</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'pass'</span><span style="color: #66cc66;">,</span> password<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">self</span>.<span style="color: black;">browser</span>.<span style="color: black;">find_by_css</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'div.menu_login_container input[type=&quot;submit&quot;]'</span><span style="color: black;">&#41;</span>.<span style="color: black;">first</span>.<span style="color: black;">click</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">assert</span> <span style="color: #008000;">self</span>.<span style="color: black;">browser</span>.<span style="color: black;">is_element_present_by_css</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'li#navAccount'</span><span style="color: black;">&#41;</span></div></div>
<p>What was made here? First of all, the method checks if there is an element present on the page, using a CSS selector. It checks for the div that contains the <em>username</em> and <em>password</em> fields. If that div is present, we tell the browser object to fill those fields, then find the <em>submit</em> button and click on it. The last line is an <em>assert</em> to guarantee that the login was successful and the current page is the Facebook homepage (by checking the presence of &#8220;Account&#8221; li).</p>
<p>We could also find elements by its texts, labels or whatever appears on screen, but remember: Facebook is an internationalized web application, and we can&#8217;t test it using only a specific language.</p>
<p>Okay, now we know how to visit a webpage, check if an element is present, fill a form and click on a button. We&#8217;re also logged in on Facebook and can finally go ahead create the <em>Splinter sprint</em> event. So, here is the event creation flow, for a user:</p>
<ol>
<li>On Facebook homepage, click on &#8220;Events&#8221; link, of left menu</li>
<li>The &#8220;Events&#8221; page will load, so click on &#8220;Create an Event&#8221; button</li>
<li>The user see a page with a form to create an event</li>
<li>Fill the date and chose the time</li>
<li>Define what is the name of the event, where it will happen and write a short description for it</li>
<li>Invite some guests</li>
<li>Upload a picture for the event</li>
<li>Click on &#8220;Create Event&#8221; button</li>
</ol>
<p>We are going to do all these steps, except the 6th, because the Splinter Sprint will just be a public event and we don&#8217;t need to invite anybody :) There are some boring AJAX requests on Facebook that we need to deal, so there is not only Splinter code for those steps above. First step is click on &#8220;Events&#8221; link. All we need to do is <em>find</em> the link and <em>click</em> on it:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">browser.<span style="color: black;">find_by_css</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'li#navItem_events a'</span><span style="color: black;">&#41;</span>.<span style="color: black;">first</span>.<span style="color: black;">click</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<p>The <em>find_by_css</em> method takes a CSS selector and returns a ElementList. So, we get the first element of the list (even when the selector returns only an element, the return type is still a <em>list</em>) and click on it. Like <em>visit</em> method, clicking in a link is a blocking action: the driver will only listen for new actions when the request is finished (the page is loaded).</p>
<p>We&#8217;re finally on &#8220;new event&#8221; page, and there is a form on screen waiting for data of the <em>Splinter Sprint</em>. Let&#8217;s fill the form. Here is the code for it:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">browser.<span style="color: black;">fill</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'event_startIntlDisplay'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'5/21/2011'</span><span style="color: black;">&#41;</span><br />
browser.<span style="color: #dc143c;">select</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'start_time_min'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'480'</span><span style="color: black;">&#41;</span><br />
browser.<span style="color: black;">fill</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'name'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'Splinter sprint'</span><span style="color: black;">&#41;</span><br />
browser.<span style="color: black;">fill</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'location'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'Rio de Janeiro, Brazil'</span><span style="color: black;">&#41;</span><br />
browser.<span style="color: black;">fill</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'desc'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'For more info, check out the #cobratem channel on freenode!'</span><span style="color: black;">&#41;</span></div></div>
<p>That is it: the event is going to happen on May 21th 2011, at 8:00 in the morning (480 minutes). As we know, the event name is <em>Splinter sprint</em>, and we are going to join some guys down here in Brazil. We filled out the form using <em>fill</em> and <em>select</em> methods.</p>
<p>The <em>fill</em> method is used to fill a field (a textarea, an input, or whatever you can fill). It receives two strings: the first is the name of the field to fill and the second is the value that will fill the field. The <em>select</em> method is used to select an option in a select element (a &#8220;combo box&#8221;). It also receives two string parameters: the first is the <em>name</em> of the select element, and the second is the <em>value</em> of the option being selected.</p>
<p>Imagine you have the following select element:</p>
<div class="codecolorer-container html4strict default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="html4strict codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">select</span> <span style="color: #000066;">name</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;gender&quot;</span>&gt;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">option</span> <span style="color: #000066;">value</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;m&quot;</span>&gt;</span>Male<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">option</span>&gt;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">option</span> <span style="color: #000066;">value</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;f&quot;</span>&gt;</span>Female<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">option</span>&gt;</span><br />
<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">select</span>&gt;</span></div></div>
<p>To select &#8220;Male&#8221;, you would call the <em>select</em> method this way:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">browser.<span style="color: #dc143c;">select</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;gender&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;m&quot;</span><span style="color: black;">&#41;</span></div></div>
<p>The last action before click on &#8220;Create Event&#8221; button is upload a picture for the event. On new event page, Facebook loads the file field for picture uploading inside a <em>iframe</em>, so we need to switch to this frame and interact with the form present inside the frame. To show the frame, we need to click on &#8220;Add Event Photo&#8221; button and then switch to it, we already know how click on a link:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">browser.<span style="color: black;">find_by_css</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'div.eventEditUpload a.uiButton'</span><span style="color: black;">&#41;</span>.<span style="color: black;">first</span>.<span style="color: black;">click</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<p>When we click this link, Facebook makes a asynchronous request, which means the driver doesn&#8217;t stay blocked waiting the request ends, so if we try to interact with the frame BEFORE it appears, we will get a <em>ElementDoesNotExist</em> exception. Splinter provides a <em>is_element_present</em> method that receives an argument called <em>wait_time</em>, which is the time Splinter will be waiting for the element to appear on the screen. If the element doesn&#8217;t appear on screen, we can&#8217;t go on, so we can assume the test failed (remember we are testing a Facebook feature):</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> browser.<span style="color: black;">is_element_present_by_css</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'iframe#upload_pic_frame'</span><span style="color: #66cc66;">,</span> wait_time<span style="color: #66cc66;">=</span><span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; fail<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;The upload pic iframe did'n't appear :(&quot;</span><span style="color: black;">&#41;</span></div></div>
<p>The <em>is_element_present_by_css</em> method takes a CSS selector and tries to find an element using it. It also receives a <em>wait_time</em> parameter that indicates a timeout for the search of the element. So, if the <em>iframe</em> element with <em>ID=&#8221;upload_pic_frame&#8221;</em> is not present or doesn&#8217;t appears on screen in 10 seconds, the method returns <em>False</em>, otherwise it returns <em>True</em>.   </p>
<blockquote><p><strong>Important:</strong> The <em>fail</em> method is a pseudocode sample and doesn&#8217;t exist (if you&#8217;re using <em>unittest</em> library, you can invoke <em>self.fail</em> in a TestCase, exactly what I did in <a href="https://github.com/cobrateam/splinter/blob/master/samples/test_facebook_events.py" rel="nofollow" target="_blank" title="Snippet for creating a new event on Facebook using Splinter">complete snippet for this example</a>, available at Github).</p></blockquote>
<p>Now we see the <em>iframe</em> element on screen and we can finally upload the picture. Imagine we have a variable that contains the path of the picture (and not a file object, <em>StringIO</em>, or something like this), and this variable name is <em>picture_path</em>, this is the code we need:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">with</span> browser.<span style="color: black;">get_iframe</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'upload_pic_frame'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">as</span> frame:<br />
&nbsp; &nbsp; frame.<span style="color: black;">attach_file</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'pic'</span><span style="color: #66cc66;">,</span> picture_path<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #dc143c;">time</span>.<span style="color: black;">sleep</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span></div></div>
<p>Splinter provides the <em>get_iframe</em> method that change the context and returns another objet to interact  with the content of the frame. So we call the <em>attach_file</em> method, who also receives two strings: the first is the <em>name</em> of the input element and the second is the absolute path of the file being sent. Facebook also uploads the picture asynchronously, but there&#8217;s no way to wait some element to appear on screen, so I just put Python to sleep 10 seconds on last line.</p>
<p>After follow all these steps, we can finally click on &#8220;Create Event&#8221; button and asserts that Facebook created it:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">browser.<span style="color: black;">find_by_css</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'label.uiButton input[type=&quot;submit&quot;]'</span><span style="color: black;">&#41;</span>.<span style="color: black;">first</span>.<span style="color: black;">click</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
title <span style="color: #66cc66;">=</span> browser.<span style="color: black;">find_by_css</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'h1 span'</span><span style="color: black;">&#41;</span>.<span style="color: black;">first</span>.<span style="color: black;">text</span><br />
<span style="color: #ff7700;font-weight:bold;">assert</span> title <span style="color: #66cc66;">==</span> <span style="color: #483d8b;">'Splinter sprint'</span></div></div>
<p>After create an event, Facebook redirects the browser to the event page, so we can check if it really happened by asserting the header of the page. That&#8217;s what the code above does: in new event page, it click on submit button, and after the redirect, get the text of a <em>span</em> element and asserts that this text equals to <em>&#8220;Splinter sprint&#8221;</em>.</p>
<p>That is it! This post was an overview on Splinter API. Check out the <a href="https://github.com/cobrateam/splinter/blob/master/samples/test_facebook_events.py" rel="nofollow" title="Testing the creation of an event on Facebook" target="_blank">complete snippet</a>, written as a test case and also check out <a href="https://github.com/cobrateam/splinter/" rel="nofollow" target="_blank" title="splinter at github">Splinter repository at Github</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://f.souza.cc/2011/05/splinter-python-tool-for-acceptance-tests-on-web-applications/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Flying with Django on Google App Engine</title>
		<link>http://f.souza.cc/2010/08/flying-with-django-on-google-app-engine/</link>
		<comments>http://f.souza.cc/2010/08/flying-with-django-on-google-app-engine/#comments</comments>
		<pubDate>Mon, 02 Aug 2010 19:52:00 +0000</pubDate>
		<dc:creator>Francisco Souza</dc:creator>
				<category><![CDATA[software development]]></category>
		<category><![CDATA[cloud computing]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[google app engine]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[web development]]></category>

		<guid isPermaLink="false">http://www.franciscosouza.com/?p=12</guid>
		<description><![CDATA[Google App Engine is a powerful tool for web developers. I am sure that it is useful and every developer should taste it =) Python was the first programming language supported by App Engine, and is a programming language with a lot of web frameworks. So, you can use some of these frameworks on Google App Engine. In a series of three blog posts, I will show how to use three Python web frameworks on App Engine: Django, Flask and web2py (not necessarily in this sequence). <a href="http://f.souza.cc/2010/08/flying-with-django-on-google-app-engine/"><em>Continue&#160;reading&#160;<span class="meta-nav">&#8594;</span></em></a>]]></description>
			<content:encoded><![CDATA[<div class="separator" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em; text-align: center;"><img src="http://c.souza.cc/s/2010/08/gae_logo.png" alt="Google App Engine" title="Google App Engine" width="142" height="109" class="alignnone size-full wp-image-103" /></div>
<p><a title="Google App Engine" rel="nofollow" href="http://appengine.google.com/" target="_blank">Google App Engine</a> is a powerful tool for web developers. I am sure that it is useful and every developer should taste it =) <a title="Python programming language" rel="nofollow" href="http://www.python.org/" target="_blank">Python</a> was the first programming language supported by App Engine, and is a programming language with a lot of web frameworks. So, you can use some of these frameworks on Google App Engine. In a series of three blog posts, I will show how to use three Python web frameworks on App Engine: <a title="Django" rel="nofollow" href="http://www.djangoproject.com/" target="_blank">Django</a>, <a title="Flask: a Python microframework" rel="nofollow" href="http://flask.pocoo.org/" target="_blank">Flask</a> and <a title="web2py" rel="nofollow" href="http://www.web2py.com/" target="_blank">web2py</a> (not necessarily in this sequence).<span id="more-12"></span></p>
<p>The first framework is Django, the most famous of all Python frameworks and maybe is used the most.</p>
<p>Django models is the strongest Django feature. It is a high level database abstraction layer with a powerful object-relational mapper, it supports a lot of relational database management systems, but App Engine doesn&#8217;t use a relational database. The database behind App Engine is called BigTable, which is a distributed storage system for managing structured data, designed to scale to a very large size (Reference: <a title="Bigtable" rel="nofollow" href="http://labs.google.com/papers/bigtable.html" target="_blank">Bigtable: A Distributed Storage System for Structured Data</a>). It is not based on schemas, tables, keys or columns, it is like a big map indexed by a row key, column key and a timestamp. We can not use native version of Django models with Bigtable, because the Django models framework was not designed for non relational databases.</p>
<p>So, what can we do? There is a Django fork, the <a title="django-nonrel project" rel="nofollow" href="http://www.allbuttonspressed.com/projects/django-nonrel" target="_blank">django-nonrel project</a>, which aims to bring the power of the Django model layer to non-relational databases. I will use the <a title="djangoappengine project" rel="nofollow" href="http://www.allbuttonspressed.com/projects/djangoappengine" target="_blank">djangoappengine</a> sub-project to build the sample application of this post, that will be deployed on Google App Engine :)</p>
<p>The sample application is the default: a blog. A very simple blog, with only a form protected by login (using Django built-in authentication system instead of Google Accounts API) and a public page listing all blog posts. It is very easy and simple to do, so let&#8217;s do it.</p>
<p>First, we have to setup our environment. According the <a href="http://www.allbuttonspressed.com/projects/djangoappengine#installation">djangoappengine project documentation</a>, we need to download 4 zip files and put it together. First, I downloaded the django-testapp file, extract its contents and renamed the project directory from <em>django-testapp </em>to <em>blog_gae</em>. After this step, I downloaded the other files and put it inside the <em>blog_gae </em>directory. Here is the final project structure:</p>
<div class="separator" style="clear: both; text-align: center;"><img src="http://c.souza.cc/s/2010/08/django_structure.png" alt="Django project structure" title="Django project structure" width="176" height="213" class="alignnone size-full wp-image-104" /></div>
<p>&#8220;django&#8221; directory is from the <em>django-nonrel</em> zip file, &#8220;djangoappengine&#8221; directory is from <em>djangoappengine</em> zip file and &#8220;djangotoolbox&#8221; directory is from <em>djangotoolbox</em> zip file. Look that is provided an <em>app.yaml</em> file, ready to be customized. I just changed the application id inside this file. The final code of the file is the following:</p>
<div class="codecolorer-container yaml default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="yaml codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: green;">application</span><span style="font-weight: bold; color: brown;">: </span>gaeseries<span style="color: green;"><br />
version</span><span style="font-weight: bold; color: brown;">: </span><span style="">1</span><span style="color: green;"><br />
runtime</span><span style="font-weight: bold; color: brown;">: </span>python<span style="color: green;"><br />
api_version</span><span style="font-weight: bold; color: brown;">: </span><span style="">1</span><br />
<span style="color: green;"><br />
default_expiration</span><span style="font-weight: bold; color: brown;">: </span>'365d'<br />
<span style="color: #007F45;"><br />
handlers</span>:<span style="color: green;"><br />
- url</span><span style="font-weight: bold; color: brown;">: </span>/remote_api<span style="color: green;"><br />
&nbsp; script</span><span style="font-weight: bold; color: brown;">: </span>$PYTHON_LIB/google/appengine/ext/remote_api/handler.py<span style="color: green;"><br />
&nbsp; login</span><span style="font-weight: bold; color: brown;">: </span>admin<br />
<span style="color: green;"><br />
- url</span><span style="font-weight: bold; color: brown;">: </span>/_ah/queue/deferred<span style="color: green;"><br />
&nbsp; script</span><span style="font-weight: bold; color: brown;">: </span>djangoappengine/deferred/handler.py<span style="color: green;"><br />
&nbsp; login</span><span style="font-weight: bold; color: brown;">: </span>admin<br />
<span style="color: green;"><br />
- url</span><span style="font-weight: bold; color: brown;">: </span>/media/admin<span style="color: green;"><br />
&nbsp; static_dir</span><span style="font-weight: bold; color: brown;">: </span>django/contrib/admin/media/<br />
<span style="color: green;"><br />
- url</span><span style="font-weight: bold; color: brown;">: </span>/.*<span style="color: green;"><br />
&nbsp; script</span><span style="font-weight: bold; color: brown;">: </span>djangoappengine/main/main.py</div></div>
<p>I will use one version for each part of the series, so it is the first version because it is the first part =D In settings.py, we just uncomment the app <em>django.contrib.auth</em> line inside the <em>INSTALLED_APPS </em>tuple, because we want to use the built-in auth application instead of the Google Accounts API provided by App Engine.</p>
<p>All settings are ok now, it is time to create the <em>core</em> application. In the Django project, we will use the core application to manage models and serve some views. We just start it using the following command:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ ./manage.py startapp core</div></div>
<p>It is a famous Django command, that creates the application structure, which is a Python package containing 3 Python modules: <em>models</em>, <em>tests</em> and <em>views</em>. Now we have to create the <em>Post</em> model. Here is the code of <em>models.py</em> file:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">db</span> <span style="color: #ff7700;font-weight:bold;">import</span> models<br />
<span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">contrib</span>.<span style="color: black;">auth</span>.<span style="color: black;">models</span> <span style="color: #ff7700;font-weight:bold;">import</span> User<br />
<br />
<span style="color: #ff7700;font-weight:bold;">class</span> Post<span style="color: black;">&#40;</span>models.<span style="color: black;">Model</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; title <span style="color: #66cc66;">=</span> models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">200</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; content <span style="color: #66cc66;">=</span> models.<span style="color: black;">TextField</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; date <span style="color: #66cc66;">=</span> models.<span style="color: black;">DateTimeField</span><span style="color: black;">&#40;</span>auto_now_add <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #dc143c;">user</span> <span style="color: #66cc66;">=</span> models.<span style="color: black;">ForeignKey</span><span style="color: black;">&#40;</span>User<span style="color: black;">&#41;</span></div></div>
<p>Now we just need to &#8220;install&#8221; the <em>core</em> application putting it on INSTALLED_APPS tuple in settings.py file and Django will be ready to play with BigTable. :) We will use the django.contrib.auth app, so let&#8217;s run a manage command to create a superuser:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ ./manage.py createsuperuser</div></div>
<p>After create the superuser, we need to setup login and logout URLs, and make two templates. So, in <em>urls.py</em> file, put two mappings to login and logout views. The file will look like this:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">conf</span>.<span style="color: black;">urls</span>.<span style="color: black;">defaults</span> <span style="color: #ff7700;font-weight:bold;">import</span> *<br />
<br />
urlpatterns <span style="color: #66cc66;">=</span> patterns<span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; <span style="color: black;">&#40;</span><span style="color: #483d8b;">'^$'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'django.views.generic.simple.direct_to_template'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp;<span style="color: black;">&#123;</span><span style="color: #483d8b;">'template'</span>: <span style="color: #483d8b;">'home.html'</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; <span style="color: black;">&#40;</span><span style="color: #483d8b;">'^login/$'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'django.contrib.auth.views.login'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; <span style="color: black;">&#40;</span><span style="color: #483d8b;">'^logout/$'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'django.contrib.auth.views.logout'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#41;</span></div></div>
<p>Here is the <em>registration/login.html</em> template:</p>
<div class="codecolorer-container html4strict default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="html4strict codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">{% extends &quot;base.html&quot; %}<br />
<br />
{% block content %}<br />
<br />
<span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">p</span>&gt;</span>Fill the form below to login in the system ;)<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">p</span>&gt;</span><br />
<br />
{% if form.errors %}<br />
<span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">p</span>&gt;</span>Your username and password didn't match. Please try again.<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">p</span>&gt;</span><br />
{% endif %}<br />
<br />
<span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">form</span> <span style="color: #000066;">method</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;post&quot;</span> <span style="color: #000066;">action</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;{% url django.contrib.auth.views.login %}&quot;</span>&gt;</span>{% csrf_token %}<br />
<span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">table</span>&gt;</span><br />
<span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">tr</span>&gt;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">td</span>&gt;</span>{{ form.username.label_tag }}<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">td</span>&gt;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">td</span>&gt;</span>{{ form.username }}<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">td</span>&gt;</span><br />
<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">tr</span>&gt;</span><br />
<span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">tr</span>&gt;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">td</span>&gt;</span>{{ form.password.label_tag }}<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">td</span>&gt;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">td</span>&gt;</span>{{ form.password }}<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">td</span>&gt;</span><br />
<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">tr</span>&gt;</span><br />
<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">table</span>&gt;</span><br />
<br />
<span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">input</span> <span style="color: #000066;">type</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;submit&quot;</span> <span style="color: #000066;">value</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;login&quot;</span> <span style="color: #66cc66;">/</span>&gt;</span><br />
<span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">input</span> <span style="color: #000066;">type</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;hidden&quot;</span> <span style="color: #000066;">name</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;next&quot;</span> <span style="color: #000066;">value</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;{{ next }}&quot;</span> <span style="color: #66cc66;">/</span>&gt;</span><br />
<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">form</span>&gt;</span><br />
<br />
{% endblock %}</div></div>
<p>And <em>registration/logged_out.html</em> template:</p>
<div class="codecolorer-container html4strict default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="html4strict codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">{% extends &quot;base.html&quot; %}<br />
<br />
{% block content %}<br />
&nbsp; &nbsp; Bye :)<br />
{% endblock %}</div></div>
<p>See the two added lines in highlight. In settings.py file, add three lines:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">LOGIN_URL <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'/login/'</span><br />
LOGOUT_URL <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'/logout/'</span><br />
LOGIN_REDIRECT_URL <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'/'</span></div></div>
<p>And we are ready to code =) Let&#8217;s create the login protected view, where we will write and save a new post. To do that, first we need to create a Django Form, to deal with the data. There are two fields in this form: title and content, when the form is submitted, the user property is filled with the current logged user and the date property is filled with the current time. So, here is the code of the ModelForm:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">class</span> PostForm<span style="color: black;">&#40;</span>forms.<span style="color: black;">ModelForm</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">class</span> Meta:<br />
&nbsp; &nbsp; &nbsp; &nbsp; model <span style="color: #66cc66;">=</span> Post<br />
&nbsp; &nbsp; &nbsp; &nbsp; exclude <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'user'</span><span style="color: #66cc66;">,</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> save<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> <span style="color: #dc143c;">user</span><span style="color: #66cc66;">,</span> commit <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; post <span style="color: #66cc66;">=</span> <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>PostForm<span style="color: #66cc66;">,</span> <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: black;">save</span><span style="color: black;">&#40;</span>commit <span style="color: #66cc66;">=</span> <span style="color: #008000;">False</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; post.<span style="color: #dc143c;">user</span> <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">user</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> commit:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; post.<span style="color: black;">save</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> post</div></div>
<p>Here is the <em>views.py</em> file, with the two views (one &#8220;mocked up&#8221;, with a simple redirect):</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">contrib</span>.<span style="color: black;">auth</span>.<span style="color: black;">decorators</span> <span style="color: #ff7700;font-weight:bold;">import</span> login_required<br />
<span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">shortcuts</span> <span style="color: #ff7700;font-weight:bold;">import</span> render_to_response<br />
<span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">template</span> <span style="color: #ff7700;font-weight:bold;">import</span> RequestContext<br />
<span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">http</span> <span style="color: #ff7700;font-weight:bold;">import</span> HttpResponseRedirect<br />
<span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">core</span>.<span style="color: black;">urlresolvers</span> <span style="color: #ff7700;font-weight:bold;">import</span> reverse<br />
<span style="color: #ff7700;font-weight:bold;">from</span> forms <span style="color: #ff7700;font-weight:bold;">import</span> PostForm<br />
<br />
<span style="color: #66cc66;">@</span>login_required<br />
<span style="color: #ff7700;font-weight:bold;">def</span> new_post<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; form <span style="color: #66cc66;">=</span> PostForm<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> request.<span style="color: black;">method</span> <span style="color: #66cc66;">==</span> <span style="color: #483d8b;">'POST'</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; form <span style="color: #66cc66;">=</span> PostForm<span style="color: black;">&#40;</span>request.<span style="color: black;">POST</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> form.<span style="color: black;">is_valid</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; form.<span style="color: black;">save</span><span style="color: black;">&#40;</span>request.<span style="color: #dc143c;">user</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponseRedirect<span style="color: black;">&#40;</span>reverse<span style="color: black;">&#40;</span><span style="color: #483d8b;">'core.views.list_posts'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> render_to_response<span style="color: black;">&#40;</span><span style="color: #483d8b;">'new_post.html'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">locals</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> context_instance<span style="color: #66cc66;">=</span>RequestContext<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: black;">&#41;</span><br />
<br />
<span style="color: #ff7700;font-weight:bold;">def</span> list_posts<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponseRedirect<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span><span style="color: black;">&#41;</span></div></div>
<p>There is only two steps to do to finally save posts on BigTable: map a URL for the views above and create the <em>new_post.html</em> template. Here is the mapping code:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: black;">&#40;</span><span style="color: #483d8b;">'^posts/new/$'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'core.views.new_post'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#40;</span><span style="color: #483d8b;">'^posts/$'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'core.views.list_posts'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span></div></div>
<p>And here is the template code:</p>
<div class="codecolorer-container html4strict default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="html4strict codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">{% extends &quot;base.html&quot; %}<br />
<br />
{% block content %}<br />
&nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">form</span> <span style="color: #000066;">action</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;{% url core.views.new_post %}&quot;</span> <span style="color: #000066;">method</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;post&quot;</span> <span style="color: #000066;">accept-charset</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;utf-8&quot;</span>&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; {% csrf_token %}<br />
&nbsp; &nbsp; &nbsp; &nbsp; {{ form.as_p }}<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">p</span>&gt;&lt;<span style="color: #000000; font-weight: bold;">input</span> <span style="color: #000066;">type</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;submit&quot;</span> <span style="color: #000066;">value</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;Post!&quot;</span><span style="color: #66cc66;">/</span>&gt;&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">p</span>&gt;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">form</span>&gt;</span><br />
{% endblock %}</div></div>
<p>Now, we can run on terminal <em>./manage.py runserver</em> and access the URL <em>http://localhost:8000/posts/new</em> on the browser, see the form, fill it and save the post :D The last one step is list all posts in <em>http://localhost:8000/posts/</em>. The <em>list_posts</em> view is already mapped to the URL <em>/posts/</em>, so we just need to create the code of the view and a template to show the list of posts. Here is the view code:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">def</span> list_posts<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; posts <span style="color: #66cc66;">=</span> Post.<span style="color: black;">objects</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> render_to_response<span style="color: black;">&#40;</span><span style="color: #483d8b;">'list_posts.html'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">locals</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> context_instance<span style="color: #66cc66;">=</span>RequestContext<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: black;">&#41;</span></div></div>
<p>And the <em>list_posts.html</em> template code:</p>
<div class="codecolorer-container html4strict default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="html4strict codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">{% extends &quot;base.html&quot; %}<br />
<br />
{% block content %}<br />
<span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">dl</span>&gt;</span><br />
&nbsp; &nbsp; {% for post in posts %}<br />
&nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">dt</span>&gt;</span>{{ post.title }} (written by {{ post.user.username }})<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">dt</span>&gt;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">dd</span>&gt;</span>{{ post.content }}<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">dd</span>&gt;</span><br />
&nbsp; &nbsp; {% endfor %}<br />
<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">dl</span>&gt;</span><br />
{% endblock %}</div></div>
<p>Finished? Not yet :) The application now is ready to deploy. How do we deploy it? Just one command:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">./manage.py deploy</div></div>
<p>Done! Now, to use everything that we have just created on App Engine remote server, just create a super user in that server and enjoy:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">./manage.py remote createsuperuser</div></div>
<p>You can check this application flying on Google App Engine: <a title="See application flying" rel="nofollow" href="http://1.latest.gaeseries.appspot.com/" target="_blank">http://1.latest.gaeseries.appspot.com</a> (use demo for username and password in login page).</p>
<p>You can check this application code out in Github: <a title="Application code" rel="nofollow" href="http://github.com/fsouza/gaeseries/tree/django" target="_blank">http://github.com/fsouza/gaeseries/tree/django</a>.</p>
<p>Enjoy :)</p>
]]></content:encoded>
			<wfw:commentRss>http://f.souza.cc/2010/08/flying-with-django-on-google-app-engine/feed/</wfw:commentRss>
		<slash:comments>25</slash:comments>
		</item>
		<item>
		<title>Seleniumless Django applications: using the test client</title>
		<link>http://f.souza.cc/2010/07/seleniumless-django-applications-using-the-test-client/</link>
		<comments>http://f.souza.cc/2010/07/seleniumless-django-applications-using-the-test-client/#comments</comments>
		<pubDate>Fri, 30 Jul 2010 02:37:00 +0000</pubDate>
		<dc:creator>Francisco Souza</dc:creator>
				<category><![CDATA[software development]]></category>
		<category><![CDATA[agile]]></category>
		<category><![CDATA[bdd]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[lettuce]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[selenium]]></category>
		<category><![CDATA[tdd]]></category>
		<category><![CDATA[web development]]></category>

		<guid isPermaLink="false">http://www.franciscosouza.com/?p=11</guid>
		<description><![CDATA[Some people waste a lot of time testing Django applications only with Selenium. No, I don't think that you should not use Selenium to test Django applications, I just wanna show that nobody needs to use Selenium for all webtests in Django. <a href="http://f.souza.cc/2010/07/seleniumless-django-applications-using-the-test-client/"><em>Continue&#160;reading&#160;<span class="meta-nav">&#8594;</span></em></a>]]></description>
			<content:encoded><![CDATA[<p>Some people waste a lot of time testing Django applications only with Selenium. No, I don&#8217;t think that you should not use Selenium to test Django applications, I just wanna show that nobody needs to use Selenium for all webtests in Django.</p>
<p>Selenium drives a browser, so you can  have true test results on navigation, but Selenium is not needed to test a page title or a response status code, for example. Selenium tests are powerful, but slow, so you can preserve them to use when you really need, and use more simple and fast tools to more simple and fast tests.<span id="more-11"></span></p>
<p>Django has a powerful tool for web tests: <a title="Django test client documentation" rel="nofollow" href="http://docs.djangoproject.com/en/dev/topics/testing/#module-django.test.client" target="_blank">the test client</a>. Using this module, you can make requests and test the response (its content, status code, URL and everything inside a response).</p>
<p>Imagine that you have a Django application that manage all books of the world, and you want to test if the title of the <em>/books</em> page is <em>&#8220;All the books of the world&#8221;</em>, you can use Selenium, but IMHO, you should not use Selenium.</p>
<p>Suppose that there is the following <a title="Lettuce - BDD in Python" rel="nofollow" href="http://www.lettuce.it/" target="_blank">Lettuce</a> feature file:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Feature: List books<br />
&nbsp; &nbsp; In order to know which books are in the system<br />
&nbsp; &nbsp; I want to see the list of books<br />
<br />
&nbsp; &nbsp; Scenario: Listing all books in the world<br />
&nbsp; &nbsp; &nbsp; &nbsp; When I go to the /books URL<br />
&nbsp; &nbsp; &nbsp; &nbsp; Then the page title should be &quot;All the books of the world&quot;</div></div>
<p>If we use Selenium to define the scenario &#8220;Listing all books in the world&#8221;, we should have a <em>terrain.py</em> file with the following content:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> lettuce <span style="color: #ff7700;font-weight:bold;">import</span> before<span style="color: #66cc66;">,</span> after<span style="color: #66cc66;">,</span> world<br />
<span style="color: #ff7700;font-weight:bold;">from</span> selenium <span style="color: #ff7700;font-weight:bold;">import</span> get_driver<span style="color: #66cc66;">,</span> FIREFOX<br />
<br />
<span style="color: #66cc66;">@</span>before.<span style="color: #008000;">all</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> setup_browser<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; world.<span style="color: black;">browser</span> <span style="color: #66cc66;">=</span> get_driver<span style="color: black;">&#40;</span>FIREFOX<span style="color: black;">&#41;</span><br />
<br />
<span style="color: #66cc66;">@</span>after.<span style="color: #008000;">all</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> teardown_browser<span style="color: black;">&#40;</span>total<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; world.<span style="color: black;">browser</span>.<span style="color: black;">quit</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<p>And define the steps definition with the following code:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #808080; font-style: italic;"># -*- coding: utf-8 -*-</span><br />
<span style="color: #ff7700;font-weight:bold;">from</span> lettuce <span style="color: #ff7700;font-weight:bold;">import</span> step<span style="color: #66cc66;">,</span> world<br />
<span style="color: #ff7700;font-weight:bold;">from</span> lettuce.<span style="color: black;">django</span> <span style="color: #ff7700;font-weight:bold;">import</span> django_url<br />
<span style="color: #ff7700;font-weight:bold;">from</span> nose.<span style="color: black;">tools</span> <span style="color: #ff7700;font-weight:bold;">import</span> assert_equals<br />
<br />
<span style="color: #66cc66;">@</span>step<span style="color: black;">&#40;</span>u<span style="color: #483d8b;">'When I go to the /books URL'</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> when_i_go_to_the_books_url<span style="color: black;">&#40;</span>step<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; world.<span style="color: black;">browser</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span>django_url<span style="color: black;">&#40;</span><span style="color: #483d8b;">'books'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #66cc66;">@</span>step<span style="color: black;">&#40;</span>u<span style="color: #483d8b;">'Then the page title should be &quot;(.*)&quot;'</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> then_the_page_title_should_be_group1<span style="color: black;">&#40;</span>step<span style="color: #66cc66;">,</span> page_title<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; assert_equals<span style="color: black;">&#40;</span>world.<span style="color: black;">browser</span>.<span style="color: black;">get_title</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> page_title<span style="color: black;">&#41;</span></div></div>
<p>There is nothing difficult here, the steps definition code is very simple and the test itself too, but how many time Lettuce spends running this test? Let&#8217;s check:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ time ./manage.py harvest<br />
Django's builtin server is running at 0.0.0.0:8000<br />
Setting up a test database...OK<br />
<br />
...<br />
<br />
1 feature (1 passed)<br />
1 scenario (1 passed)<br />
2 steps (2 passed)<br />
<br />
real 0m7.172s<br />
user 0m0.740s<br />
sys 0m0.270s</div></div>
<p>About 7 seconds to run a really really really simple and small test. We can earn time testing Django applications using the Django test client. Let&#8217;s rewrite the test above using Django test client, we just need to change the <em>terrain.py</em>:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> lettuce <span style="color: #ff7700;font-weight:bold;">import</span> before<span style="color: #66cc66;">,</span> world<br />
<span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: #dc143c;">test</span> <span style="color: #ff7700;font-weight:bold;">import</span> client<br />
<br />
<span style="color: #66cc66;">@</span>before.<span style="color: #008000;">all</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> setup_browser<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; world.<span style="color: black;">browser</span> <span style="color: #66cc66;">=</span> client.<span style="color: black;">Client</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<p>And the step definitions:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #808080; font-style: italic;"># -*- coding: utf-8 -*-</span><br />
<span style="color: #ff7700;font-weight:bold;">from</span> lettuce <span style="color: #ff7700;font-weight:bold;">import</span> step<span style="color: #66cc66;">,</span> world<br />
<span style="color: #ff7700;font-weight:bold;">from</span> nose.<span style="color: black;">tools</span> <span style="color: #ff7700;font-weight:bold;">import</span> assert_equals<br />
<span style="color: #ff7700;font-weight:bold;">from</span> lxml <span style="color: #ff7700;font-weight:bold;">import</span> html<br />
<br />
<span style="color: #66cc66;">@</span>step<span style="color: black;">&#40;</span>u<span style="color: #483d8b;">'When I go to the /books URL'</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> when_i_go_to_the_books_url<span style="color: black;">&#40;</span>step<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; world.<span style="color: black;">response</span> <span style="color: #66cc66;">=</span> world.<span style="color: black;">browser</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/books/'</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #66cc66;">@</span>step<span style="color: black;">&#40;</span>u<span style="color: #483d8b;">'Then the page title should be &quot;(.*)&quot;'</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> then_the_page_title_should_be_group1<span style="color: black;">&#40;</span>step<span style="color: #66cc66;">,</span> page_title<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; dom <span style="color: #66cc66;">=</span> html.<span style="color: black;">fromstring</span><span style="color: black;">&#40;</span>world.<span style="color: black;">response</span>.<span style="color: black;">content</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; title <span style="color: #66cc66;">=</span> dom.<span style="color: black;">xpath</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'//head/title'</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; title <span style="color: #66cc66;">=</span> title.<span style="color: black;">pop</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; assert_equals<span style="color: black;">&#40;</span>title.<span style="color: black;">text</span><span style="color: #66cc66;">,</span> page_title<span style="color: black;">&#41;</span></div></div>
<p>The only notice here is that is we have a little much code to test the title of the page. How many time Lettuce spends testing it?</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ time ./manage.py harvest<br />
Django's builtin server is running at 0.0.0.0:8000<br />
Setting up a test database...OK<br />
<br />
...<br />
<br />
1 feature (1 passed)<br />
1 scenario (1 passed)<br />
2 steps (2 passed)<br />
<br />
real 0m3.288s<br />
user 0m0.700s<br />
sys 0m0.130s</div></div>
<p>About 3 seconds :) We didn&#8217;t need to open Firefox for visit the page and get it&#8217;s title, we just use the Django test client that knows how to deal with Django requests, and <em>lxml</em> to deal with the response content. Think about how many seconds you can earn just stopping to open a browser for the simplest tests of the world =P</p>
<p>There is a lot of other stuffs that you can do using the Django Test Client, and I plan to write more about it, but now, just check the <a title="Django test client documentation" rel="nofollow" href="http://docs.djangoproject.com/en/dev/topics/testing/#module-django.test.client" target="_blank">official docs</a> and have fun.</p>
<p>P.S.: Another great tool for Django testing is <a title="Django sane testing" rel="nofollow" href="http://devel.almad.net/trac/django-sane-testing" target="_blank">Django sane testing</a>.</p>
<p>Happy testing :)</p>
]]></content:encoded>
			<wfw:commentRss>http://f.souza.cc/2010/07/seleniumless-django-applications-using-the-test-client/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Making development with Django more fun with BDD using Freshen</title>
		<link>http://f.souza.cc/2010/06/making-development-with-django-more-fun-with-bdd-using-freshen/</link>
		<comments>http://f.souza.cc/2010/06/making-development-with-django-more-fun-with-bdd-using-freshen/#comments</comments>
		<pubDate>Tue, 22 Jun 2010 19:23:00 +0000</pubDate>
		<dc:creator>Francisco Souza</dc:creator>
				<category><![CDATA[software development]]></category>
		<category><![CDATA[agile]]></category>
		<category><![CDATA[bdd]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[freshen]]></category>
		<category><![CDATA[nose]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[tdd]]></category>
		<category><![CDATA[web development]]></category>

		<guid isPermaLink="false">http://www.franciscosouza.com/?p=9</guid>
		<description><![CDATA[Freshen is an acceptance testing framework for Python, inspired by Cucumber and has the same goal: make BDD fun, but using Python instead of Ruby. Freshen uses the same syntax of Cucumber (Gherkin Syntax) and runs as a plugin of Nose, a powerful Python tool for using daily when you with TDD =) <a href="http://f.souza.cc/2010/06/making-development-with-django-more-fun-with-bdd-using-freshen/"><em>Continue&#160;reading&#160;<span class="meta-nav">&#8594;</span></em></a>]]></description>
			<content:encoded><![CDATA[<p>Freshen is an acceptance testing framework for Python, inspired by <a title="Cucumber" rel="nofollow" href="http://cukes.info/" target="_blank">Cucumber</a> and has the same goal: make BDD fun, but using Python instead of Ruby. Freshen uses the same syntax of Cucumber (Gherkin Syntax) and runs as a plugin of <a title="Nose" rel="nofollow" href="http://somethingaboutorange.com/mrl/projects/nose/" target="_blank">Nose</a>, a powerful Python tool for using daily when you with TDD =)<span id="more-9"></span></p>
<blockquote><p><strong>Important:</strong> If you don&#8217;t know Django, this article is not recommended for you.</p></blockquote>
<p>Nose is plugin based, so to use Django under Nose, we need to install a plugin called NoseDjango. To work isolated from the database, we need a mock framework. Let&#8217;s use <a title="Mock framework for Python" rel="nofollow" href="http://www.voidspace.org.uk/python/mock/" target="_blank">Mock</a>, wich is a simple and powerful mock framework for Python. We can install Django, Freshen, Mock, Nose and NoseDjango with a simple command:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ [sudo] pip install mock django nose nosedjango freshen</div></div>
<p>It works in any operating system with Python and <a title="PIP" rel="nofollow" href="http://pip.openplans.org/" target="_blank">PIP</a>.</p>
<p>After installing everything we need, we just start a Django Project, called &#8220;library&#8221; (just supposing that we are developing a software for the library on the next corner):</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ django-admin.py startproject library</div></div>
<p>Looking quickly on Django settings, in the settings.py file, only database and templates settings:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">DATABASES <span style="color: #66cc66;">=</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #483d8b;">'default'</span>: <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'ENGINE'</span>: <span style="color: #483d8b;">'django.db.backends.sqlite3'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'NAME'</span>: <span style="color: #483d8b;">'data.db'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'USER'</span>: <span style="color: #483d8b;">''</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'PASSWORD'</span>: <span style="color: #483d8b;">''</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'HOST'</span>: <span style="color: #483d8b;">''</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'PORT'</span>: <span style="color: #483d8b;">''</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; <span style="color: black;">&#125;</span><br />
<span style="color: black;">&#125;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Other settings here</span><br />
<br />
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span><br />
project_root <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">dirname</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">abspath</span><span style="color: black;">&#40;</span>__file__<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
template_root <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span>project_root<span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'templates'</span><span style="color: black;">&#41;</span><br />
<br />
TEMPLATE_DIRS <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><br />
&nbsp; &nbsp; template_root<span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Other settings here</span></div></div>
<p>After start and setup the project, let&#8217;s start our Django application. Let&#8217;s start an application called &#8220;loans&#8221;. It app will manage the loans of our library:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ django-admin startapp loans</div></div>
<p>After start the application, let&#8217;s define our Book model and install the application. You can see below the code of <em>models</em> module inside the <em>loans</em> app:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">db</span> <span style="color: #ff7700;font-weight:bold;">import</span> models<br />
<br />
<span style="color: #ff7700;font-weight:bold;">class</span> Book<span style="color: black;">&#40;</span>models.<span style="color: black;">Model</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; title <span style="color: #66cc66;">=</span> models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length<span style="color: #66cc66;">=</span><span style="color: #ff4500;">100</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; author <span style="color: #66cc66;">=</span> models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length<span style="color: #66cc66;">=</span><span style="color: #ff4500;">100</span><span style="color: black;">&#41;</span></div></div>
<p>Now, we need to describe our acceptance tests, or as Freshen and Cucumber call: our system features. Let&#8217;s describe the loans application&#8217;s features inside the loans application, creating a directory called &#8220;features&#8221; inside the application.</p>
<p>The first feature of the application will be a feature that describes the access to a list containg all the books on the path &#8220;/loans/books&#8221;:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Feature: List of books<br />
&nbsp; In order to loan a book<br />
&nbsp; As a system user<br />
&nbsp; I want to see the list of all books in the system<br />
<br />
&nbsp; Scenario: List all books<br />
&nbsp; &nbsp; Given there are 20 books in the system<br />
&nbsp; &nbsp; When I navigate to the books list page<br />
&nbsp; &nbsp; Then I should see the title of the 20 books</div></div>
<p>Let&#8217;s understand the feature: A Freshen feature is a description of a system feature! :) On the list of books, we have a scenario where we list all books. That scenario is described using some steps: given, when and then. You can learn more about Given, When and Then looking for some <a title="Introducing BDD" rel="nofollow" href="http://blog.dannorth.net/introducing-bdd/" target="_blank">resources about BDD</a>.</p>
<p>After describe a feature and it&#8217;s scenarios, we need to define the steps of each scenario. So, we put these definitions in a Python module called <em>steps.py</em> in our <strong>features</strong> directory. The scenario <em>List all books</em> has three steps: <em>&#8220;Given there are 20 books in the system&#8221;; &#8220;When I navigate to the books list page&#8221;</em> and <em>&#8220;Then I should see the title of the 20 books&#8221;</em>. First, let&#8217;s define the first step: <em>Given there are 20 books in the system</em>. Each step definition is a Python function decorated with the keyword that defines that step.</p>
<p>So, to define &#8220;Given&#8221; step, Freshen provides the @Given decorator. Let&#8217;s define a function that creates and saves 20 books in the database. But we will not really save it in the database, because we are working isolated from the database. We will just store the book list in a context object.</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #66cc66;">@</span>Given<span style="color: black;">&#40;</span><span style="color: #483d8b;">'there are (<span style="color: #000099; font-weight: bold;">\d</span>+) books in the system'</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> create_books<span style="color: black;">&#40;</span>total_of_books<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; scc.<span style="color: black;">books</span> <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">&#40;</span><span style="color: #008000;">int</span><span style="color: black;">&#40;</span>total_of_books<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; book <span style="color: #66cc66;">=</span> Book<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; book.<span style="color: black;">title</span> <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'Book %d'</span> %<span style="color: black;">&#40;</span>i + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; book.<span style="color: black;">author</span> <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'Francisco Souza'</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; scc.<span style="color: black;">books</span>.<span style="color: black;">append</span><span style="color: black;">&#40;</span>book<span style="color: black;">&#41;</span></div></div>
<p>We use a regular expression to determine a pattern to receive a parameter in the function. The function<em> create_books</em> receives a parameter wich indicates the number of books that will be created. All parameters obtained from step specification is an instance of <em>str</em> and need to be converted properly if you want to use it as a number.</p>
<p>The second line of the <em>create_books</em> function defines an empty list called <em>books</em> inside an object called <em>scc</em>. Freshen provides three objects for context storage: <em>glc</em>, the global context, wich is never cleaned; <em>ftc</em>, the feature context, cleaned at the start of each feature and <em>scc</em>, the scenario context, wich is cleaned at the start of each scenario. Once we put the books object list in the scenario context, we can use this list in all steps of that scenario.</p>
<p>Now, let&#8217;s define the second step: <em>When I navigate to the books list page</em>. Now we will use the @When decorator to decorate a function called <em>navigate_to_books_list</em>. Here is the code:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #66cc66;">@</span>When<span style="color: black;">&#40;</span><span style="color: #483d8b;">'I navigate to the books list page'</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> navigate_to_books_list<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">with</span> patch<span style="color: black;">&#40;</span><span style="color: #483d8b;">'loans.models.Book'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">as</span> MockClass:<br />
&nbsp; &nbsp; &nbsp; &nbsp; Book.<span style="color: black;">objects</span>.<span style="color: #008000;">all</span> <span style="color: #66cc66;">=</span> Mock<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; Book.<span style="color: black;">objects</span>.<span style="color: #008000;">all</span>.<span style="color: black;">return_value</span> <span style="color: #66cc66;">=</span> scc.<span style="color: black;">books</span><br />
&nbsp; &nbsp; client <span style="color: #66cc66;">=</span> Client<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; scc.<span style="color: black;">response</span> <span style="color: #66cc66;">=</span> client.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/loans/books'</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; Book.<span style="color: black;">objects</span>.<span style="color: #008000;">all</span>.<span style="color: black;">assert_called_with</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<p>This function just makes a GET request on the URL /loans/books and saves  the response in a object called <em>response</em>, stored in scenario  context. The Book model was mocked here, using the <a href="http://www.voidspace.org.uk/python/mock/patch.html">patch decorator from Mock</a>. We said to the mock: &#8220;when <em>Book.objects.all()</em> is called, return my list of books, stored in scenario context&#8221;. The last line of the function asserts if the method is really called in our GET request.</p>
<p>Now we can run the nosetests and see what happens. To execute Nose with Django and Freshen, we need to run the following command:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ nosetests --with-freshen --with-django</div></div>
<p>The output at this point is:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">E.<br />
======================================================================<br />
ERROR: List of books: List all books<br />
----------------------------------------------------------------------<br />
Traceback (most recent call last):<br />
File &quot;/home/francisco/Projetos/library/loans/features/steps.py&quot;, line 19, in navigate_to_books_list<br />
scc.response = client.get('/loans/books')<br />
File &quot;/usr/local/lib/python2.6/dist-packages/django/test/client.py&quot;, line 290, in get<br />
response = self.request(**r)<br />
File &quot;/usr/local/lib/python2.6/dist-packages/django/core/handlers/base.py&quot;, line 127, in get_response<br />
return callback(request, **param_dict)<br />
File &quot;/usr/local/lib/python2.6/dist-packages/django/views/defaults.py&quot;, line 13, in page_not_found<br />
t = loader.get_template(template_name) # You need to create a 404.html template.<br />
File &quot;/usr/local/lib/python2.6/dist-packages/django/template/loader.py&quot;, line 157, in get_template<br />
template, origin = find_template(template_name)<br />
File &quot;/usr/local/lib/python2.6/dist-packages/django/template/loader.py&quot;, line 138, in find_template<br />
raise TemplateDoesNotExist(name)<br />
TemplateDoesNotExist: 404.html<br />
<br />
&gt;&gt; in &quot;I navigate to the books list page&quot; # loans/features/list_books.feature:8<br />
<br />
----------------------------------------------------------------------<br />
Ran 2 tests in 0.174s<br />
<br />
FAILED (errors=1)<br />
Destroying test database 'default'...</div></div>
<p>The test fails with the following exception: <em>TemplateDoesNotExist: 404.html</em>. So, let&#8217;s create that template and see what happens.</p>
<p>After add a 404.html template to the templates directory in the root of the project, just run nosetests again, using the verbose mode:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ nosetests --with-freshen --with-django -v<br />
List of books: List all books ... FAIL<br />
test_model_validation_title_author (library.loans.tests.BookTest) ... ok<br />
<br />
======================================================================<br />
FAIL: List of books: List all books<br />
----------------------------------------------------------------------<br />
Traceback (most recent call last):<br />
File &quot;/home/francisco/Projetos/library/loans/features/steps.py&quot;, line 24, in navigate_to_books_list<br />
Book.objects.all.assert_called_with()<br />
File &quot;/usr/local/lib/python2.6/dist-packages/mock.py&quot;, line 150, in assert_called_with<br />
assert self.call_args == (args, kwargs), 'Expected: %s\nCalled with: %s' % ((args, kwargs), self.call_args)<br />
AssertionError: Expected: ((), {})<br />
Called with: None<br />
<br />
----------------------------------------------------------------------<br />
Ran 2 tests in 0.176s<br />
<br />
FAILED (failures=1)<br />
Destroying test database 'default'...</div></div>
<p>Step fails again, why? On function &#8220;navigate_to_books_list&#8221; the last line defines that the method Book.objects.all() should be called with no parameters in the GET request (lines above), but this method was not called anywhere, once that GET request returned a 404 response, because there is not an URL mapping to a view that responds by the action &#8216;/loans/books&#8217;, and there is not a view wich makes the expected method call. So, let&#8217;s make the view and map it to the &#8220;/loans/books&#8221; path. Here is the view code:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">def</span> books<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>:<br />
    books <span style="color: #66cc66;">=</span> Book.<span style="color: black;">objects</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
    <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponse<span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span><span style="color: black;">&#41;</span></div></div>
<p>Look that the view has only two lines of code and returns a HttpResponse object with blank content, it&#8217;s the enough code to make the Step definition works. To map the view to a URL path, create a <em>urls.py</em> module inside the application directory, with the following code:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">conf</span>.<span style="color: black;">urls</span>.<span style="color: black;">defaults</span> <span style="color: #ff7700;font-weight:bold;">import</span> *<br />
<br />
urlpatterns <span style="color: #66cc66;">=</span> patterns<span style="color: black;">&#40;</span><span style="color: #483d8b;">'loans.views'</span><span style="color: #66cc66;">,</span><br />
    url<span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'^books/'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'books'</span><span style="color: #66cc66;">,</span> name <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'books_list'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#41;</span></div></div>
<p>And include it on the main urls.py of project, that will contain the code bellow:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">conf</span>.<span style="color: black;">urls</span>.<span style="color: black;">defaults</span> <span style="color: #ff7700;font-weight:bold;">import</span> *<br />
<br />
<span style="color: #808080; font-style: italic;"># Uncomment the next two lines to enable the admin:</span><br />
<span style="color: #808080; font-style: italic;"># from django.contrib import admin</span><br />
<span style="color: #808080; font-style: italic;"># admin.autodiscover()</span><br />
<br />
urlpatterns <span style="color: #66cc66;">=</span> patterns<span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span><span style="color: #66cc66;">,</span><br />
    <span style="color: #808080; font-style: italic;"># Example:</span><br />
    <span style="color: #808080; font-style: italic;"># (r'^library/', include('library.foo.urls')),</span><br />
<br />
    <span style="color: #808080; font-style: italic;"># Uncomment the admin/doc line below and add 'django.contrib.admindocs' </span><br />
    <span style="color: #808080; font-style: italic;"># to INSTALLED_APPS to enable admin documentation:</span><br />
    <span style="color: #808080; font-style: italic;"># (r'^admin/doc/', include('django.contrib.admindocs.urls')),</span><br />
<br />
    <span style="color: #808080; font-style: italic;"># Uncomment the next line to enable the admin:</span><br />
    <span style="color: #808080; font-style: italic;"># (r'^admin/', include(admin.site.urls)),</span><br />
    <span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'^loans/'</span><span style="color: #66cc66;">,</span> include<span style="color: black;">&#40;</span><span style="color: #483d8b;">'loans.urls'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#41;</span></div></div>
<p>Run nosetests again and see what happens:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ nosetests --with-freshen --with-django -v<br />
List of books: List all books ... UNDEFINED: &quot;I should see the title of the 20 books&quot; # loans/features/list_books.feature:9<br />
test_model_validation_title_author (library.loans.tests.BookTest) ... ok<br />
<br />
----------------------------------------------------------------------<br />
Ran 2 tests in 0.181s<br />
<br />
OK (UNDEFINED=1)<br />
Destroying test database 'default'...</div></div>
<p>Freshen said: I found an undefined step (&#8220;I should see the title of the 20 books&#8221;). Let&#8217;s define that step, using the @Then decorator to decorate our function called <em>check_presence_of_the_title_of_books</em>:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #66cc66;">@</span>Then<span style="color: black;">&#40;</span><span style="color: #483d8b;">'I should see the title of the (<span style="color: #000099; font-weight: bold;">\d</span>+) books'</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> check_presence_of_the_title_of_books<span style="color: black;">&#40;</span>total_of_books<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; expected_string <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">''</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> book <span style="color: #ff7700;font-weight:bold;">in</span> scc.<span style="color: black;">books</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; expected_string +<span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'&amp;lt;li&amp;gt;%s&amp;lt;/li&amp;gt;'</span> % book.<span style="color: black;">title</span><br />
&nbsp; &nbsp; assert_true<span style="color: black;">&#40;</span>expected_string <span style="color: #ff7700;font-weight:bold;">in</span> scc.<span style="color: black;">response</span>.<span style="color: black;">content</span><span style="color: black;">&#41;</span></div></div>
<p>The function checks if the books titles is included in a li HTML element. Let&#8217;s call nosetests again and see the test failing:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ nosetests --with-django --with-freshen -v<br />
List of books: List all books ... FAIL<br />
test_model_validation_title_author (library.loans.tests.BookTest) ... ok<br />
<br />
======================================================================<br />
FAIL: List of books: List all books<br />
----------------------------------------------------------------------<br />
Traceback (most recent call last):<br />
  File &quot;/home/francisco/Projetos/library/loans/features/steps.py&quot;, line 25, in check_presence_of_the_title_of_books<br />
    assert_true(expected_string in scc.response.content)<br />
AssertionError<br />
<br />
----------------------------------------------------------------------<br />
Ran 2 tests in 0.178s<br />
<br />
FAILED (failures=1)<br />
Destroying test database 'default'...</div></div>
<p>The content returned by the GET request (an empty string) is not the content expected by the defined step, so we see an AssertionError. Now, we need to make this test pass. To do it, we need only to refactor the view:</p>
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">def</span> books<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>:<br />
    books <span style="color: #66cc66;">=</span> Book.<span style="color: black;">objects</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
    <span style="color: #ff7700;font-weight:bold;">return</span> render_to_response<span style="color: black;">&#40;</span><span style="color: #483d8b;">'books_list.html'</span><span style="color: #66cc66;">,</span> <span style="color: black;">&#123;</span><br />
            <span style="color: #483d8b;">'books'</span> : books<br />
        <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span> context_instance<span style="color: #66cc66;">=</span>RequestContext<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span><br />
    <span style="color: black;">&#41;</span></div></div>
<p>And we run nosetests with Django and Freshen, in verbose mode again, and see that everything works fine =D</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ nosetests --with-django --with-freshen -v<br />
List of books: List all books ... ok<br />
test_model_validation_title_author (library.loans.tests.BookTest) ... ok<br />
<br />
----------------------------------------------------------------------<br />
Ran 2 tests in 0.190s<br />
<br />
OK<br />
Destroying test database 'default'...</div></div>
<p>That is all, folks! :) You can download <a title="Project source code at Github" rel="nofollow" href="http://github.com/fsouza/library" target="_blank">this Django Project at Github</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://f.souza.cc/2010/06/making-development-with-django-more-fun-with-bdd-using-freshen/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
	</channel>
</rss>

