<?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>Curia &#187; turbogears</title>
	<atom:link href="http://blog.curiasolutions.com/tag/turbogears/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.curiasolutions.com</link>
	<description>Technology thoughts and ideas</description>
	<lastBuildDate>Wed, 16 Dec 2009 01:38:40 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Dead-simple pagination wizardry with TurboGears 2</title>
		<link>http://blog.curiasolutions.com/2009/11/dead-simple-pagination-wizardry-with-turbogears-2/</link>
		<comments>http://blog.curiasolutions.com/2009/11/dead-simple-pagination-wizardry-with-turbogears-2/#comments</comments>
		<pubDate>Wed, 04 Nov 2009 01:00:21 +0000</pubDate>
		<dc:creator>Seth</dc:creator>
				<category><![CDATA[Web Development]]></category>
		<category><![CDATA[turbogears]]></category>

		<guid isPermaLink="false">http://blog.curiasolutions.com/?p=556</guid>
		<description><![CDATA[I recently had to implement pagination in a TG 2 project that I&#8217;m almost finished with. Although I had previously glanced at the paginate webhelper module, I first turned to the updated TurboGears documentation to see what was &#8220;officially&#8221; written up about this subject.
A lot of work has been going into the TG docs in [...]]]></description>
			<content:encoded><![CDATA[<p>I recently had to implement pagination in a TG 2 project that I&#8217;m almost finished with. Although I had previously glanced at the <a href="http://turbogears.org/2.1/docs/modules/thirdparty/webhelpers_paginate.html" target="_blank"><em>paginate</em></a> webhelper module, I first turned to the <a href="http://turbogears.org/2.1/docs/index.html" target="_blank">updated TurboGears documentation</a> to see what was &#8220;officially&#8221; written up about this subject.</p>
<p>A lot of work has been going into the TG docs in preparation for the upcoming v2.1 launch, and one little gem that was recently added was the <a href="http://turbogears.org/2.1/docs/main/Pagination/index.html" target="_blank">Pagination Quickstart</a> (thanks Lukas). While this sent me down the right path towards pagination perfection, there were a few things that came up that I felt like deserved further explanation:</p>
<ol>
<li>The <a href="http://turbogears.org/2.1/docs/modules/tgdecorators.html#tg.decorators.paginate" target="_blank">@paginate() decorator</a> is an extremely simple wrapper for the <em>paginate</em> webhelper module.</li>
<li>When paginating SQLAlchemy queries, <em>paginate</em> runs a duplicate <em>count()</em> query to figure out its current position in the data collection. While the magic of this is nice, the possible performance issues with duplicating complex SQL queries on a high-traffic website could be undesirable.</li>
<li>It&#8217;s common to have controller methods dynamically switch to a &#8220;feed template&#8221; when doing AJAX pagination. Don&#8217;t worry, <em>paginate</em> + <em>override_template</em> makes this a piece of cake!</li>
</ol>
<p>&nbsp;</p>
<blockquote><p>The @paginate() decorator makes pagination a breeze</p></blockquote>
<p>The @paginate decorator is a wrapper for the paginate module that can be added to your controller methods. Simply specify your data collection&#8217;s name as the first argument of the decorator, and pagination is &#8220;automagically&#8221; setup for you.</p>
<p>An example controller method with pagination would look like this:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># dead-simple pagination in TurboGears 2</span>
&nbsp;
@expose<span style="color: black;">&#40;</span><span style="color: #483d8b;">'myproject.templates.posts'</span><span style="color: black;">&#41;</span>
@paginate<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;posts&quot;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> posts<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
    posts = DBSession.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Posts<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">dict</span><span style="color: black;">&#40;</span>posts=posts<span style="color: black;">&#41;</span></pre></div></div>

<p>Really? That was easy!</p>
<p>The documentation on this decorator seems quite clear to me, so instead of repeating its content here I will simply give you a <a href="http://turbogears.org/2.1/docs/modules/tgdecorators.html#tg.decorators.paginate" target="_blank">link for further reading</a>.</p>
<p><span id="more-556"></span>&nbsp;</p>
<blockquote><p>SQLAlchemy pagination performance wizardry</p></blockquote>
<p>The main issue here is that the paginate module has to figure out its current position in the data collection in order to be able to spit out the right &#8220;chunk&#8221; of data (as well as to provide you with fancy stuff like &#8220;page 3 of 6&#8243;). So, by default when you give it an SQLAlchemy query object, it runs a count() query to figure this out. This results in two practically identical SQL queries (one to get the total count, and the other to return your actual collection of data). While this may not be an issue on simple queries, duplicating complex queries on a high-traffic website could be disastrous.</p>
<p>Remember, magic can be unpredictable, so before we dive into this topic there are a few things you should take into consideration:</p>
<ul>
<li>This technique is not compatible with the @paginate() decorator due to the fact that the current page arg isn&#8217;t passed into the controller method when using the decorator, so be ready to get your hands dirty with the full-blown <em>paginate</em> module.</li>
<li>The SQLAlchemy all() query method results in immediate query execution. This may have undesirable side-effects.</li>
<li>I&#8217;m sure there is a cleaner/sexier way to do this, but for the sake of time and in an effort to be verbose this is what I have come up with so far. Please leave a helpful comment below if your magic is cleaner than mine.</li>
</ul>
<p>Ok, so now that we are familiar with the problem, what is a good solution? Well, a rough hack to work around this issue is to use SQLAlchemy&#8217;s limit() and offset() query methods combined with the all() method to get SQLAlchemy to return a list instead of a query object. This puts <em>paginate</em> to work on the list instead of on the database, avoiding the extra count() query. However, as tricky as this may be, there are still problems:</p>
<ol>
<li>In order for paginate to calculate a correct current position based on the &#8220;page&#8221; it thinks it&#8217;s on, you&#8217;ve got to pad the <strong>beginning</strong> of the list you send to the paginate.Page() method with enough items to accurately reflect the &#8220;current&#8221; size of the data collection.</li>
<li>In order for paginate to know there are more records waiting to be loaded, you&#8217;ve got to pad the <strong>end</strong> of the list you send to the paginate.Page() method with at least one more item than each page&#8217;s specified item limit.</li>
</ol>
<p>This may seem confusing at first, but an example controller method should help clear things up for you:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># import the paginate webhelper</span>
<span style="color: #ff7700;font-weight:bold;">from</span> webhelpers <span style="color: #ff7700;font-weight:bold;">import</span> paginate
<span style="color: #808080; font-style: italic;"># specify the number of items you want per page</span>
items_per_page = <span style="color: #ff4500;">20</span>
&nbsp;
@expose<span style="color: black;">&#40;</span><span style="color: #483d8b;">'myproject.templates.posts'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> posts<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, page=<span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>:
    <span style="color: #808080; font-style: italic;"># specify the limit, plus 1 so the Page() method can tell if extra data is available</span>
    limit = items_per_page + <span style="color: #ff4500;">1</span>
    <span style="color: #808080; font-style: italic;"># specify the offset to start with</span>
    offset = <span style="color: black;">&#40;</span><span style="color: #008000;">int</span><span style="color: black;">&#40;</span>page<span style="color: black;">&#41;</span> - <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span> <span style="color: #66cc66;">*</span> items_per_page
&nbsp;
    <span style="color: #808080; font-style: italic;"># if the offset is greater than 0, we have to pad the beginning of the list</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> offset: posts = <span style="color: black;">&#91;</span>x <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">&#40;</span>offset<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
    <span style="color: #808080; font-style: italic;"># otherwise we're starting at the beginning (with an empty list)</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>: posts = <span style="color: #008000;">list</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># load the DB data into the list</span>
    posts.<span style="color: black;">extend</span><span style="color: black;">&#40;</span>DBSession.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Posts<span style="color: black;">&#41;</span>.<span style="color: black;">limit</span><span style="color: black;">&#40;</span>limit<span style="color: black;">&#41;</span>.<span style="color: black;">offset</span><span style="color: black;">&#40;</span>offset<span style="color: black;">&#41;</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># setup the pagination object</span>
    pagination = paginate.<span style="color: black;">Page</span><span style="color: black;">&#40;</span>posts, page, items_per_page=items_per_page<span style="color: black;">&#41;</span>
    <span style="color: #808080; font-style: italic;"># either use tmpl_context or return the object in the return dict</span>
    tmpl_context.<span style="color: black;">pagination</span> = pagination
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">dict</span><span style="color: black;">&#40;</span>posts=pagination.<span style="color: black;">items</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Voilà! Now that you&#8217;re dealing with a list, pagination should work as expected without the extra &#8220;count()&#8221; query.</p>
<p>&nbsp;</p>
<blockquote><p>Use override_template() for dynamic template rendering</p></blockquote>
<p>AJAX pagination is a common practice and often provides a richer user experience than &#8220;static&#8221; pagination. Fortunately, AJAX pagination is almost as easy to setup as normal pagination, and only requires <a href="http://turbogears.org/2.1/docs/modules/thirdparty/webhelpers_paginate.html#can-i-use-ajax-ajah" target="_blank">a few extra tweaks</a> to an already simple configuration.</p>
<p>The only &#8220;tricky&#8221; thing here is if you&#8217;re accessing the same controller method for partial data as well as the full page. In such a case, you&#8217;ll need to have the controller method render two separate templates depending on if you&#8217;re asking for the &#8220;full&#8221; page, or just the listing of data. Don&#8217;t worry though, <em>paginate</em> automatically provides you with a &#8220;partial&#8221; arg that you can check against, and override_template() allows you to override whatever template you have defined in the @expose() decorator.</p>
<p>Modifying the previous example&#8217;s controller method for this type of functionality looks like this:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># add the override_template import</span>
<span style="color: #ff7700;font-weight:bold;">from</span> tg <span style="color: #ff7700;font-weight:bold;">import</span> override_template
&nbsp;
<span style="color: #808080; font-style: italic;"># add the &quot;partial&quot; arg to the controller method</span>
<span style="color: #ff7700;font-weight:bold;">def</span> posts<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, page=<span style="color: #ff4500;">1</span>, partial=<span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>:
    <span style="color: #808080; font-style: italic;"># ...snip...</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">if</span> partial:
        override_template<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">posts</span>, <span style="color: #483d8b;">'mako:myproject.templates.post_feed'</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">dict</span><span style="color: black;">&#40;</span>posts=pagination.<span style="color: black;">items</span><span style="color: black;">&#41;</span></pre></div></div>

<p>More information on override_template() can be found in <a href="http://turbogears.org/2.1/docs/modules/tgdecorators.html#tg.decorators.override_template" target="_blank">the docs</a>.</p>
<p>&nbsp;</p>
<p>I hope this tutorial has been helpful. I will be submitting a TG docs pull request soon, so please leave comments if you have any suggestions or feedback.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.curiasolutions.com/2009/11/dead-simple-pagination-wizardry-with-turbogears-2/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>The great web technology shootout &#8211; Round 3: Better, Faster, and Shinier</title>
		<link>http://blog.curiasolutions.com/2009/10/the-great-web-technology-shootout-round-3-better-faster-and-shinier/</link>
		<comments>http://blog.curiasolutions.com/2009/10/the-great-web-technology-shootout-round-3-better-faster-and-shinier/#comments</comments>
		<pubDate>Mon, 05 Oct 2009 18:42:52 +0000</pubDate>
		<dc:creator>Seth</dc:creator>
				<category><![CDATA[F/OSS]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[pylons]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[turbogears]]></category>

		<guid isPermaLink="false">http://blog.curiasolutions.com/?p=409</guid>
		<description><![CDATA[[Note: This post is the continuation of a series. Please read Round 1 and Round 2 first if you are just now joining us.]
[Update 10/6: Slight updates to the Pylons test after some input from Ben. Added a couple new charts for you visual folks.]
As I briefly mentioned in Round 1, this whole thing came [...]]]></description>
			<content:encoded><![CDATA[<p>[Note: This post is the continuation of a series. Please read <a href="http://blog.curiasolutions.com/?p=172">Round 1</a> and <a href="http://blog.curiasolutions.com/?p=278">Round 2</a> first if you are just now joining us.]</p>
<p><strong>[Update 10/6: Slight updates to the Pylons test after some input from Ben. Added a couple new charts for you visual folks.]</strong></p>
<p>As I briefly mentioned in <a href="http://blog.curiasolutions.com/?p=172">Round 1</a>, this whole thing came about as an experiment to satisfy my own curiosity. Unfortunately, I wasn&#8217;t expecting these posts to draw the amount of attention they have been getting, and several people informed me of a few &#8220;issues&#8221; with the first round. Since my initial approach to this topic was somewhat casual, I didn&#8217;t really take the time to perform each test in a &#8220;proper scientific fashion.&#8221; Although this was clearly stated in the introduction to round one, it unfortunately resulted in performance estimations that were somewhat less than accurate.</p>
<p>After input from various people much smarter than myself, I quickly went to work tweaking my test environment and building &#8220;proper&#8221; test apps. In the midst of this, a conversation about PHP accelerators prompted me to put PHP under the spotlight, which brought about <a href="http://blog.curiasolutions.com/?p=278">Round 2</a> as an interim round. This gave me a chance to demonstrate the necessity of PHP acceleration, and only continued to solidify my opinion of PHP as an inferior web development language (remember, I just said <em>my opinion</em>).</p>
<p>Which brings us to Round 3. A lot of work has gone into &#8220;doing it right&#8221; this time, so I am fairly confident that these results are a much more accurate representation of each test subject&#8217;s performance estimations. Remember, benchmark test code typically has no real-world value, so &#8220;performance estimations&#8221; are about all I can promise here. <strong>Your mileage <em>will</em> vary.</strong> As a wise person once said:</p>
<blockquote><p>&#8220;All this benchmarking is doing is proving what we already know: More code takes longer to execute.&#8221; &#8211; Ben Bangert (dev lead of Pylons)</p></blockquote>
<p><span id="more-409"></span></p>
<p>&nbsp;</p>
<p>What you should know about Round 3:</p>
<ul>
<li>The hardware/software platform is the same as in <a href="http://blog.curiasolutions.com/?p=172">Round 1</a>.</li>
<li>Python v2.5.4 was used for all the Python tests. Ruby v1.8.6 was used for the Rails test.</li>
<li>Apache v2.2.3 was used, with <a href="http://code.google.com/p/modwsgi/" target="_blank">mod_wsgi</a> v2.5 for the Python tests and <a href="http://www.modrails.com/" target="_blank">Phusion Passenger</a> v2.2.5 for the Rails test.</li>
<li>Note that while the mod_wsgi tests in Round 1 were using &#8220;daemon mode&#8221;, the mod_wsgi tests here are using &#8220;embedded mode&#8221; which is often faster (although <a href="http://blog.dscpl.com.au/2009/03/load-spikes-and-excessive-memory-usage.html" target="_blank">not necessarily the recommended configuration)</a>.
<li>The Python component versions used here were: SQLAlchemy v0.5.6, Genshi v0.5.1, Mako v0.2.5, and Jinja 2.2.1.</li>
<li>ApacheBench was run with -n 10000 -c 10.</li>
<li>Each test was run several times to make sure that there were no anomalies, with the &#8220;optimum average&#8221; chosen as the numbers represented here.</li>
<li>In an attempt to try to make these tests more scientific, I am now including the source code for each test.</li>
</ul>
<p>&nbsp;</p>
<blockquote><h4>Plain WSGI – <a href="http://blog.curiasolutions.com/wp-content/uploads/2009/10/wsgi_test_src.zip">wsgi_test_src.zip</a></h4>
</blockquote>
<p>Let&#8217;s start with a basic &#8220;hello world&#8221; WSGI test to set the baseline:</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">The <span style="color: #ff0000;">'hello world'</span> test<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">WSGIServer</span><span style="color: #66cc66;">/</span><span style="color: #cc66cc;">0.1</span>
Document Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">12</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">1532.76</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
&nbsp;
Apache<span style="color: #66cc66;">/</span>2<span style="color: #66cc66;">.</span>2<span style="color: #66cc66;">.</span>3 <span style="color: #66cc66;">&#40;</span>mod_wsgi<span style="color: #66cc66;">&#41;</span>
Document Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">12</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">5549.73</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>As you can see, mod_wsgi allows Apache to put some serious muscle into Python&#8217;s WSGI. It&#8217;s no wonder that mod_wsgi is quickly becoming the de-facto standard for Apache Python deployment.</p>
<p>&nbsp;</p>
<blockquote><h4>TurboGears v2.0.3 – <a href="http://blog.curiasolutions.com/wp-content/uploads/2009/10/tg2_test_src.zip">tg2_test_src.zip</a></h4>
</blockquote>
<p>As I mentioned in Round 1, <a href="http://www.turbogears.org/" target="_blank">TurboGears</a> has quickly become my web framework of choice. Building a &#8220;best-of-breed&#8221; component stack on top of Pylons <a href="http://percious.com/blog/archives/31" target="_blank">may not be easy</a>, but <a href="http://percious.com/blog/archives/32" target="_blank">when it works</a> I believe it pays off immensely (e.g. Ubuntu).</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">The <span style="color: #ff0000;">'hello world'</span> test<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">12</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">935.13</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
&nbsp;
Genshi template test<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>genshi_hello<span style="color: #66cc66;">/</span>
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">923</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">601.07</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
&nbsp;
Mako template test<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>mako_hello<span style="color: #66cc66;">/</span>
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">932</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">723.18</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
&nbsp;
Jinja2 template test<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>jinja_hello<span style="color: #66cc66;">/</span>
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">937</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">764.35</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
&nbsp;
Database query tests<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>raw_sql<span style="color: #66cc66;">/</span>
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">1270</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">569.71</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
Document Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>genshi_sql<span style="color: #66cc66;">/</span>
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">2409</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">388.96</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
Document Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>mako_sql<span style="color: #66cc66;">/</span>
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">2418</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">515.07</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
Document Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>jinja_sql<span style="color: #66cc66;">/</span>
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">2472</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">535.06</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>&nbsp;</p>
<blockquote><h4>TurboGears v2.1a1 – <a href="http://blog.curiasolutions.com/wp-content/uploads/2009/10/tg21_test_src.zip">tg21_test_src.zip</a></h4>
</blockquote>
<p>The TurboGears community is alive and well, and just before finishing this post the release of v2.1 Alpha 1 was announced. Significant improvements were made to TG&#8217;s <a href="http://percious.com/blog/archives/33" target="_blank">object dispatch</a>, so I was excited to see what the new numbers would look like.</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">The <span style="color: #ff0000;">'hello world'</span> test<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">11</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">1118.92</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
&nbsp;
Genshi template test<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>genshi_hello<span style="color: #66cc66;">/</span>
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">923</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">713.97</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
&nbsp;
Mako template test<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>mako_hello<span style="color: #66cc66;">/</span>
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">932</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">812.64</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
&nbsp;
Jinja2 template test<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>jinja_hello<span style="color: #66cc66;">/</span>
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">937</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">1000.98</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
&nbsp;
Database query tests<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>raw_sql<span style="color: #66cc66;">/</span>
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">1270</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">673.04</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
Document Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>genshi_sql<span style="color: #66cc66;">/</span>
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">2409</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">433.45</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
Document Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>mako_sql<span style="color: #66cc66;">/</span>
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">2418</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">601.89</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
Document Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>jinja_sql<span style="color: #66cc66;">/</span>
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">2472</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">630.66</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>As you can see, the future of TurboGears looks very bright, and I only expect these numbers to get better as the final 2.1 release approaches (expected in Q1 2010).</p>
<p>&nbsp;</p>
<blockquote><h4>Pylons v0.9.7 – <a href="http://blog.curiasolutions.com/wp-content/uploads/2009/10/pylons_test_src.zip">pylons_test_src.zip</a></h4>
</blockquote>
<p><a href="http://www.pylonshq.com/" target="_blank">Pylons</a> is the foundation which TurboGears is built upon, and for those who don&#8217;t need all &#8220;the extras&#8221; it is an excellent choice.</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">The <span style="color: #ff0000;">'hello world'</span> test<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>hello<span style="color: #66cc66;">/</span>index
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">12</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">2593.47</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
&nbsp;
Mako template test<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>hello<span style="color: #66cc66;">/</span>hello
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">932</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">1737.03</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
&nbsp;
Database query tests<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>hello<span style="color: #66cc66;">/</span>raw_sql
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">1270</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">964.07</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
Document Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>hello<span style="color: #66cc66;">/</span>hellodb
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">2418</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">831.26</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>&nbsp;</p>
<blockquote><h4>Django v1.1 – <a href="http://blog.curiasolutions.com/wp-content/uploads/2009/10/django_test_src.zip">django_test_src.zip</a></h4>
</blockquote>
<p>While <a href="http://www.djangoproject.com/" target="_blank">Django</a> is probably Python&#8217;s most popular web framework, I have never really clicked with it. Its way of doing things is significantly different than TG/Pylons, but if you don&#8217;t mind its &#8220;<a href="http://docs.djangoproject.com/en/dev/faq/general/#django-appears-to-be-a-mvc-framework-but-you-call-the-controller-the-view-and-the-view-the-template-how-come-you-don-t-use-the-standard-names" target="_blank">MTV</a>&#8221; architectural pattern and can survive without SQLAlchemy (although there are hacks), it is a very capable framework.</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">The <span style="color: #ff0000;">'hello world'</span> test<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>hello
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">12</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">3376.48</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
&nbsp;
Django template test<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>hellos
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">936</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">1781.43</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
&nbsp;
Database query test<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>hellodb
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">2476</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">972.11</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>&nbsp;</p>
<blockquote><h4>Bottle 0.5.8 – <a href="http://blog.curiasolutions.com/wp-content/uploads/2009/10/bottle_test_src.zip">bottle_test_src.zip</a></h4>
</blockquote>
<p><a href="http://bottle.paws.de/" target="_blank">Bottle</a> caught my attention a few months ago while surveying the WSGI landscape for an alternative to PHP when building websites that only require a few pieces of dynamic functionality. As a &#8220;micro-framework&#8221;, it does an excellent job at bridging the gap between &#8220;pure WSGI&#8221; and its &#8220;feature-rich&#8221; big brothers.</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">The <span style="color: #ff0000;">'hello world'</span> test<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">12</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">5545.96</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
&nbsp;
Mako template test<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>hello
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">923</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">3588.04</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
&nbsp;
Database query test <span style="color: #66cc66;">&#40;</span>SQLAlchemy<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>hellodb
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">2413</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">1479.54</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>As you can see, there was practically no difference in speed between Bottle and pure WSGI in a basic &#8220;hello world&#8221; test. Even with the addition of Mako and SQLAlchemy, Bottle performed significantly faster than a bare Pylons or Django setup.</p>
<p>(Note: Mako template inheritance was not used in the Bottle Mako test.)</p>
<p>&nbsp;</p>
<blockquote><h4>Ruby on Rails v2.3.3 – <a href="http://blog.curiasolutions.com/wp-content/uploads/2009/10/rails_test_src.zip">rails_test_src.zip</a></h4>
</blockquote>
<p>What web technology shootout would be complete without <a href="http://www.rubyonrails.org/" target="_blank">Rails</a>? Although I am not a Rails fan (mostly because I can&#8217;t stand Ruby), I believe that in many ways we owe much of the popularity and success of the web framework movement to Rails.</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">The <span style="color: #ff0000;">'hello world'</span> test<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>hello<span style="color: #66cc66;">/</span>hello
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">12</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">1048.18</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
&nbsp;
Rails template test<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>hello<span style="color: #66cc66;">/</span>hellos
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">937</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">949.54</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span>
&nbsp;
&nbsp;
Database query test<span style="color: #66cc66;">:</span>
&nbsp;
<span style="color: #006600;">Document</span> Path<span style="color: #66cc66;">:</span>          <span style="color: #66cc66;">/</span>hello<span style="color: #66cc66;">/</span>hellodb
Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">2482</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">734.18</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>(Note: I think my version of Ruby was using a newer version of the SQLite3 library than my version of Python. This may or may not have given Rails an advantage on the database query test.)</p>
<p>&nbsp;</p>
<p>For those of you who like charts, here&#8217;s a comparison of the &#8220;return hello world&#8221; test results for each framework:</p>
<div id="attachment_531" class="wp-caption aligncenter" style="width: 566px"><a href="http://blog.curiasolutions.com/wp-content/uploads/2009/10/hello_world_test.png"><img src="http://blog.curiasolutions.com/wp-content/uploads/2009/10/hello_world_test.png" alt="The return hello world test chart" title="Hello World Test" width="556" height="352" class="size-full wp-image-531" /></a><p class="wp-caption-text">The return hello world test chart</p></div>
<p>And here&#8217;s a comparison of the template test:</p>
<div id="attachment_532" class="wp-caption aligncenter" style="width: 566px"><a href="http://blog.curiasolutions.com/wp-content/uploads/2009/10/template_test.png"><img src="http://blog.curiasolutions.com/wp-content/uploads/2009/10/template_test.png" alt="The template test chart" title="Template Test" width="556" height="263" class="size-full wp-image-532" /></a><p class="wp-caption-text">The template test chart</p></div>
<p>Finally, here&#8217;s the template test with the database query added:</p>
<div id="attachment_530" class="wp-caption aligncenter" style="width: 566px"><a href="http://blog.curiasolutions.com/wp-content/uploads/2009/10/database_test.png"><img src="http://blog.curiasolutions.com/wp-content/uploads/2009/10/database_test.png" alt="Template test with database query" title="Database Query" width="556" height="264" class="size-full wp-image-530" /></a><p class="wp-caption-text">Template test with database query</p></div>
<p>&nbsp;</p>
<h4>Closing thoughts</h4>
<ul>
<li>Gone are the days of Rails as &#8220;the one framework to rule them all.&#8221; C&#8217;mon guys, it&#8217;s not 2005 anymore.</li>
<li>Python has gotten a lot of flak from other language camps for its abundance of web frameworks. While some Pythonists have been embarrassed by this, I only see it as proof of Python&#8217;s success as a language. My retort would be: &#8220;If it was as easy to write a web framework in your language as it is in Python, you&#8217;d have the same problem!&#8221;</li>
</ul>
<p>&nbsp;</p>
<h5>Coming soon: &#8220;Round 4: Micro-frameworks &mdash; Quick &#038; Dirty, and leaving PHP in the dust!&#8221;</h5>
<p>(Note: Please don&#8217;t leave benchmarking requests as comments. If you&#8217;d like me to take a look at your favorite framework, direct message me on twitter. Thanks!)</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.curiasolutions.com/2009/10/the-great-web-technology-shootout-round-3-better-faster-and-shinier/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>The great web technology shootout &#8211; Round 1: A quick glance at the landscape</title>
		<link>http://blog.curiasolutions.com/2009/09/the-great-web-development-shootout/</link>
		<comments>http://blog.curiasolutions.com/2009/09/the-great-web-development-shootout/#comments</comments>
		<pubDate>Sun, 13 Sep 2009 05:29:28 +0000</pubDate>
		<dc:creator>Seth</dc:creator>
				<category><![CDATA[F/OSS]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[turbogears]]></category>

		<guid isPermaLink="false">http://blog.curiasolutions.com/?p=172</guid>
		<description><![CDATA[[Update: Some of the results here ended up being less than accurate. Please see Round 3 for an explanation (and updated tests).]
Recently I went on a benchmarking spree and decided to throw ApacheBench at a bunch of the different web development technology platforms I interact with on a day-to-day basis. The results were interesting enough [...]]]></description>
			<content:encoded><![CDATA[<p><strong>[Update: Some of the results here ended up being less than accurate. Please see <a href="http://blog.curiasolutions.com/?p=409">Round 3</a> for an explanation (and updated tests).]</strong></p>
<p>Recently I went on a benchmarking spree and decided to throw <a href="http://en.wikipedia.org/wiki/ApacheBench" target="_blank">ApacheBench</a> at a bunch of the different web development technology platforms I interact with on a day-to-day basis. The results were interesting enough to me that I decided I&#8217;d take a post to share them here.</p>
<p><strong>Disclaimer: The following test results should be taken with a <em>massive</em> grain of salt.</strong> If you know anything about benchmarking, you will know that the slightest adjustments have the potential to change things drastically. While I have tried to perform each test as fairly and accurately as possible, it would be foolish to consider these results as scientific in any way. <strong>It should also be noted that my goal here was not to see how fast each technology performs at its most optimized configuration, but rather what a minimal out-of-the-box experience looks like.</strong></p>
<p>Test platform info:</p>
<ul>
<li>The hardware was an Intel Core2Quad Q9300, 2.5Ghz, 6MB Cache, 1333FSB, 2GB DDR RAM.</li>
<li>The OS was CentOS v5.3 32-bit with a standard Apache Webserver setup.</li>
<li> ApacheBench was used with only the -n and -c flags (1000 requests for the PHP frameworks, 5000 requests for everything else).	</li>
<li> Each ApacheBench test was run 5-10 times, with the &#8220;optimum average&#8221; chosen as the numbers represented here.</li>
<li>The PHP tests were done using the standard Apache PHP module.</li>
<li>The mod_wsgi tests were done in daemon mode set to 2 processes/15 threads.</li>
<li>The SQL tests were done with mysqli ($mysql->query()) on PHP, and SQLAlchemy (conn.execute()) on Python fetching and printing 5 rows of data from a sample database.</li>
</ul>
<p>&nbsp;</p>
<h4>Apache v2.2.3</h4>
<p>We will start with the raw Apache benchmark.</p>
<p>For this test, Apache loaded a simple HTML file with random text:</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">6537</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">8356.23</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>As expected, Apache is lightning fast.</p>
<p>Ok, so now that we&#8217;ve set the high water mark, let&#8217;s take a look at some popular web technology platforms&#8230;<span id="more-172"></span></p>
<p>&nbsp;</p>
<h4>PHP v5.2.8</h4>
<p>For this test, I created a PHP file that simply printed the $_SERVER array and some random text:</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">6135</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">2812.22</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>Next, I decided to add some mysqli() code to connect to a database and print out a few rows of data:</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">283</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">2135.46</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>Not at all bad, but who really wants to build a webapp using plain old PHP anymore?</p>
<p>Next I decided to test the two PHP frameworks that I am most familiar with.</p>
<p>&nbsp;</p>
<h4>CakePHP v1.2.5</h4>
<p>For this test, I did a basic CakePHP install, and added a route that printed &#8220;hello world&#8221; from a template:</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">944</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">42.00</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>As you can see, frameworks can be a blessing and a curse. On a side note, CakePHP was the only test that gave me failed requests.</p>
<h4>Kohana v2.3.4</h4>
<p>For this test, I did a basic install and loaded Kohana&#8217;s default welcome/index setup (which seemed lightweight enough to me to qualify as the test focal point):</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">1987</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">100.11</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>Kohana fared much better than CakePHP, but it appears as though PHP&#8217;s speed breaks down quickly once you start building a framework around it.</p>
<p>&nbsp;</p>
<h4>Python v2.5.4</h4>
<p>Unlike PHP, Python wasn&#8217;t specifically created for the web. However, several years ago the <a href="http://en.wikipedia.org/wiki/Web_Server_Gateway_Interface" target="_blank">WSGI spec</a> was introduced which makes building web applications with Python a breeze. There are many different ways to depoly a WSGI app, but my tests were limited to Python&#8217;s built-in WSGIServer, and Apache&#8217;s <a href="http://code.google.com/p/modwsgi/" target="_blank">mod_wsgi</a>.</p>
<h4>WSGIServer</h4>
<p>For this test, I used WSGI.org&#8217;s <a href="http://hg.moinmo.in/moin/1.8/raw-file/tip/wiki/server/test.wsgi" target="_blank">test.wsgi</a>:</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">4123</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">1658.35</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>These results were quite impressive to me, considering that this is pure Python at work.</p>
<h4>mod_wsgi</h4>
<p>mod_wsgi is Apache&#8217;s module to process WSGI scripts. This test used the exact same script as the previous WSGIServer example:</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">9552</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">3994.30</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>Don&#8217;t look now, but those results are better than the vanilla PHP test!</p>
<p>Next, I added SQLAlchemy imports to the test file and did a conn.execute(), in an attempt to try to mimic the PHP mysqli() test:</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">10212</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">3379.87</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>I expected to lose quite a bit of speed with the introduction of an ORM, but it looks like SQLAlchemy is quite fast. These results were still over 1,000reqs/s more than the PHP example.</p>
<h4>TornadoServer</h4>
<p>There&#8217;s been a lot of buzz lately about the new <a href="http://www.tornadoweb.org/" target="_blank">TornadoServer</a>, so I thought I&#8217;d throw it in the mix. This test uses their basic &#8220;hello world&#8221; example:</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">12</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">3330.00</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>Nothing shocking here at first glance, but the Tornado is built to handle extreme loads. So, I increased the concurrency to 1,000 and threw 10,000 requests at it:</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">12</span> bytes
Concurrency Level<span style="color: #66cc66;">:</span>      <span style="color: #cc66cc;">1000</span>
<span style="color: #0000ff;">COMPLETE</span> requests<span style="color: #66cc66;">:</span>      <span style="color: #cc66cc;">10000</span>
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">3288.15</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>Almost exactly the same results! On a side note, none of the other tests subjects were able to handle a concurrency of 1,000.</p>
<p>&nbsp;</p>
<h4>TurboGears v2.0.3 (mod_wsgi)</h4>
<p><a href="http://www.turbogears.com/" target="_blank">TurboGears</a> has quickly become my framework of choice, so naturally it was the first Python framework I tested.</p>
<p>For this test I setup a default quickstart project, and then added a hello() controller method which simply returned a &#8220;hello world&#8221; string:</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">11</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">470.18</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>Next, I added a simple Genshi template with 1 level of inheritance:</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">1351</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">143.26</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>While those results are not terrible, they should encourage you to check out the other templating options.</p>
<p>Next, I performed the same test with Mako:</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">1534</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">394.41</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>As you can see, Mako is significantly faster than Genshi.</p>
<p>TurboGears also supports Jinja2, so I thought I&#8217;d also see how it held up:</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">1020</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">401.04</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>I was not expecting Jinja2 to be faster than Mako, so I am not entirely sure about these results. However, the difference is only slight, and might have something to do with the fact that the dotted template names finder is disabled for Jinja templates.</p>
<h4>Pylons v0.9.7 (mod_wsgi)</h4>
<p>Mark Ramm (the current development lead of TurboGears) has been quoted as saying &#8220;TurboGears is to Pylons as Ubuntu is to Debian.&#8221; With that in mind, I naturally wanted to see how much speed I was losing with TurboGears&#8217; &#8220;additions&#8221;.</p>
<p>For this test I tried to mimic the first TurboGears test (basic layout, returning a string):</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">11</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">1961.00</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>While I was expecting a gain from TurboGears&#8217; omissions, I wasn&#8217;t quite expecting over four times the increase. I can only assume that the bare Pylons setup is pretty minimal, while TurboGears bootstraps a lot of stuff on for you by default.</p>
<p>Duplicating the Mako test in Pylons was also quite fast:</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">9743</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">1183.39</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<h4>Bottle v0.5.7 (mod_wsgi)</h4>
<p><a href="http://bottle.paws.de/" target="_blank">Bottle</a> caught my attention recently as a minimalistic Python web framework, so I thought I&#8217;d put it through the grind as well.</p>
<p>For this test, I used their simple &#8220;hello world&#8221; example:</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">12</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">3912.43</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>As you can see, Bottle is very close to bare WSGI as far as speed goes. On a side note, adding a sample template using Bottle&#8217;s default templating package didn&#8217;t seem to change these numbers at all.</p>
<p>Next I tested Bottle with its templating system switched to Mako:</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">41</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">3134.61</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>Then I added a SQLAlchemy and performed a query:</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">9374</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">2503.27</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>What was exciting to me about this test was that I found myself looking at a minimal Python framework equipped with a full templating system and ORM which all performed faster than the PHP+mysqli() test.</p>
<h4>Django v1.1 (mod_wsgi)</h4>
<p>I don&#8217;t use <a href="http://www.djangoproject.com/" target="_blank">Django</a>, but I thought it would be worth including in this report. This is a basic Django setup printing out a simple hello world template:</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">1471</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">564.41</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>As expected, a minimal Django setup is quite fast. (Note: This test included a SQLite database query.)</p>
<p>&nbsp;</p>
<h4>Ruby On Rails v2.2.3 (mod_passenger)</h4>
<p>I also don&#8217;t use <a href="http://rubyonrails.org/" target="_blank">Rails</a>, but I figured this report would be incomplete without Rails on the list. So, I took <a href="http://gems.mediatemple.net/testapp_rails.tgz">MediaTemple&#8217;s Rails test app</a> and loaded up a test page:</p>

<div class="wp_syntax"><div class="code"><pre class="progress" style="font-family:monospace;">Document Length<span style="color: #66cc66;">:</span>        <span style="color: #cc66cc;">1291</span> bytes
Requests per second<span style="color: #66cc66;">:</span>    <span style="color: #cc66cc;">433.57</span> <span style="color: #66cc66;">&#91;</span>#<span style="color: #66cc66;">/</span>sec<span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#40;</span>mean<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>Note that the test app used here is doing a little bit more than just returning &#8220;Hello World&#8221;.</p>
<p>&nbsp;</p>
<h4>My Conclusions</h4>
<p>As I said in the disclaimer, to arrive at any sort of decisive conclusions based on these numbers alone would be foolish. However, here is what all of this left me thinking:</p>
<ul>
<li>All of these technologies performed adequately for use with most projects.</li>
<li>CakePHP was the only test that produced less than 100 requests per second. However, there are many articles available on optimizing CakePHP.</li>
<li>These results made me wish I had moved away from PHP much earlier in my web development carrier. There are just better options these days when it comes to choosing a web development language (imho). If you <em>must</em> use PHP for a large project, I strongly suggest that you look into <a href="http://en.wikipedia.org/wiki/PHP_accelerator" target="_blank">an accelerator</a>.</li>
<li>The Bottle+Mako+SQLAlchemy results struck me as an extremely minimal drop-in replacement for simple PHP sites.</li>
<li>mod_wsgi is a huge winner in deploying Python webapps. Way to go Graham!</li>
<li>Do your own bencharking, because <em>your mileage <strong>will</strong> vary</em>!</li>
</ul>
<p>&nbsp;</p>
<h5>Like what you&#8217;ve seen here? Then why not check out <a href="http://blog.curiasolutions.com/?p=278">Round 2</a>?</h5>
<p>(Note: Please don&#8217;t leave benchmarking requests as comments. If you&#8217;d like me to take a look at your favorite framework, direct message me on twitter. Thanks!)</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.curiasolutions.com/2009/09/the-great-web-development-shootout/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>How to use multiple databases in TurboGears 2.0</title>
		<link>http://blog.curiasolutions.com/2009/07/multiple-databases-in-turbogears-2/</link>
		<comments>http://blog.curiasolutions.com/2009/07/multiple-databases-in-turbogears-2/#comments</comments>
		<pubDate>Fri, 31 Jul 2009 01:46:08 +0000</pubDate>
		<dc:creator>Seth</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[turbogears]]></category>

		<guid isPermaLink="false">http://blog.curiasolutions.com/?p=87</guid>
		<description><![CDATA[I recently had to setup a special marketing web-portal for a client of mine that would collect some basic information and throw it into a database to be retrieved later. Since I&#8217;ve already got most of the client&#8217;s web-stuff on TG2 (and in an effort to keep things DRY), I decided I&#8217;d just add a [...]]]></description>
			<content:encoded><![CDATA[<p>I recently had to setup a special marketing web-portal for a client of mine that would collect some basic information and throw it into a database to be retrieved later. Since I&#8217;ve already got most of the client&#8217;s web-stuff on TG2 (and in an effort to keep things DRY), I decided I&#8217;d just add a controller for the new pages to TG and use some mod_proxy kung-fu to make it look like it all lived autonomously. Easy enough, right?</p>
<p>Well, about halfway through this process I decided I wanted to have the collected information dump into its own SQLite DB, keeping it safely away from the rest of my client&#8217;s data. I had heard that setting up multiple databases in TG2 was supposed to be easy, and with some help from Google I soon ran across <a href="http://groups.google.com/group/turbogears/browse_thread/thread/70c14ac308563af5" target="_blank">this thread on the ML</a>. In it, Chris supplies some <a href="http://groups.google.com/group/turbogears/msg/dec19abfaa503bbc" target="_blank">very helpful example code</a> which Mike subsequently posted on his blog as <a href="http://www.blog.pythonlibrary.org/?p=210" target="_blank">a nice tutorial</a>. However, neither of these resources were exhaustive enough to achieve what I was looking for without a bit of &#8220;stumbling around&#8221;, so in an effort to be overly verbose (and perhaps unnecessarily repetitive) I&#8217;ve decided to post what I hope will be a more comprehensive run-down of how to accomplish this task.</p>
<p>Disclaimer: I am in no way a TurboGears or SQLAlchemy expert. There&#8217;s probably an easier/better way to do this, but since there&#8217;s no &#8220;official&#8221; TurboGears tutorial on this topic yet I&#8217;m afraid this is the best method I&#8217;ve found so far. If anyone reading this knows a better way to implement this kind of thing, please leave a comment and I will update this post as the suggestions come in.</p>
<blockquote><p>Step 1: Define your database urls in the [app:main] section of your .ini file(s)</p></blockquote>
<p>This is where the magic begins. Instead of one simple <em>sqlalchemy.url =</em> assignment, you&#8217;ve got to create assignments for each of the databases you want to use:<span id="more-87"></span></p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># in myapp/development.ini (or production.ini, or whatever.ini you are using)</span>
&nbsp;
<span style="color: #808080; font-style: italic;">#sqlalchemy.url = sqlite:///%(here)s/devdata.db</span>
sqlalchemy.<span style="color: black;">first</span>.<span style="color: black;">url</span> = sqlite:///<span style="color: #66cc66;">%</span><span style="color: black;">&#40;</span>here<span style="color: black;">&#41;</span>s/database_1.<span style="color: black;">db</span>
sqlalchemy.<span style="color: black;">second</span>.<span style="color: black;">url</span> = sqlite:///<span style="color: #66cc66;">%</span><span style="color: black;">&#40;</span>here<span style="color: black;">&#41;</span>s/database_2.<span style="color: black;">db</span></pre></div></div>

<blockquote><p>Step 2: Change the way your app loads the databases</p></blockquote>
<p>Now we need to instruct the app to load the multiple databases correctly. This requires telling base_config (in app_cfg.py) to load our own custom AppConfig with the proper multi-db assignments and a call to the model&#8217;s init_model method (more on that in Step 3):</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># in myapp/config/app_cfg.py</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># make sure these imports are added to the top</span>
<span style="color: #ff7700;font-weight:bold;">from</span> tg.<span style="color: black;">configuration</span> <span style="color: #ff7700;font-weight:bold;">import</span> AppConfig, config
<span style="color: #ff7700;font-weight:bold;">from</span> pylons <span style="color: #ff7700;font-weight:bold;">import</span> config <span style="color: #ff7700;font-weight:bold;">as</span> pylons_config
<span style="color: #ff7700;font-weight:bold;">from</span> myapp.<span style="color: black;">model</span> <span style="color: #ff7700;font-weight:bold;">import</span> init_model
&nbsp;
<span style="color: #808080; font-style: italic;"># add this before base_config =</span>
<span style="color: #ff7700;font-weight:bold;">class</span> MultiDBAppConfig<span style="color: black;">&#40;</span>AppConfig<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> setup_sqlalchemy<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot;Setup SQLAlchemy database engine(s)&quot;&quot;&quot;</span>
        <span style="color: #ff7700;font-weight:bold;">from</span> sqlalchemy <span style="color: #ff7700;font-weight:bold;">import</span> engine_from_config
        engine1 = engine_from_config<span style="color: black;">&#40;</span>pylons_config, <span style="color: #483d8b;">'sqlalchemy.first.'</span><span style="color: black;">&#41;</span>
        engine2 = engine_from_config<span style="color: black;">&#40;</span>pylons_config, <span style="color: #483d8b;">'sqlalchemy.second.'</span><span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># engine1 should be assigned to sa_engine as well as your first engine's name</span>
        config<span style="color: black;">&#91;</span><span style="color: #483d8b;">'pylons.app_globals'</span><span style="color: black;">&#93;</span>.<span style="color: black;">sa_engine</span> = engine1
        config<span style="color: black;">&#91;</span><span style="color: #483d8b;">'pylons.app_globals'</span><span style="color: black;">&#93;</span>.<span style="color: black;">sa_engine_first</span> = engine1
        config<span style="color: black;">&#91;</span><span style="color: #483d8b;">'pylons.app_globals'</span><span style="color: black;">&#93;</span>.<span style="color: black;">sa_engine_second</span> = engine2
        <span style="color: #808080; font-style: italic;"># Pass the engine to init_model, to be able to introspect tables</span>
        init_model<span style="color: black;">&#40;</span>engine1, engine2<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;">#base_config = AppConfig()</span>
base_config = MultiDBAppConfig<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<blockquote><p>Step 3: Update your model&#8217;s __init__ to handle multiple sessions and metadata</p></blockquote>
<p>Switching the model&#8217;s init from a single-db config to a multi-db simply means we have to duplicate our DBSession and metata assignments, and then update the init_model method to assign/configure each engine correctly:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># in myapp/model/__init__.py</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># after the first maker/DBSession assignment, add a 2nd one</span>
maker2 = sessionmaker<span style="color: black;">&#40;</span>autoflush=<span style="color: #008000;">True</span>, autocommit=<span style="color: #008000;">False</span>,
                     extension=ZopeTransactionExtension<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
DBSession2 = scoped_session<span style="color: black;">&#40;</span>maker2<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># after the first DeclarativeBase assignment, add a 2nd one</span>
DeclarativeBase2 = declarative_base<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># uncomment the metadata2 line and assign it to DeclarativeBase2.metadata</span>
metadata2 = DeclarativeBase2.<span style="color: black;">metadata</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># finally, modify the init_model method to allow both engines to be passed (see step 2) and</span>
<span style="color: #808080; font-style: italic;"># assign the sessions and metadata to each engine</span>
<span style="color: #ff7700;font-weight:bold;">def</span> init_model<span style="color: black;">&#40;</span>engine1, engine2<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Call me before using any of the tables or classes in the model.&quot;&quot;&quot;</span>
&nbsp;
<span style="color: #808080; font-style: italic;">#    DBSession.configure(bind=engine)</span>
    DBSession.<span style="color: black;">configure</span><span style="color: black;">&#40;</span>bind=engine1<span style="color: black;">&#41;</span>
    DBSession2.<span style="color: black;">configure</span><span style="color: black;">&#40;</span>bind=engine2<span style="color: black;">&#41;</span>
&nbsp;
    metadata.<span style="color: black;">bind</span> = engine1
    metadata2.<span style="color: black;">bind</span> = engine2</pre></div></div>

<blockquote><p>Step 4: Have your model classes inherit from either DeclarativeBase or DeclarativeBase2, and use DBSession or DBSession2 as your database session handler</p></blockquote>
<p>Now that the configuration has all been taken care of, you can instruct your models to use either the first or second DeclarativeBase and DBSession based on what DB engine you want it to access:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># myapp/model/spam.py (uses engine1)</span>
<span style="color: #ff7700;font-weight:bold;">from</span> sqlalchemy <span style="color: #ff7700;font-weight:bold;">import</span> Table, ForeignKey, Column
<span style="color: #ff7700;font-weight:bold;">from</span> sqlalchemy.<span style="color: #dc143c;">types</span> <span style="color: #ff7700;font-weight:bold;">import</span> Integer, Unicode, Boolean
<span style="color: #ff7700;font-weight:bold;">from</span> myapp.<span style="color: black;">model</span> <span style="color: #ff7700;font-weight:bold;">import</span> DeclarativeBase
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> Spam<span style="color: black;">&#40;</span>DeclarativeBase<span style="color: black;">&#41;</span>:
    __tablename__ = <span style="color: #483d8b;">'spam'</span>
&nbsp;
        <span style="color: #008000;">self</span>.<span style="color: #008000;">id</span> = <span style="color: #008000;">id</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">variety</span> = variety
&nbsp;
    <span style="color: #008000;">id</span> = Column<span style="color: black;">&#40;</span>Integer, autoincrement=<span style="color: #008000;">True</span>, primary_key=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
    variety = Column<span style="color: black;">&#40;</span>Unicode<span style="color: black;">&#40;</span><span style="color: #ff4500;">50</span><span style="color: black;">&#41;</span>, nullable=<span style="color: #008000;">False</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;">#---------------------------------------------------------------------#</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># myapp/model/eggs.py (uses engine2)</span>
<span style="color: #ff7700;font-weight:bold;">from</span> sqlalchemy <span style="color: #ff7700;font-weight:bold;">import</span> Table, ForeignKey, Column
<span style="color: #ff7700;font-weight:bold;">from</span> sqlalchemy.<span style="color: #dc143c;">types</span> <span style="color: #ff7700;font-weight:bold;">import</span> Integer, Unicode, Boolean
<span style="color: #ff7700;font-weight:bold;">from</span> myapp.<span style="color: black;">model</span> <span style="color: #ff7700;font-weight:bold;">import</span> DeclarativeBase2
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> Eggs<span style="color: black;">&#40;</span>DeclarativeBase2<span style="color: black;">&#41;</span>:
    __tablename__ = <span style="color: #483d8b;">'eggs'</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, <span style="color: #008000;">id</span>, pkg_qty<span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.<span style="color: #008000;">id</span> = <span style="color: #008000;">id</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">pkg_qty</span> = pkg_qty
&nbsp;
    <span style="color: #008000;">id</span> = Column<span style="color: black;">&#40;</span>Integer, autoincrement=<span style="color: #008000;">True</span>, primary_key=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
    pkg_qty = Column<span style="color: black;">&#40;</span>Integer, default=<span style="color: #ff4500;">12</span><span style="color: black;">&#41;</span></pre></div></div>

<blockquote><p>Optional: Create and populate each database in websetup.py</p></blockquote>
<p>If you want your setup_app method to populate each database with data, simply use the appropriate metadata/DBSession objects as you would in a single-db setup:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># in myapp/websetup.py</span>
<span style="color: #ff7700;font-weight:bold;">def</span> setup_app<span style="color: black;">&#40;</span>command, conf, <span style="color: #008000;">vars</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Place any commands to setup myapp here&quot;&quot;&quot;</span>
    load_environment<span style="color: black;">&#40;</span>conf.<span style="color: black;">global_conf</span>, conf.<span style="color: black;">local_conf</span><span style="color: black;">&#41;</span>
    <span style="color: #808080; font-style: italic;"># Load the models</span>
    <span style="color: #ff7700;font-weight:bold;">from</span> myapp <span style="color: #ff7700;font-weight:bold;">import</span> model
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;Creating tables for engine1&quot;</span>
    model.<span style="color: black;">metadata</span>.<span style="color: black;">create_all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;Creating tables for engine2&quot;</span>
    model.<span style="color: black;">metadata2</span>.<span style="color: black;">create_all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># populate spam table</span>
    spam = <span style="color: black;">&#91;</span>
        model.<span style="color: black;">Spam</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span>, u<span style="color: #483d8b;">'Classic'</span><span style="color: black;">&#41;</span>,
        model.<span style="color: black;">Spam</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">2</span>, u<span style="color: #483d8b;">'Golden Honey Grail'</span><span style="color: black;">&#41;</span>,
    <span style="color: black;">&#93;</span>
    <span style="color: #808080; font-style: italic;"># DBSession is bound to the spam table</span>
    model.<span style="color: black;">DBSession</span>.<span style="color: black;">add_all</span><span style="color: black;">&#40;</span>spam<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># populate eggs table</span>
    eggs = <span style="color: black;">&#91;</span>
        model.<span style="color: black;">Eggs</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">12</span><span style="color: black;">&#41;</span>,
        model.<span style="color: black;">Eggs</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">2</span>, <span style="color: #ff4500;">6</span><span style="color: black;">&#41;</span>,
    <span style="color: black;">&#93;</span>
    <span style="color: #808080; font-style: italic;"># DBSession2 is bound to the eggs table</span>
    model.<span style="color: black;">DBSession2</span>.<span style="color: black;">add_all</span><span style="color: black;">&#40;</span>eggs<span style="color: black;">&#41;</span>
&nbsp;
    model.<span style="color: black;">DBSession</span>.<span style="color: black;">flush</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    model.<span style="color: black;">DBSession2</span>.<span style="color: black;">flush</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    transaction.<span style="color: black;">commit</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;Successfully setup&quot;</span></pre></div></div>

<p>Hope this helps! If you have any questions or suggestions please leave a comment below.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.curiasolutions.com/2009/07/multiple-databases-in-turbogears-2/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Making TurboGears and Authorize.net play nice together</title>
		<link>http://blog.curiasolutions.com/2009/07/making-turbogears-and-authorize-net-play-nice-together/</link>
		<comments>http://blog.curiasolutions.com/2009/07/making-turbogears-and-authorize-net-play-nice-together/#comments</comments>
		<pubDate>Sat, 18 Jul 2009 19:18:49 +0000</pubDate>
		<dc:creator>Seth</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[turbogears]]></category>

		<guid isPermaLink="false">http://blog.curiasolutions.com/?p=40</guid>
		<description><![CDATA[I recently had to wire up an Authorize.net form for a TurboGears project I&#8217;m building, and since I&#8217;m a bit new to custom form validation in TG I had quite a bit of trouble figuring out the &#8220;right&#8221; way to do it.
The goal was to get the form to go through two layers of validation [...]]]></description>
			<content:encoded><![CDATA[<p>I recently had to wire up an Authorize.net form for a TurboGears project I&#8217;m building, and since I&#8217;m a bit new to custom form validation in TG I had quite a bit of trouble figuring out the &#8220;right&#8221; way to do it.</p>
<p>The goal was to get the form to go through two layers of validation before passing:</p>
<ol>
<li>Use the validation packages provided by TG (<a href="http://toscawidgets.org/" target="_blank">tw.forms</a> and <a href="http://www.formencode.org/" target="_blank">formencode</a>)</li>
<li>If the first layer of validation passes, try to run the authorize.net charge. If this returns a response code of 1 (approved) then all validation has passed. Otherwise, invalidate the form and flash the authorize.net error.</li>
</ol>
<p>tw.forms and formencode are awesome packages, but at first glance there didn&#8217;t seem to be &#8220;one obvious way&#8221; to do things. I found the documentation and tutorials to be scant at best, and eventually went to the <a href="http://groups.google.com/group/turbogears" target="_blank">TurboGears mailing list</a> for help. In spite of the fact that I&#8217;d probably give TurboGears a &#8220;C&#8221; <em>at best</em> for its documentation (not to mention the availability of &#8220;verbose&#8221; tutorials), the folks on the mailing list and IRC channel (when it&#8217;s actually active) seem to be quite helpful.</p>
<p>Eventually (and with additional help from Google&#8217;s <a href="http://www.google.com/codesearch" target="_blank">source code search</a>) I was able to hack together something that worked as planned, and was overjoyed to observe chained validators in action. Below is some example code provided for your hacking pleasure. To download the full example file, see the links at the bottom of the post.<span id="more-40"></span></p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># FancyValidator to process the Credit Card using the authorize package</span>
<span style="color: #ff7700;font-weight:bold;">class</span> ProcessCard<span style="color: black;">&#40;</span>validators.<span style="color: black;">FancyValidator</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> _to_python<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, value, state<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">from</span> authorize <span style="color: #ff7700;font-weight:bold;">import</span> aim <span style="color: #ff7700;font-weight:bold;">as</span> aim_api
&nbsp;
        <span style="color: #808080; font-style: italic;"># Setup the aim Api object.</span>
        aim = aim_api.<span style="color: black;">Api</span><span style="color: black;">&#40;</span>AUTHNET_LOGIN, AUTHNET_KEY, is_test=<span style="color: #008000;">False</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;"># Create a transaction against a credit card</span>
        result_dict = aim.<span style="color: black;">transaction</span><span style="color: black;">&#40;</span>
            amount=u<span style="color: #483d8b;">&quot;16.00&quot;</span>,
            card_num=<span style="color: #008000;">unicode</span><span style="color: black;">&#40;</span>value<span style="color: black;">&#91;</span><span style="color: #483d8b;">'card_number'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>,
            exp_date=<span style="color: #008000;">unicode</span><span style="color: black;">&#40;</span>value<span style="color: black;">&#91;</span><span style="color: #483d8b;">'card_expiry'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>,
            <span style="color: #808080; font-style: italic;"># ...and others...</span>
            <span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">if</span> result_dict<span style="color: black;">&#91;</span><span style="color: #483d8b;">'code'</span><span style="color: black;">&#93;</span> == <span style="color: #483d8b;">'1'</span>:
            <span style="color: #808080; font-style: italic;"># success</span>
            <span style="color: #ff7700;font-weight:bold;">return</span> value
        <span style="color: #ff7700;font-weight:bold;">else</span>:
            <span style="color: #808080; font-style: italic;"># failure</span>
            <span style="color: #ff7700;font-weight:bold;">raise</span> validators.<span style="color: black;">Invalid</span><span style="color: black;">&#40;</span>result_dict<span style="color: black;">&#91;</span><span style="color: #483d8b;">'reason_text'</span><span style="color: black;">&#93;</span>, value, state<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> AuthnetForm<span style="color: black;">&#40;</span>TableForm<span style="color: black;">&#41;</span>:
    submit_text=<span style="color: #483d8b;">'Process Card'</span>
&nbsp;
    validator = validators.<span style="color: black;">Schema</span><span style="color: black;">&#40;</span>
        chained_validators = <span style="color: black;">&#91;</span>
            validators.<span style="color: black;">CreditCardValidator</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'card_type'</span>,<span style="color: #483d8b;">'card_number'</span><span style="color: black;">&#41;</span>,
            validators.<span style="color: black;">CreditCardSecurityCode</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'card_type'</span>,<span style="color: #483d8b;">'card_cvv'</span><span style="color: black;">&#41;</span>,
            <span style="color: #808080; font-style: italic;"># you could also add an expiry validator, but authnet will handle this for you</span>
            ProcessCard<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: black;">&#93;</span>
    <span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">class</span> fields<span style="color: black;">&#40;</span>WidgetsList<span style="color: black;">&#41;</span>:
        name = TextField<span style="color: black;">&#40;</span>validator=validators.<span style="color: black;">String</span><span style="color: black;">&#40;</span>not_empty=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># ...and others like address, city, state, zip...</span>
        spacer = Spacer<span style="color: black;">&#40;</span>suppress_label=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
        card_type = SingleSelectField<span style="color: black;">&#40;</span>options=<span style="color: black;">&#91;</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'visa'</span>, <span style="color: #483d8b;">'Visa'</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">'mastercard'</span>, <span style="color: #483d8b;">'Master Card'</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">'discover'</span>, <span style="color: #483d8b;">'Discover'</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">'amex'</span>, <span style="color: #483d8b;">'American Express'</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>, validator=validators.<span style="color: black;">NotEmpty</span><span style="color: black;">&#41;</span>
        card_expiry = CalendarDatePicker<span style="color: black;">&#40;</span>date_format=<span style="color: #483d8b;">&quot;%m/%Y&quot;</span>, validator=validators.<span style="color: black;">NotEmpty</span><span style="color: black;">&#41;</span>
        card_number = TextField<span style="color: black;">&#40;</span>label_text=<span style="color: #483d8b;">'Card #'</span>, validator=validators.<span style="color: black;">NotEmpty</span><span style="color: black;">&#41;</span>
        card_cvv = TextField<span style="color: black;">&#40;</span>label_text=<span style="color: #483d8b;">'CVV Code'</span>, validator=validators.<span style="color: black;">NotEmpty</span><span style="color: black;">&#41;</span>
&nbsp;
authnet_form = AuthnetForm<span style="color: black;">&#40;</span><span style="color: #483d8b;">'authnet_form'</span>, action=url<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/authnet/process/'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Links of interest:</p>
<ul>
<li><a href="http://blog.curiasolutions.com/wp-content/uploads/2009/07/authnet.py">Download the authnet.py file</a></li>
<li><a href="http://groups.google.com/group/turbogears/t/c721e2d15bb2c134" target="_blank">This post&#8217;s original Google group thread</a></li>
<li><a href="http://www.adroll.com/labs" target="_blank">The Authorize package</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.curiasolutions.com/2009/07/making-turbogears-and-authorize-net-play-nice-together/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Introducing TurboGears 2</title>
		<link>http://blog.curiasolutions.com/2009/06/turbogears-2/</link>
		<comments>http://blog.curiasolutions.com/2009/06/turbogears-2/#comments</comments>
		<pubDate>Wed, 10 Jun 2009 02:43:00 +0000</pubDate>
		<dc:creator>Seth</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[turbogears]]></category>

		<guid isPermaLink="false">http://blog.curiasolutions.com/?p=3</guid>
		<description><![CDATA[I&#8217;m the type of person who is a bit anal about trying to use what I think is &#8220;the right tool for the job.&#8221; This is something I think my Dad passed down to me (I can&#8217;t tell you how many times I was scolded with that phrase as a kid—usually having something to do [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m the type of person who is a bit anal about trying to use what I think is &#8220;the right tool for the job.&#8221; This is something I think my Dad passed down to me (I can&#8217;t tell you how many times I was scolded with that phrase as a kid—usually having something to do with me trying to hammer an object with anything but a hammer); and it influences my world of development heavily. Because of this, I often find myself using a number of different programming languages and technologies at any given time for various projects. A shell script is perfect when you need something quick &amp; dirty. PHP makes adding logic to HTML a piece of cake. And then there&#8217;s my new personal favorite: Python.</p>
<p>Ok, let me back up before I get ahead of myself&#8230;<span id="more-3"></span></p>
<p>I&#8217;ve been a web developer for over 10 years now. When it comes to web programming languages and platforms, I&#8217;ve been around the block a bit: Back in the 90s I was building shopping carts from scratch using Perl (ouch!). From Perl, I eventually migrated to PHP which usually made things much easier. From time to time I would play around with Rails; but I never could stomach Ruby&#8217;s syntax (I think it reminded me too much of Perl). Even though I never felt like I really &#8220;clicked&#8221; with PHP, I eventually settled into a few PHP frameworks I liked and finally called myself a PHP developer.</p>
<p>Then one day I stumbled across Python, and it was love at first sight. I quickly vowed that I would soon be doing 99% of my web development in my newfound favorite language, but was disappointed to discover that (at that time) the available web frameworks were either ugly or poorly documented/supported. Reluctantly I returned to PHP, unable to justify moving away from the &#8220;quick &amp; simple&#8221; nature of what I was used to with PHP and the frameworks I was used to.</p>
<p>I&#8217;m not one to give up easily though, and on a recent &#8220;re-visit&#8221; of the Python web framework scene I was excited to see that things had changed dramatically: Django&#8217;s community had exploded and its documentation looked rock-solid. Pylons had cropped up as a serious WSGI-based contender (and modeling quite a bit of it&#8217;s functionality after Rails). But what really caught my attention was the (at the time) upcoming TurboGears v2.0 release.</p>
<p>What made TG2 stand out to me was its attempt to build a &#8220;best-of-breed&#8221; module-set on top of Pylons. Mark Ramm (the current leader of TG development) has been quoted as comparing TG2 to Ubuntu in that  &#8220;TG 2 is to Pylons as Ubuntu is to Debian.&#8221; The sound of that intrigued me, and I started playing around with the TG 2 Betas (and liking what I was seeing!).</p>
<p>Recently one of my clients asked me to whip up a medium-sized project with various amounts of CRUD and Ajax. Ironically, that very same day TurboGears&#8217; v2.0 release went gold. Seeing this as my opportunity to finally begin transitioning into the Python world with a stable &#8220;next-generation&#8221; Python web-development framework, I have decided to implement my latest project using TurboGears 2. Could this be the beginning of the rest of my life?</p>
<p>I will keep you posted on my progress and what I encounter along the way (good, bad, and ugly). Keep it here for what will likely be an interesting adventure.</p>
<p>&nbsp;</p>
<p>Related links of interest:</p>
<ul>
<li><a href="http://www.turbogears.com/2.0" target="_blank">TurboGears</a></li>
<li><a href="http://www.linuxjournal.com/content/introducing-three-python-web-frameworks" target="_blank">Introducing Three Python Web Frameworks</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.curiasolutions.com/2009/06/turbogears-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
