<?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>Oliver's Place &#187; Django</title>
	<atom:link href="http://weichhold.com/category/django/feed/" rel="self" type="application/rss+xml" />
	<link>http://weichhold.com</link>
	<description></description>
	<lastBuildDate>Fri, 10 Sep 2010 08:21:56 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>High Performance Media Merging with Django, Nginx and Memcached</title>
		<link>http://weichhold.com/2009/02/16/high-performance-media-merging-with-django-nginx-and-memcached/</link>
		<comments>http://weichhold.com/2009/02/16/high-performance-media-merging-with-django-nginx-and-memcached/#comments</comments>
		<pubDate>Mon, 16 Feb 2009 00:20:30 +0000</pubDate>
		<dc:creator>oliver</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[memcached]]></category>

		<guid isPermaLink="false">http://weichhold.com/?p=18</guid>
		<description><![CDATA[I admit it &#8211; I&#8217;m a performance junkie. I can&#8217;t stand code that just works but performs poorly. Being said, I recently fell in love with Django (a fantastic Python powered web application framework). In one of my current projects &#8211; xarmory.com &#8211; the number of requests for static resources issued during page load began [...]]]></description>
			<content:encoded><![CDATA[<p>I admit it &#8211; I&#8217;m a performance junkie. I can&#8217;t stand code that just works but performs poorly. Being said, I recently fell in love with Django (a fantastic Python powered web application framework). In one of my current projects &#8211; <a href="http://xarmory.com">xarmory.com</a> &#8211; the number of requests for static resources issued during page load began to bother me. The project makes intensive use of jQuery and it&#8217;s my personal belief that Django + jQuery is a match made in heaven. When working with jQuery you will find yourself often in the situation to rely in cool little jQuery plugins &#8211; each distributed as a seperate javascript file of course. When a project grows, those files begin to add up. In the case of xarmory.com we now had eleven javascript references in the header section of the page. Although the individual files are were only 3-11k in Size, the request overhead for all those tiny files became unacceptably in my eyes.</p>
<p>To alleviate the problem I decided to resort to a simple solution and just merge the individual script files into a single file. A pretty common practice. Before reinventing the wheel I surfed the net for existing solutions and found two which I gave a shot. Short story, both solutions had their share of problems mostly stemming from the fact that they wanted to do it all and integrate their own Javascript Minifier in addition to the merging. During my short evaluation both Minifiers choked on fifty percent of my javascript files, even on the official jquery 1.2.6 script. A bit frustrated I decided to roll out my own Django Template Tag based on this <a href="http://www.djangosnippets.org/snippets/405/">code</a>.</p>
<p><span id="more-18"></span><br />
I can be used like this:</p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p18code6'); return false;">View Code</a> PYTHON</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p186"><td class="code" id="p18code6"><pre class="python" style="font-family:monospace;"><span style="color: black;">&#123;</span><span style="color: #66cc66;">%</span> load mediamerge <span style="color: #66cc66;">%</span><span style="color: black;">&#125;</span>
&nbsp;
<span style="color: black;">&#123;</span><span style="color: #66cc66;">%</span> mediamerge merged/standard <span style="color: #ff4500;">1</span> scripts/jquery-1.2.6.<span style="color: #008000;">min</span>.<span style="color: black;">js</span> scripts/jquery.<span style="color: black;">cookie</span>.<span style="color: black;">js</span> scripts/superfish.<span style="color: black;">js</span> scripts/supersubs.<span style="color: black;">js</span> scripts/shared.<span style="color: black;">js</span> <span style="color: #66cc66;">%</span><span style="color: black;">&#125;</span></pre></td></tr></table></div>

<p>The first line obviously makes the mediamerge tag available to the template. The second line needs more explanation. The first parameter has two meanings</p>
<ul>
<li>First it is the relative path of the generated merged javascript file. The path is relativ to settings.MEDIA_ROOT (not used if the mediamerged is configured to use memcached &#8211; on to that later)</li>
<li>Second it defines an URI relative to settings.MEDIA_URL</li>
</ul>
<p>The second parameter defines a numeric version of the merged scripts and needs to be bumped by you whenever one of the files changes.</p>
<p>After the version follows the list of resources to be merged. All specified relative to settings.MEDIA_ROOT.</p>
<p>Assuming that settings.MEDIA_URL = http://static.example.com/ the tag above would result in the following HTML:</p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p18code7'); return false;">View Code</a> HTML</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p187"><td class="code" id="p18code7"><pre class="html" style="font-family:monospace;">&lt;script src=&quot;http://static.example.com/merged/standard.js?1&quot; type=&quot;text/javascript&quot;&gt;&lt;!--mce:0--&gt;&lt;/script&gt;</pre></td></tr></table></div>

<p>Great, we&#8217;ve cut the number of requests by a factor. But it gets even faster. The mediamerge tag can be instructed to not generate an output file but to put the merged content directly into memcached (I&#8217;m obsessed with memcached) for a webserver supporting memcached to pick it up there. Nginx does and it is lightning fast serving static content.</p>
<p>So we instruct the mediamerge tag to use memcached in settings.py:</p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p18code8'); return false;">View Code</a> PYTHON</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p188"><td class="code" id="p18code8"><pre class="python" style="font-family:monospace;">MEDIAMERGE_ENABLE_MEMCACHED = <span style="color: #008000;">True</span>
MEDIAMERGE_MEMCACHED_URI_PREFIX = <span style="color: #483d8b;">&quot;mmerge_&quot;</span>
MEMCACHED_SERVERS = <span style="color: black;">&#91;</span> <span style="color: #483d8b;">'localhost:11211'</span> <span style="color: black;">&#93;</span></pre></td></tr></table></div>

<p>Using these settings, mediamerge will store the merged scripts in memcached under the key &#8216;mmerge_ http://static.example.com/merged/standard.js?1&#8242; (settings.MEDIAMERGE_MEMCACHED_URI_PREFIX + settings.MEDIA_URL + )</p>
<p>The final thing to make it all work is to instruct Nginx to pick up request for resources handled by the mediamerge tag directly from memcached.</p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p18code9'); return false;">View Code</a> APACHE</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p189"><td class="code" id="p18code9"><pre class="apache" style="font-family:monospace;">server
{
	<span style="color: #00007f;">listen</span> <span style="color: #ff0000;">80</span>;
	server_name static.example.com;
	expires 24h;
&nbsp;
	<span style="color: #adadad; font-style: italic;"># mediamerge</span>
	location /merged/
	{
		set $memcached_key mmerge_$scheme://$server_name$request_uri;
		memcached_pass 127.0.0.1:<span style="color: #ff0000;">11211</span>;
	}
&nbsp;
	location /
	{
		root /var/www/example_static;
	}
}</pre></td></tr></table></div>

<p>Bingo, Nginx will now serve all requests for resources in http://static.example.com /merged/ by looking up the contents in the specified memcached server (cluster). Nginx is already very fast for traditionally served static files but this is just ridiculously fast.</p>
<p><strong>UPDATE:</strong> I&#8217;ve updated the source below since the article was originally posted. Changes: </p>
<ul>
<li>A new option is now supported when using the disk based method: MEDIAMERGE_DISABLE_MERGE_UPDATES. Setting this option to True in settings.py will disable any checks for updated merge source files and is intended for production servers where your input files should not change in the background unless the web server is restarted. This should improve the rendering time of the tag in production considerably.</li>
<li>The files are now opened and written in binary mode</li>
</ul>
<p>Here&#8217;s the source code for the mediamerge template tag:</p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p18code10'); return false;">View Code</a> PYTHON</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p1810"><td class="code" id="p18code10"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>, <span style="color: #dc143c;">re</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">logging</span>
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">os</span> <span style="color: #ff7700;font-weight:bold;">import</span> path
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">cStringIO</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">StringIO</span>
<span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">template</span> <span style="color: #ff7700;font-weight:bold;">import</span> Library, Node, TemplateSyntaxError
<span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">conf</span> <span style="color: #ff7700;font-weight:bold;">import</span> settings
&nbsp;
logger = <span style="color: #dc143c;">logging</span>.<span style="color: black;">getLogger</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'mediamerge'</span><span style="color: black;">&#41;</span>
mc = <span style="color: #008000;">None</span>
register = Library<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> JSMergeNode<span style="color: black;">&#40;</span>Node<span style="color: black;">&#41;</span>:
	<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>, js_name, js_ver, js_files<span style="color: black;">&#41;</span>:
		<span style="color: #008000;">self</span>.<span style="color: black;">js_name</span> = <span style="color: #483d8b;">'%s.js'</span> <span style="color: #66cc66;">%</span> js_name
		<span style="color: #008000;">self</span>.<span style="color: black;">js_ver</span> = js_ver
		<span style="color: #008000;">self</span>.<span style="color: black;">js_files</span> = js_files
		<span style="color: #008000;">self</span>.<span style="color: black;">merge_filename</span> = path.<span style="color: black;">normpath</span><span style="color: black;">&#40;</span>path.<span style="color: black;">join</span><span style="color: black;">&#40;</span>settings.<span style="color: black;">MEDIA_ROOT</span>, <span style="color: #008000;">self</span>.<span style="color: black;">js_name</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
		<span style="color: #808080; font-style: italic;"># make sure that the target directory exists</span>
		<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> settings.<span style="color: black;">MEDIAMERGE_ENABLE_MEMCACHED</span> <span style="color: #ff7700;font-weight:bold;">and</span> <span style="color: #ff7700;font-weight:bold;">not</span> path.<span style="color: black;">exists</span><span style="color: black;">&#40;</span>path.<span style="color: black;">dirname</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">merge_filename</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>:
			<span style="color: #dc143c;">os</span>.<span style="color: black;">makedirs</span><span style="color: black;">&#40;</span>path.<span style="color: black;">dirname</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">merge_filename</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
	<span style="color: #ff7700;font-weight:bold;">def</span> merge_files<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, merge_file<span style="color: black;">&#41;</span>:
		<span style="color: #808080; font-style: italic;"># join all input files into a single output file</span>
		<span style="color: #ff7700;font-weight:bold;">for</span> js <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">js_files</span>:
			jspath = path.<span style="color: black;">normpath</span><span style="color: black;">&#40;</span>path.<span style="color: black;">join</span><span style="color: black;">&#40;</span>settings.<span style="color: black;">MEDIA_ROOT</span>, js<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
			<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> path.<span style="color: black;">isfile</span><span style="color: black;">&#40;</span>jspath<span style="color: black;">&#41;</span>:
				<span style="color: #ff7700;font-weight:bold;">continue</span>
			<span style="color: #008000;">self</span>.<span style="color: black;">merge_js</span><span style="color: black;">&#40;</span>js, jspath, merge_file<span style="color: black;">&#41;</span>
&nbsp;
	<span style="color: #ff7700;font-weight:bold;">def</span> render<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, context<span style="color: black;">&#41;</span>:
		<span style="color: #483d8b;">''</span><span style="color: #483d8b;">'Implementation of the render method of the tag'</span><span style="color: #483d8b;">''</span>
		<span style="color: #ff7700;font-weight:bold;">if</span> settings.<span style="color: black;">MEDIAMERGE_ENABLE_MEMCACHED</span>:
			<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">render_memcached</span><span style="color: black;">&#40;</span>context<span style="color: black;">&#41;</span>
		<span style="color: #ff7700;font-weight:bold;">else</span>:
			<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">render_file</span><span style="color: black;">&#40;</span>context<span style="color: black;">&#41;</span>
&nbsp;
	<span style="color: #ff7700;font-weight:bold;">def</span> render_passthrough<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, context<span style="color: black;">&#41;</span>:
		result = <span style="color: #483d8b;">&quot;&quot;</span>
		<span style="color: #ff7700;font-weight:bold;">for</span> js <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">js_files</span>:
			<span style="color: #ff7700;font-weight:bold;">if</span> settings.<span style="color: black;">MEDIA_URL</span><span style="color: black;">&#91;</span>-<span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> == <span style="color: #483d8b;">'/'</span>:
				result += <span style="color: #483d8b;">'&lt;script type=&quot;text/javascript&quot; src=&quot;%s%s&quot;&gt;&lt;/script&gt;<span style="color: #000099; font-weight: bold;">\n</span>'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>settings.<span style="color: black;">MEDIA_URL</span>, js<span style="color: black;">&#41;</span>
			<span style="color: #ff7700;font-weight:bold;">else</span>:
				result += <span style="color: #483d8b;">'&lt;script type=&quot;text/javascript&quot; src=&quot;%s/%s&quot;&gt;&lt;/script&gt;<span style="color: #000099; font-weight: bold;">\n</span>'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>settings.<span style="color: black;">MEDIA_URL</span>, js<span style="color: black;">&#41;</span>
		<span style="color: #ff7700;font-weight:bold;">return</span> result
&nbsp;
	<span style="color: #ff7700;font-weight:bold;">def</span> render_memcached<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, context<span style="color: black;">&#41;</span>:
		<span style="color: #483d8b;">''</span><span style="color: #483d8b;">'Memcached based render method - merged output directly stored in memcache - where nginx will pick it up based on the rquest uri) without using a temp file'</span><span style="color: #483d8b;">''</span>
		<span style="color: #ff7700;font-weight:bold;">import</span> memcache
		key = settings.<span style="color: black;">MEDIAMERGE_MEMCACHED_URI_PREFIX</span> + <span style="color: #008000;">str</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">compute_media_url</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
		<span style="color: #ff7700;font-weight:bold;">global</span> mc
		<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> mc:
			mc = memcache.<span style="color: black;">Client</span><span style="color: black;">&#40;</span>settings.<span style="color: black;">MEMCACHED_SERVERS</span>, debug=<span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>
&nbsp;
		<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> mc.<span style="color: black;">get</span><span style="color: black;">&#40;</span>key<span style="color: black;">&#41;</span>:
			merge_file = <span style="color: #dc143c;">StringIO</span>.<span style="color: #dc143c;">StringIO</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
			<span style="color: #008000;">self</span>.<span style="color: black;">merge_files</span><span style="color: black;">&#40;</span>merge_file<span style="color: black;">&#41;</span>
			contents = merge_file.<span style="color: black;">getvalue</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
			mc.<span style="color: #008000;">set</span><span style="color: black;">&#40;</span>key, contents, <span style="color: #ff4500;">3600</span><span style="color: black;">&#41;</span>
			logger.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Setting memcached value for for resource %s - key %s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">js_name</span>, key<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
		<span style="color: #ff7700;font-weight:bold;">else</span>:
			logger.<span style="color: black;">debug</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Merge key %s already exists in memcached for resource %s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>key, <span style="color: #008000;">self</span>.<span style="color: black;">js_name</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
		<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">js_tag</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
	<span style="color: #ff7700;font-weight:bold;">def</span> render_file<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, context<span style="color: black;">&#41;</span>:
		<span style="color: #483d8b;">''</span><span style="color: #483d8b;">'File based render method - merged output directly stored in the file specified with the tag - relative to settings.MEDIA_ROOT'</span><span style="color: #483d8b;">''</span>
		<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> path.<span style="color: black;">exists</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">merge_filename</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">self</span>.<span style="color: black;">is_merge_updated</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
			logger.<span style="color: black;">debug</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Trying to open output file %s for resource %s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">merge_filename</span>, <span style="color: #008000;">self</span>.<span style="color: black;">js_name</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
			merge_file = <span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">merge_filename</span>, <span style="color: #483d8b;">'wb'</span><span style="color: black;">&#41;</span>
			<span style="color: #008000;">self</span>.<span style="color: black;">merge_files</span><span style="color: black;">&#40;</span>merge_file<span style="color: black;">&#41;</span>
			merge_file.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
		<span style="color: #ff7700;font-weight:bold;">else</span>:
			logger.<span style="color: black;">debug</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Merge file %s already exists for resource %s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">merge_filename</span>, <span style="color: #008000;">self</span>.<span style="color: black;">js_name</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
		<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">js_tag</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
	<span style="color: #ff7700;font-weight:bold;">def</span> is_merge_updated<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
		<span style="color: #ff7700;font-weight:bold;">if</span> settings.<span style="color: black;">MEDIAMERGE_DISABLE_MERGE_UPDATES</span>:
			<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
&nbsp;
		<span style="color: #483d8b;">&quot;&quot;&quot; compares modification time of all js with merged js &quot;&quot;&quot;</span>
		last_mtime = <span style="color: #ff4500;">0</span> <span style="color: #808080; font-style: italic;"># last modification time of a js file</span>
		<span style="color: #ff7700;font-weight:bold;">for</span> js <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">js_files</span>:
			jspath = path.<span style="color: black;">normpath</span><span style="color: black;">&#40;</span>path.<span style="color: black;">join</span><span style="color: black;">&#40;</span>settings.<span style="color: black;">MEDIA_ROOT</span>, js<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
			jsstat = <span style="color: #dc143c;">os</span>.<span style="color: #dc143c;">stat</span><span style="color: black;">&#40;</span>jspath<span style="color: black;">&#41;</span>
			mtime = jsstat<span style="color: black;">&#91;</span>-<span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span>
			<span style="color: #ff7700;font-weight:bold;">if</span> last_mtime <span style="color: #66cc66;">&lt;</span> mtime:
				last_mtime = mtime
		merge_mtime = <span style="color: #dc143c;">os</span>.<span style="color: #dc143c;">stat</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">merge_filename</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span>-<span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span>
		<span style="color: #ff7700;font-weight:bold;">return</span> merge_mtime <span style="color: #66cc66;">&gt;</span> last_mtime
&nbsp;
	<span style="color: #ff7700;font-weight:bold;">def</span> merge_js<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, jsname, jspath, fd<span style="color: black;">&#41;</span>:
		<span style="color: #483d8b;">&quot;&quot;&quot; do merging and compressing of javascript &quot;&quot;&quot;</span>
		logger.<span style="color: black;">debug</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Merging file %s for resource %s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>jsname, <span style="color: #008000;">self</span>.<span style="color: black;">js_name</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
		jsfile = <span style="color: #008000;">open</span><span style="color: black;">&#40;</span>jspath, <span style="color: #483d8b;">&quot;rb&quot;</span><span style="color: black;">&#41;</span>
		jscontent = jsfile.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
		fd.<span style="color: black;">write</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/* -- %s -- */<span style="color: #000099; font-weight: bold;">\n</span><span style="color: #000099; font-weight: bold;">\n</span>'</span> <span style="color: #66cc66;">%</span> jsname<span style="color: black;">&#41;</span>
		fd.<span style="color: black;">write</span><span style="color: black;">&#40;</span>jscontent<span style="color: black;">&#41;</span>
		fd.<span style="color: black;">write</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;<span style="color: #000099; font-weight: bold;">\n</span><span style="color: #000099; font-weight: bold;">\n</span><span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
	<span style="color: #ff7700;font-weight:bold;">def</span> compute_media_url<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
		<span style="color: #808080; font-style: italic;"># detect if MEDIA_URL ends with a slash</span>
		<span style="color: #ff7700;font-weight:bold;">if</span> settings.<span style="color: black;">MEDIA_URL</span><span style="color: black;">&#91;</span>-<span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> == <span style="color: #483d8b;">'/'</span>:
			js_url = <span style="color: #483d8b;">'%s%s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>settings.<span style="color: black;">MEDIA_URL</span>, <span style="color: #008000;">self</span>.<span style="color: black;">js_name</span>, <span style="color: #008000;">self</span>.<span style="color: black;">js_ver</span><span style="color: black;">&#41;</span>
		<span style="color: #ff7700;font-weight:bold;">else</span>:
			js_url = <span style="color: #483d8b;">'%s/%s?%d'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>settings.<span style="color: black;">MEDIA_URL</span>, <span style="color: #008000;">self</span>.<span style="color: black;">js_name</span>, <span style="color: #008000;">self</span>.<span style="color: black;">js_ver</span><span style="color: black;">&#41;</span>
&nbsp;
		<span style="color: #ff7700;font-weight:bold;">return</span> js_url
&nbsp;
	<span style="color: #ff7700;font-weight:bold;">def</span> js_tag<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; write js tag for merged file inclusion &quot;&quot;&quot;</span>
&nbsp;
		<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #483d8b;">'&lt;script type=&quot;text/javascript&quot; src=&quot;%s&quot;&gt;&lt;/script&gt;'</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">self</span>.<span style="color: black;">compute_media_url</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> do_jsmerge<span style="color: black;">&#40;</span><span style="color: #dc143c;">parser</span>, <span style="color: #dc143c;">token</span><span style="color: black;">&#41;</span>:
	<span style="color: #483d8b;">&quot;&quot;&quot;
	This will merge javascript files in only one compressed javascript.
&nbsp;
	Usage::
&nbsp;
		{% load mediamerge %}
		{% mediamerge &lt;output_js_file&gt; &lt;output_version_integer&gt; [jsfile1] [jsfile2] .. %}
&nbsp;
	Example::
&nbsp;
		{% load mediamerge %}
		{% mediamerge mediamergefile 1  js/file1.js js/file2.js js/file3.js %}
&nbsp;
	This will create (if not exists) a /media/mediamergefile.js with three files merged. The HTML output for this will be::
&nbsp;
		&lt;script type=&quot;text/javascript&quot; src=&quot;/media/mediamergefile.js&quot;&gt;&lt;/script&gt;
	&quot;&quot;&quot;</span>
	tokens = <span style="color: #dc143c;">token</span>.<span style="color: black;">contents</span>.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
	<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>tokens<span style="color: black;">&#41;</span> <span style="color: #66cc66;">&lt;</span> <span style="color: #ff4500;">3</span>:
		<span style="color: #ff7700;font-weight:bold;">raise</span> TemplateSyntaxError<span style="color: black;">&#40;</span>u<span style="color: #483d8b;">&quot;'%r' tag requires at least 1 arguments.&quot;</span> <span style="color: #66cc66;">%</span> tokens<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
	<span style="color: #ff7700;font-weight:bold;">return</span> JSMergeNode<span style="color: black;">&#40;</span>tokens<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>, <span style="color: #008000;">int</span><span style="color: black;">&#40;</span>tokens<span style="color: black;">&#91;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>, tokens<span style="color: black;">&#91;</span><span style="color: #ff4500;">3</span>:<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
register.<span style="color: black;">tag</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'mediamerge'</span>, do_jsmerge<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<img src="http://weichhold.com/?ak_action=api_record_view&id=18&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://weichhold.com/2009/02/16/high-performance-media-merging-with-django-nginx-and-memcached/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>django, nginx, memcached &#8211; the dynamic trio</title>
		<link>http://weichhold.com/2008/09/12/django-nginx-memcached-the-dynamic-trio/</link>
		<comments>http://weichhold.com/2008/09/12/django-nginx-memcached-the-dynamic-trio/#comments</comments>
		<pubDate>Fri, 12 Sep 2008 21:01:22 +0000</pubDate>
		<dc:creator>oliver</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[memcached]]></category>

		<guid isPermaLink="false">http://weichhold.com/?p=17</guid>
		<description><![CDATA[Inspired by this article I decided to find out if the same technique can be exploited in my current project which is developed in django. My first problem was to come up with a viable cache key scheme since simply using the full request URI as suggested in the article wouldn&#8217;t work for me because [...]]]></description>
			<content:encoded><![CDATA[<p>Inspired by <a href="http://www.igvita.com/2008/02/11/nginx-and-memcached-a-400-boost/" target="_blank">this article</a> I decided to find out if the same technique can be exploited in my current project which is developed in <a href="http://www.djangoproject.com" target="_blank">django</a>.</p>
<p>My first problem was to come up with a viable cache key scheme since simply using the full request URI as suggested in the article wouldn&#8217;t work for me because my site renders a different version of a navigation menu depending on the authentication state of current the user. After weighing in the advantages and disadvantages between the super clean variant of factoring the session cookie and all other cookies into the <a href="http://www.danga.com/memcached/" target="_blank">memcached</a> key and a less heavy weight method that would only append a server supplied abstract &#8220;page version&#8221; field to the request URI, I went for the latter. My resulting nginx virtual host config was looking like this:</p>
<p><span id="more-17"></span></p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p17code14'); return false;">View Code</a> APACHE</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p1714"><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
</pre></td><td class="code" id="p17code14"><pre class="apache" style="font-family:monospace;"><span style="color: #adadad; font-style: italic;"># define application servers</span>
upstream backend
{
  server 127.0.0.1:<span style="color: #ff0000;">8080</span> weight=<span style="color: #ff0000;">1</span>;
}
&nbsp;
server
{
  <span style="color: #00007f;">listen</span> <span style="color: #ff0000;">80</span>;
  server_name domain.com;
  access_log /var/log/nginx/domain.com.log;
&nbsp;
  location /
  {
    <span style="color: #adadad; font-style: italic;"># we never cache post requests</span>
    if ($request_method = POST)
    {
      proxy_pass http://backend;
      break;
    }
&nbsp;
    <span style="color: #adadad; font-style: italic;"># extract cache key args and compute cache key</span>
    if ($http_cookie ~* <span style="color: #7f007f;">&quot;pv=([^;]+)(?:;|$)&quot;</span>)
    {
      set $page_version $<span style="color: #ff0000;">1</span>;
    }
    set $memcached_key $request_uri&amp;amp;pv=$page_version;
&nbsp;
    <span style="color: #adadad; font-style: italic;"># Check if local memcached server can answer this request</span>
    default_type text/html;
    memcached_pass 127.0.0.1:<span style="color: #ff0000;">11211</span>;
&nbsp;
    <span style="color: #adadad; font-style: italic;"># Send to app. server if Memcached could not answer the request</span>
    error_page <span style="color: #ff0000;">404</span> = @cache_miss;
  }
&nbsp;
  location @cache_miss
  {
    proxy_pass http://backend;
  }
}</pre></td></tr></table></div>

<p>The important lines are line 23-27. Line 23 tests the cookies header of the current request for the presence of a cookie named &#8216;pv&#8217; using a regular expression and line 25 stores the extracted value of this cookie in a temporary variable. Later, in line 27 we combine the value of that variable with the request URI to form the final memcached key. For example if the request uri would be /foo and the headers would contain a cookie pv=acme123, then the cache key would be /foo&amp;pv=acme123.</p>
<p>Line 31 is where the actual memcached lookup happens. If the computed key is present in the cache, the cached page is returned immediately &#8211; completely bypassing the backend. If the page (or the specific version of the page) is not present in the cache, then the alternate branch at line 37 is taken which ultimately forwards the request to our load balanced cluster of application servers in line 39. The actual servers making up the cluster is defined at line 2. There&#8217;s currently only one lonely application server defined there and that&#8217;s a apache 2.2 listening on localhost port 8080 running our django application through mod_wsgi.</p>
<p>So far I have explained how the front line request processing in nginx works but how is invalidation handled? To tackle that problem I wrote a django middleware that can be applied to arbitrary django views using a decorator:<br />
<br/></p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p17code15'); return false;">View Code</a> PYTHON</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p1715"><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="code" id="p17code15"><pre class="python" style="font-family:monospace;">@cache_page_nginx<span style="color: black;">&#40;</span><span style="color: #ff4500;">3600</span><span style="color: #66cc66;">*</span><span style="color: #ff4500;">6</span>, compute_common_page_version<span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> index<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>:
  <span style="color: #ff7700;font-weight:bold;">return</span> render_to_response<span style="color: black;">&#40;</span><span style="color: #483d8b;">'index.html'</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> compute_common_page_version<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>:
  <span style="color: #483d8b;">''</span><span style="color: #483d8b;">'This method is called by the nginx_cache decorator
  The method is supposed to return some value that can be used to distinguish
  different cached versions of the same page. For now it is sufficient to differentiate
  between authenticated and anonymous users'</span><span style="color: #483d8b;">''</span>
  <span style="color: #ff7700;font-weight:bold;">if</span> request.<span style="color: #dc143c;">user</span> <span style="color: #ff7700;font-weight:bold;">and</span> request.<span style="color: #dc143c;">user</span>.<span style="color: black;">is_authenticated</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #ff4500;">1</span>
  <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #ff4500;">0</span></pre></td></tr></table></div>

<p>Some readers will notice that the decorator looks very similar to Django&#8217;s built-in cache_page decorator. And it actually works similar but with several important differences:</p>
<ul>
<li>It totally relies on nginx doing the actual cache lookup. The decorator is only responsible for storing the rendered content of page in the cache.</li>
<li>It bypasses the Django cache framework and talks to memcached directly using cmemcached or python-memcached. This was done because there&#8217;s reallly no point in supporting an additional level of abstraction if the consumer of the cached content won&#8217;t look for it anywhere but in memcached.</li>
</ul>
<p>A word about the compute_common_page_version function. What does it do? The purpose of this method is to ensure that the cache will contain at maximum two distinct versions of the home page. Once for authenticated users and one for anonymous users. For more complex scenarios, the complexity of this method would also increase, resulting on more distinct versions of the same page. Obviously the difficulty lies in maintaining data integrity for every user while not producing to many cached versions of a page, thus increasing memory pressure on the server and lowering cache hit ratio.<br />
<br/><br />
The decorator:<br />
<br/></p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p17code16'); return false;">View Code</a> PYTHON</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p1716"><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
</pre></td><td class="code" id="p17code16"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">utils</span>.<span style="color: black;">decorators</span> <span style="color: #ff7700;font-weight:bold;">import</span> decorator_from_middleware
<span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">conf</span> <span style="color: #ff7700;font-weight:bold;">import</span> settings
<span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">core</span>.<span style="color: black;">cache</span> <span style="color: #ff7700;font-weight:bold;">import</span> cache
<span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">utils</span>.<span style="color: black;">encoding</span> <span style="color: #ff7700;font-weight:bold;">import</span> smart_unicode, smart_str
<span style="color: #ff7700;font-weight:bold;">from</span> utils <span style="color: #ff7700;font-weight:bold;">import</span> kdebug
&nbsp;
<span style="color: #ff7700;font-weight:bold;">try</span>:
    <span style="color: #ff7700;font-weight:bold;">import</span> cmemcache <span style="color: #ff7700;font-weight:bold;">as</span> memcache
<span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">ImportError</span>:
    <span style="color: #ff7700;font-weight:bold;">try</span>:
        <span style="color: #ff7700;font-weight:bold;">import</span> memcache
    <span style="color: #ff7700;font-weight:bold;">except</span>:
        <span style="color: #ff7700;font-weight:bold;">raise</span> InvalidCacheBackendError<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Memcached cache backend requires either the 'memcache' or 'cmemcache' library&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> UpdateCacheMiddleware<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
  <span style="color: #483d8b;">&quot;&quot;&quot;
  Updates memcached with the response of the request. It is of _paramount_
  importance that the generated cache_key matches exactly the key generated
  by your web to lookup the page from the cache.
&nbsp;
  This class talks to memcached, bypassing Djangos cache backend because
  it is only meant to talk to memcached and nothing else.
&nbsp;
  Must the first piece of middleware in settings.MIDDLEWARE_CLASSES so
  that it'll get called last during the response phase.
  &quot;&quot;&quot;</span>
  <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>, cache_timeout, page_version_method<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">''</span><span style="color: #483d8b;">'timeout - is the timeout in seconds after which the cached response expires in memcached
    page_version_method - is called during response processing and must return an arbitrary value
    that can be used to distinguish different cached versions of the same page '</span><span style="color: #483d8b;">''</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">cache_timeout</span> = cache_timeout
    <span style="color: #008000;">self</span>.<span style="color: black;">page_version_method</span> = page_version_method
    <span style="color: #008000;">self</span>.<span style="color: black;">cache</span> = memcache.<span style="color: black;">Client</span><span style="color: black;">&#40;</span>settings.<span style="color: black;">NGINX_MEMCACHED_SERVERS</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">cookie_name</span> = <span style="color: #483d8b;">'pv'</span>
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> process_response<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, request, response<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Sets the cache, if needed.&quot;&quot;&quot;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> settings.<span style="color: black;">NGINX_MEMCACHED_ENABLE</span> <span style="color: #ff7700;font-weight:bold;">or</span> request.<span style="color: black;">method</span> <span style="color: #66cc66;">!</span>= <span style="color: #483d8b;">'GET'</span> <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: #ff7700;font-weight:bold;">not</span> response.<span style="color: black;">status_code</span> == <span style="color: #ff4500;">200</span>:
      <span style="color: #808080; font-style: italic;"># because of interactions between this middleware and the</span>
      <span style="color: #808080; font-style: italic;"># HTTPMiddleware, which throws the body of a HEAD-request</span>
      <span style="color: #808080; font-style: italic;"># away before this middleware gets a chance to cache it.</span>
      <span style="color: #ff7700;font-weight:bold;">return</span> response
&nbsp;
    <span style="color: #008000;">self</span>.<span style="color: black;">cache_response</span><span style="color: black;">&#40;</span>request, response<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> response
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> cache_response<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, request, response<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">''</span><span style="color: #483d8b;">'Manually insertion of cached version of page into the cache'</span><span style="color: #483d8b;">''</span>
    <span style="color: #808080; font-style: italic;"># retrieve page version</span>
    pv = <span style="color: #008000;">self</span>.<span style="color: black;">page_version_method</span><span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># compute key that's follows the same naming convention as specified in nginx configuration:</span>
    <span style="color: #808080; font-style: italic;"># set $memcached_key $request_uri&amp;sid=$session_id;</span>
    cache_key = <span style="color: #008000;">self</span>.<span style="color: black;">compute_cache_key_from_request</span><span style="color: black;">&#40;</span>request, pv<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># update cache</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">cache</span>.<span style="color: #008000;">set</span><span style="color: black;">&#40;</span>cache_key, response._get_content<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>, <span style="color: #008000;">self</span>.<span style="color: black;">cache_timeout</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># communicate the page version to the browser using cookie</span>
    response.<span style="color: black;">set_cookie</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">cookie_name</span>, pv<span style="color: black;">&#41;</span>
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> compute_cache_key_from_request<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, request, page_version<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">''</span><span style="color: #483d8b;">'Computes the cache key for the specified page and version of the page'</span><span style="color: #483d8b;">''</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">compute_cache_key</span><span style="color: black;">&#40;</span>request.<span style="color: black;">get_full_path</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>, page_version<span style="color: black;">&#41;</span>
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> compute_cache_key<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, page, page_version<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">''</span><span style="color: #483d8b;">'Computes the cache key for the specified page and version of the page'</span><span style="color: #483d8b;">''</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> smart_str<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;%s&amp;%s=%s&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>page, <span style="color: #008000;">self</span>.<span style="color: black;">cookie_name</span>, page_version<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> invalidate_page_version_from_request<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, request, page_version<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">''</span><span style="color: #483d8b;">'Removes the specified version of the page requested by '</span>request<span style="color: #483d8b;">' from the cache'</span><span style="color: #483d8b;">''</span>
    cache_key = <span style="color: #008000;">self</span>.<span style="color: black;">compute_cache_key_from_request</span><span style="color: black;">&#40;</span>request, page_version<span style="color: black;">&#41;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">cache</span>.<span style="color: black;">delete</span><span style="color: black;">&#40;</span>cache_key<span style="color: black;">&#41;</span>
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> invalidate_page_version<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, page, page_version<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">''</span><span style="color: #483d8b;">'Removes the specified version of the page requested by '</span>request<span style="color: #483d8b;">' from the cache'</span><span style="color: #483d8b;">''</span>
    cache_key = <span style="color: #008000;">self</span>.<span style="color: black;">compute_cache_key</span><span style="color: black;">&#40;</span>request, page_version<span style="color: black;">&#41;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">cache</span>.<span style="color: black;">delete</span><span style="color: black;">&#40;</span>cache_key<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># decorator</span>
cache_page_nginx = decorator_from_middleware<span style="color: black;">&#40;</span>UpdateCacheMiddleware<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<img src="http://weichhold.com/?ak_action=api_record_view&id=17&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://weichhold.com/2008/09/12/django-nginx-memcached-the-dynamic-trio/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Using IntelliJ for Django Development</title>
		<link>http://weichhold.com/2008/07/11/using-intellij-for-django-development/</link>
		<comments>http://weichhold.com/2008/07/11/using-intellij-for-django-development/#comments</comments>
		<pubDate>Fri, 11 Jul 2008 10:08:56 +0000</pubDate>
		<dc:creator>oliver</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Hosting]]></category>
		<category><![CDATA[IntelliJ]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[Eclipse]]></category>
		<category><![CDATA[Notepad++]]></category>

		<guid isPermaLink="false">http://weichhold.com/?p=15</guid>
		<description><![CDATA[About a year ago I’ve switched over to IntelliJ as my primary Java IDE. When I began to dabble a bit in Django a few days ago, I realized that IntelliJ had me spoiled when it comes to Editor Features – especially when working with Javascript and HTML files. Notepad++ &#8211; although a great text [...]]]></description>
			<content:encoded><![CDATA[<p>About a year ago I’ve switched over to IntelliJ as my primary Java IDE. When I began to dabble a bit in Django a few days ago, I realized that IntelliJ had me spoiled when it comes to Editor Features – especially when working with Javascript and HTML files. Notepad++ &#8211; although a great text editor on its own right &#8211; simply didn’t cut it for me for web development.</p>
<p>When I tried opening my Django files in IntelliJ I had to realize that IntelliJ needs a project context for opening a file. Even for a simple html file. Fortunately the solution was pretty straight forward. Navigate to your Django project root directory and create a new file .project. Some of you have guessed it: we are pretending to be Eclipse. Open the file in a text editor and paste the following snipped into it:</p>
<blockquote><p>&lt;?xml version=&#8221;1.0&#8243; encoding=&#8221;UTF-8&#8243;?&gt;<br />
&lt;projectDescription&gt;<br />
&lt;name&gt;myproject&lt;/name&gt;<br />
&lt;comment&gt;&lt;/comment&gt;<br />
&lt;projects&gt;&lt;/projects&gt;<br />
&lt;/projectDescription&gt;</p></blockquote>
<p>Edit the &lt;name&gt; tag value and change it to your liking.</p>
<p><span id="more-15"></span></p>
<p>Now it’s time to launch IntelliJ. Select <em>File </em>-&gt; <em>New Project</em>. Select <em>Import project from external model</em> and click <em>Next</em>. Select <em>Eclipse </em>(should be the default) and click <em>Next</em>. Now enter the path to your Django project root folder in the <em>Select Eclipse projects directory</em> field. Click <em>Next</em>. You should now see the name of your pseudo eclipse project in the list. Click <em>Next</em>. You may now change the project name or just click <em>Finish</em>.  Open the project view and change to <em>Project </em>mode. Congratulations, you have imported your Django project into IntelliJ.</p>
<p>I&#8217;d suggest that you install the Pythonid plugin for IntelliJ in order to get a better Python code editing experience and that you add .pyc and .pyo to the <em>Ignored files and folders </em>in IntelliJ&#8217;s <em>General Settings</em> as final steps.</p>
<img src="http://weichhold.com/?ak_action=api_record_view&id=15&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://weichhold.com/2008/07/11/using-intellij-for-django-development/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
