<?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>Ryan Kanno: The diary of an Enginerd in Hawaii&#187; Python</title>
	<atom:link href="http://blog.localkinegrinds.com/category/technology/python/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.localkinegrinds.com</link>
	<description>Everything you&#039;ve ever thought, but never had the balls to say.</description>
	<lastBuildDate>Sat, 03 Sep 2011 13:19:23 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Lessons Learned: Google App Engine + App Engine Patch + Django + Boto</title>
		<link>http://blog.localkinegrinds.com/2009/04/02/lessons-learned-google-app-engine-app-engine-patch-django-boto/</link>
		<comments>http://blog.localkinegrinds.com/2009/04/02/lessons-learned-google-app-engine-app-engine-patch-django-boto/#comments</comments>
		<pubDate>Thu, 02 Apr 2009 08:00:18 +0000</pubDate>
		<dc:creator>ryankanno</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[app-engine]]></category>
		<category><![CDATA[boto]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[lessons-learned]]></category>

		<guid isPermaLink="false">http://blog.localkinegrinds.com/?p=348</guid>
		<description><![CDATA[Update: Mitch Garnaat from CloudRight has pointed out that you can actually set the policy of the S3 file in the set_contents_from_file call instead of making another roundtrip request into S3 (and saving you some coin). Thanks Mitch! Btw, I&#8217;m using App Engine Patch 1.0 and Boto 1.6a. Sorry I haven&#8217;t updated my blog in [...]]]></description>
			<content:encoded><![CDATA[<h3>Update:</h3>
<p>Mitch Garnaat from <a href="http://cloudright.com/">CloudRight</a> has pointed out that you can actually set the policy of the S3 file in the set_contents_from_file call instead of making another roundtrip request into S3 (and saving you some coin).  <strong>Thanks Mitch!</strong></p>
<p><strong>Btw, I&#8217;m using App Engine Patch 1.0 and Boto 1.6a.</strong></p>
<hr/>
<p>Sorry I haven&#8217;t updated my blog in a few <strike>weeks</strike> months, but I&#8217;ve been a little busy.  With that said, along with <a href="http://erlang.org">Erlang</a>, I&#8217;ve been playing around with <a href="http://code.google.com/appengine">Google App Engine</a>, <a href="http://code.google.com/p/app-engine-patch/">App Engine Patch</a> (for <a href="http://www.djangoproject.com">Django</a> support), and the <a href="http://code.google.com/p/boto">Boto library</a> (for <a href="https://s3.amazonaws.com">Amazon S3</a> support).  After not having touched Python code in a few months, I wanted to document some of my lessons learned to help over developers who may be in a similar boat.</p>
<h2>Lessons learned</h2>
<ul>
<li>If you&#8217;re upgrading the App Engine Patch, make sure you don&#8217;t have the App Engine library installed in a hidden directory</li>
<li>Uploading bulk data changed ever so slightly</li>
<li>If you&#8217;re not running off of Boto&#8217;s trunk, you&#8217;ll need to patch your Boto installation to work with App Engine.</li>
</ul>
<h3>Make sure the App Engine library isn&#8217;t installed in a hidden directory</h3>
<p>Apparently, Google&#8217;s SDK 1.1.9 doesn&#8217;t like to rely on files that won&#8217;t be uploaded with your application &#8211; and hidden directories are no longer uploaded.  I was running into the dreaded purple-nurple screen of death.  Thank goodness for this <a href="http://groups.google.com/group/google-appengine/browse_thread/thread/7b2445428b8115c2/c09621c0bf3f038d?#c09621c0bf3f038d">AppEngine Google Group post</a>, but I&#8217;m still not even sure when this popped up considering Google&#8217;s articles still <a href="http://code.google.com/appengine/articles/appengine_helper_for_django.html">refer to this setup</a>.</p>
<h3>Bulk upload</h3>
<p>Compared to the previous SDK I was playing around with, bulk uploading changed significantly.  I recall having to patch Goog&#8217;s bulkupload.py file to get unicode support.  However, their new <a href="http://code.google.com/appengine/articles/remote_api.html">remote api tool</a> has definitely fixed this issue, so +1 for Googs.  People are <a href="http://code.google.com/p/googleappengine/issues/detail?id=157">reporting that uploading unicode</a> is still broken, but it&#8217;s not.  <em>Or at least it wasn&#8217;t for me.</em>  Second, if you&#8217;re like me and don&#8217;t read documentation, you&#8217;ll find out (the hard way) that the method signature to HandleEntity changed.  Instead of accepting a datastore.Entity, it&#8217;s now expecting a db.Model object.</p>
<p><strong>Note:</strong>  When actually running the remote api tool, you&#8217;ll also want to make sure your PYTHONPATH includes your current project.  (Another one-liner in the documentation. <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' /> )</p>
<h3>Integrating Boto + App Engine</h3>
<p>I wasn&#8217;t running off of Boto&#8217;s trunk and I was getting an obscure type conversion error.  Being too lazy to check out the source, I jumped to their issue tracker and <a href="http://code.google.com/p/boto/issues/detail?id=115">found a patch</a> (halfway down the page) by one of the App Engine Patch lead devs.  Apply the patch and you&#8217;ll be on your way to uploading images/data from App Engine into Amazon S3!  If you&#8217;re looking for example code, I&#8217;ve included a small snippet of what I tested.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">    @<span style="color: #008000;">staticmethod</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> upload_to_s3<span style="color: black;">&#40;</span>original_filename, photo<span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot; Upload a photo file, storing its original name as metadata in an S3 bucket &quot;&quot;&quot;</span>
        connection = Connection<span style="color: black;">&#40;</span>settings.<span style="color: black;">AWS_ACCESS_KEY_ID</span>, settings.<span style="color: black;">AWS_SECRET_ACCESS_KEY</span><span style="color: black;">&#41;</span>
        bucket = connection.<span style="color: black;">get_bucket</span><span style="color: black;">&#40;</span>settings.<span style="color: black;">AWS_IMAGE_BUCKET_NAME</span><span style="color: black;">&#41;</span>
        photo_uuid = <span style="color: #008000;">str</span><span style="color: black;">&#40;</span>uuid.<span style="color: black;">uuid4</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        new_key = Key<span style="color: black;">&#40;</span>bucket<span style="color: black;">&#41;</span>
        new_key.<span style="color: black;">key</span> = photo_uuid
        new_key.<span style="color: black;">set_metadata</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'original_filename'</span>, original_filename<span style="color: black;">&#41;</span>
        new_key.<span style="color: black;">set_contents_from_file</span><span style="color: black;">&#40;</span>photo, policy=<span style="color: #483d8b;">'public-read'</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> photo_uuid</pre></td></tr></table></div>

<p><strong>Note: </strong> I only tested the code above with small images ~300-500K in size and it seemed to work perfectly fine (with no load! <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' /> ).  As always, feel free to use, steal, take, and/or copy anything on this blog.  Hopefully somewhere, someone on the Interwebs will find these tips handy!</p>
<h2>Enjoy!</h2>
<img src="http://blog.localkinegrinds.com/?ak_action=api_record_view&id=348&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://blog.localkinegrinds.com/2009/04/02/lessons-learned-google-app-engine-app-engine-patch-django-boto/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>web2email.py &#8211; A web to email Python backup script</title>
		<link>http://blog.localkinegrinds.com/2008/09/02/web2emailpy-a-web-to-email-python-backup-script/</link>
		<comments>http://blog.localkinegrinds.com/2008/09/02/web2emailpy-a-web-to-email-python-backup-script/#comments</comments>
		<pubDate>Tue, 02 Sep 2008 08:14:43 +0000</pubDate>
		<dc:creator>ryankanno</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[archive]]></category>
		<category><![CDATA[backup]]></category>
		<category><![CDATA[email]]></category>
		<category><![CDATA[script]]></category>
		<category><![CDATA[smtp]]></category>

		<guid isPermaLink="false">http://blog.localkinegrinds.com/?p=191</guid>
		<description><![CDATA[I&#8217;m back, at least for the time being. There&#8217;s definitely a calm before the impending storm, but until then, I&#8217;m back posting little tidbits of uselessness. Enjoy! Python goodness While introducing the concept of automation to a friend of mine, I came across a requirement to archive a series of URL&#8217;s on a daily basis. [...]]]></description>
			<content:encoded><![CDATA[<div class="info"><b>I&#8217;m back</b>, at least for the time being.  There&#8217;s definitely a calm before the impending storm, but until then, I&#8217;m back posting little tidbits of uselessness.  <strong>Enjoy!</strong></div>
<h2>Python goodness</h2>
<p>While introducing the concept of automation to a friend of mine, I came across a requirement to archive a series of URL&#8217;s on a daily basis.  Luckily for me, the URL&#8217;s consisted primarily of plain text.  Loading up VIM, I concocted this Python script in a few hours &#8211; most of which was spent searching Googs <3.  </p>
<p>If you're looking for a true web crawler, this won't be for you - though loading up <a href="http://codespeak.net/lxml/">lxml</a>/<a href="http://www.crummy.com/software/BeautifulSoup/">Beautiful Soup</a>, <a href="http://code.google.com/p/cssutils/">cssutils</a>, and a Javascript parser to determine what artifacts need to be downloaded shouldn&#8217;t be all that difficult&#8230;</p>
<h3>But, I&#8217;ll leave that as an exercise for the reader (That&#8217;s you, btw!)</h3>
<p>In any case, the following script crawls a URL and sends the page via Googs or <a href="http://www.webfaction.com">Webfaction</a> via SMTP-AUTH or via a plain SMTP server of your choosing.  Sorta-kinda like having your own <a href="http://www.archive.org/web/web.php">WayBackMachine</a>.  In any case, cut and paste the following into a neat file called web2email.py.<br />
<br/></p>

<div class="wp_syntax"><table><tr><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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#! /usr/bin/env python2.5</span>
<span style="color: #808080; font-style: italic;"># -*- coding: utf-8 -*-</span>
<span style="color: #808080; font-style: italic;">#</span>
<span style="color: #808080; font-style: italic;"># Copyright (c) 2008 Ryan Kanno (ryankanno@localkinegrinds.com)</span>
<span style="color: #808080; font-style: italic;"># License: GNU GPLv3</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">urllib2</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">smtplib</span>
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">email</span>.<span style="color: black;">MIMEMultipart</span> <span style="color: #ff7700;font-weight:bold;">import</span> MIMEMultipart
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">email</span>.<span style="color: black;">MIMEBase</span> <span style="color: #ff7700;font-weight:bold;">import</span> MIMEBase
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">email</span>.<span style="color: black;">MIMEText</span> <span style="color: #ff7700;font-weight:bold;">import</span> MIMEText
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">email</span>.<span style="color: black;">Utils</span> <span style="color: #ff7700;font-weight:bold;">import</span> COMMASPACE, formatdate
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">email</span> <span style="color: #ff7700;font-weight:bold;">import</span> Encoders
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">datetime</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">optparse</span> <span style="color: #ff7700;font-weight:bold;">import</span> OptionParser
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>, <span style="color: #dc143c;">logging</span>
&nbsp;
__doc__ = <span style="color: #483d8b;">&quot;&quot;&quot;
&nbsp;
This script retrieves a URL and sends its contents via email to 
a list of recipients.  Typically, this script is run from a cron
job that sends emails to a Gmail account to archive the contents
of a URL.
&nbsp;
Mail can be sent via normal or authenticated SMTP.  Tested using 
Gmail SMTP (authenticated), Webfaction SMTP (authenticated), and
localhost (normal).
&nbsp;
Example:
&nbsp;
Sends the contents of http://www.espn.com to friend@domain.com using your Gmail settings
&nbsp;
    python web2email.py -u gmail_username <span style="color: #000099; font-weight: bold;">\</span>
                        -p gmail_password <span style="color: #000099; font-weight: bold;">\</span>
                        -f gmail_username@gmail.com <span style="color: #000099; font-weight: bold;">\</span>
                        -r friend@domain.com http://www.espn.com
&nbsp;
Sends the contents of http://www.espn.com to friend@domain.com using your Webfaction settings
&nbsp;
    python web2email.py -u webfaction_username <span style="color: #000099; font-weight: bold;">\</span>
                        -p webfaction_password <span style="color: #000099; font-weight: bold;">\</span>
                        -f webfaction_account@webfaction_domain.com <span style="color: #000099; font-weight: bold;">\</span>
                        -s smtp.webfaction.com <span style="color: #000099; font-weight: bold;">\</span>
                        -r friend@domain.com http://www.espn.com
&nbsp;
Sends the contents of http://www.espn.com to friend@domain.com using your local settings
&nbsp;
    python web2email.py -f your_email@domain.com <span style="color: #000099; font-weight: bold;">\</span>
                        -s localhost <span style="color: #000099; font-weight: bold;">\</span>
                        --port 25 <span style="color: #000099; font-weight: bold;">\</span>
                        -r friend@domain.com http://www.espn.com
&quot;&quot;&quot;</span>
&nbsp;
__author__  = <span style="color: #483d8b;">&quot;ryankanno@localkinegrinds.com&quot;</span>
__url__     = <span style="color: #483d8b;">&quot;http://blog.localkinegrinds.com&quot;</span>
__version__ = <span style="color: #483d8b;">&quot;0.1&quot;</span>
&nbsp;
USAGE = <span style="color: #483d8b;">&quot;usage: %prog [options] url&quot;</span> 
DESC  = __doc__.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\n</span><span style="color: #000099; font-weight: bold;">\n</span>'</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> configure_logging<span style="color: black;">&#40;</span>log_level, format=<span style="color: #483d8b;">'%(asctime)s %(levelname)s %(message)s'</span><span style="color: black;">&#41;</span>:
    <span style="color: #dc143c;">logging</span>.<span style="color: black;">basicConfig</span><span style="color: black;">&#40;</span>level=log_level, format=format<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> _validate_options_and_args<span style="color: black;">&#40;</span><span style="color: #dc143c;">parser</span>, options, args<span style="color: black;">&#41;</span>:
    <span style="color: #dc143c;">logging</span>.<span style="color: black;">debug</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Validating options and arguments.&quot;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: black;">&#40;</span><span style="color: #008000;">len</span><span style="color: black;">&#40;</span>args<span style="color: black;">&#41;</span> <span style="color: #66cc66;">!</span>= <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>:
        <span style="color: #dc143c;">parser</span>.<span style="color: black;">error</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Incorrect number of arguments.  Script expects 1 (URL to backup), but received %i.&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>args<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        <span style="color: #dc143c;">sys</span>.<span style="color: black;">exit</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span> <span style="color: #808080; font-style: italic;"># Command line syntax error</span>
    <span style="color: #ff7700;font-weight:bold;">elif</span> <span style="color: #ff7700;font-weight:bold;">not</span> options.<span style="color: black;">recipients</span>: 
        <span style="color: #dc143c;">parser</span>.<span style="color: black;">error</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;You must include at least one recipient.&quot;</span><span style="color: black;">&#41;</span>
        <span style="color: #dc143c;">sys</span>.<span style="color: black;">exit</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span> 
    <span style="color: #ff7700;font-weight:bold;">elif</span> <span style="color: black;">&#40;</span>options.<span style="color: black;">username</span> <span style="color: #ff7700;font-weight:bold;">and</span> options.<span style="color: black;">password</span> <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #008000;">None</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: black;">&#40;</span>options.<span style="color: black;">username</span> <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #008000;">None</span> <span style="color: #ff7700;font-weight:bold;">and</span> options.<span style="color: black;">password</span> <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">None</span><span style="color: black;">&#41;</span>:
        <span style="color: #dc143c;">parser</span>.<span style="color: black;">error</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;You must include both a username and password.&quot;</span><span style="color: black;">&#41;</span>
        <span style="color: #dc143c;">sys</span>.<span style="color: black;">exit</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span> 
    <span style="color: #ff7700;font-weight:bold;">elif</span> <span style="color: #ff7700;font-weight:bold;">not</span> options.<span style="color: black;">from_email</span>:
        <span style="color: #dc143c;">parser</span>.<span style="color: black;">error</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;You must include a valid from email address.&quot;</span><span style="color: black;">&#41;</span>
        <span style="color: #dc143c;">sys</span>.<span style="color: black;">exit</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span> 
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> getPage<span style="color: black;">&#40;</span>url<span style="color: black;">&#41;</span>:
    <span style="color: #dc143c;">logging</span>.<span style="color: black;">debug</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Attempting to retrieve %s&quot;</span> <span style="color: #66cc66;">%</span> url<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">try</span>:
        response = <span style="color: #dc143c;">urllib2</span>.<span style="color: black;">urlopen</span><span style="color: black;">&#40;</span>url<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> response.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #dc143c;">urllib2</span>.<span style="color: black;">HTTPError</span>, e:
        <span style="color: #dc143c;">logging</span>.<span style="color: black;">error</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;HTTPError (%s) occurred retrieving %s&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>e.<span style="color: #dc143c;">code</span>, url<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        <span style="color: #dc143c;">sys</span>.<span style="color: black;">exit</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #dc143c;">urllib2</span>.<span style="color: black;">URLError</span>, e:
        <span style="color: #dc143c;">logging</span>.<span style="color: black;">error</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;URLError (%s) occurred retrieving %s&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>e.<span style="color: black;">reason</span>, url<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        <span style="color: #dc143c;">sys</span>.<span style="color: black;">exit</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> mail<span style="color: black;">&#40;</span>send_from, send_to, subject, text, content_type, files=<span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>, server=<span style="color: #483d8b;">'localhost'</span>, port=<span style="color: #ff4500;">25</span>, username=<span style="color: #008000;">None</span>, password=<span style="color: #008000;">None</span><span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> _auth<span style="color: black;">&#40;</span>server, port, username, password<span style="color: black;">&#41;</span>:
        <span style="color: #dc143c;">logging</span>.<span style="color: black;">debug</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Attempting to send email via %s:%i using the following credentials (%s:%s).&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>server, port, username, password<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        smtp = <span style="color: #dc143c;">smtplib</span>.<span style="color: black;">SMTP</span><span style="color: black;">&#40;</span>server, port<span style="color: black;">&#41;</span> 
        smtp.<span style="color: black;">ehlo</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        smtp.<span style="color: black;">starttls</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        smtp.<span style="color: black;">ehlo</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        smtp.<span style="color: black;">login</span><span style="color: black;">&#40;</span>username, password<span style="color: black;">&#41;</span>
        smtp.<span style="color: black;">sendmail</span><span style="color: black;">&#40;</span>username, send_to, msg.<span style="color: black;">as_string</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        smtp.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> _unauth<span style="color: black;">&#40;</span>server, port<span style="color: black;">&#41;</span>:
        <span style="color: #dc143c;">logging</span>.<span style="color: black;">debug</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Attempting to send email via %s:%i&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>server, port<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        smtp = <span style="color: #dc143c;">smtplib</span>.<span style="color: black;">SMTP</span><span style="color: black;">&#40;</span>server, port<span style="color: black;">&#41;</span>
        smtp.<span style="color: black;">sendmail</span><span style="color: black;">&#40;</span>send_from, send_to, msg.<span style="color: black;">as_string</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        smtp.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">assert</span> <span style="color: #008000;">type</span><span style="color: black;">&#40;</span>send_to<span style="color: black;">&#41;</span>==<span style="color: #008000;">list</span>
&nbsp;
    msg=MIMEMultipart<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    msg<span style="color: black;">&#91;</span><span style="color: #483d8b;">'From'</span><span style="color: black;">&#93;</span> = send_from
    msg<span style="color: black;">&#91;</span><span style="color: #483d8b;">'To'</span><span style="color: black;">&#93;</span> = COMMASPACE.<span style="color: black;">join</span><span style="color: black;">&#40;</span>send_to<span style="color: black;">&#41;</span>
    msg<span style="color: black;">&#91;</span><span style="color: #483d8b;">'Date'</span><span style="color: black;">&#93;</span> = formatdate<span style="color: black;">&#40;</span>localtime=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
    msg<span style="color: black;">&#91;</span><span style="color: #483d8b;">'Subject'</span><span style="color: black;">&#93;</span> = subject
&nbsp;
    text = MIMEText<span style="color: black;">&#40;</span>text<span style="color: black;">&#41;</span>
    text.<span style="color: black;">set_type</span><span style="color: black;">&#40;</span>content_type<span style="color: black;">&#41;</span>
    text.<span style="color: black;">set_param</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'charset'</span>, <span style="color: #483d8b;">'UTF-8'</span><span style="color: black;">&#41;</span>
&nbsp;
    msg.<span style="color: black;">attach</span><span style="color: black;">&#40;</span>text<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">for</span> f <span style="color: #ff7700;font-weight:bold;">in</span> files:
        part = MIMEBase<span style="color: black;">&#40;</span><span style="color: #483d8b;">'application'</span>, <span style="color: #483d8b;">&quot;octet-stream&quot;</span><span style="color: black;">&#41;</span>
        part.<span style="color: black;">set_payload</span><span style="color: black;">&#40;</span><span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #008000;">file</span>,<span style="color: #483d8b;">&quot;rb&quot;</span><span style="color: black;">&#41;</span>.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        Encoders.<span style="color: black;">encode_base64</span><span style="color: black;">&#40;</span>part<span style="color: black;">&#41;</span>
        part.<span style="color: black;">add_header</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Content-Disposition'</span>, <span style="color: #483d8b;">'attachment; filename=&quot;%s&quot;'</span> <span style="color: #66cc66;">%</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">basename</span><span style="color: black;">&#40;</span>f<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        msg.<span style="color: black;">attach</span><span style="color: black;">&#40;</span>part<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> username <span style="color: #ff7700;font-weight:bold;">and</span> <span style="color: #ff7700;font-weight:bold;">not</span> password:
        _unauth<span style="color: black;">&#40;</span>server, port<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
        _auth<span style="color: black;">&#40;</span>server, port, username, password<span style="color: black;">&#41;</span> 
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #dc143c;">parser</span> = OptionParser<span style="color: black;">&#40;</span>usage=USAGE, description=DESC<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #dc143c;">parser</span>.<span style="color: black;">add_option</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;-u&quot;</span>, <span style="color: #483d8b;">&quot;--username&quot;</span>, dest=<span style="color: #483d8b;">&quot;username&quot;</span>, metavar=<span style="color: #483d8b;">&quot;USER&quot;</span>, <span style="color: #008000;">help</span>=<span style="color: #483d8b;">&quot;Username to SMTP server&quot;</span><span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">parser</span>.<span style="color: black;">add_option</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;-p&quot;</span>, <span style="color: #483d8b;">&quot;--password&quot;</span>, dest=<span style="color: #483d8b;">&quot;password&quot;</span>, metavar=<span style="color: #483d8b;">&quot;PWD&quot;</span>, <span style="color: #008000;">help</span>=<span style="color: #483d8b;">&quot;Password to SMTP server&quot;</span><span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">parser</span>.<span style="color: black;">add_option</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;-s&quot;</span>, <span style="color: #483d8b;">&quot;--server&quot;</span>, dest=<span style="color: #483d8b;">&quot;server&quot;</span>, metavar=<span style="color: #483d8b;">&quot;SERVER&quot;</span>, <span style="color: #008000;">help</span>=<span style="color: #483d8b;">&quot;SMTP server (Defaults to Gmail)&quot;</span><span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">parser</span>.<span style="color: black;">add_option</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;--port&quot;</span>, dest=<span style="color: #483d8b;">&quot;port&quot;</span>, metavar=<span style="color: #483d8b;">&quot;PORT&quot;</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">&quot;int&quot;</span>, <span style="color: #008000;">help</span>=<span style="color: #483d8b;">&quot;SMTP server port (Defaults to Gmail)&quot;</span><span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">parser</span>.<span style="color: black;">add_option</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;-f&quot;</span>, <span style="color: #483d8b;">&quot;--from&quot;</span>, dest=<span style="color: #483d8b;">&quot;from_email&quot;</span>, metavar=<span style="color: #483d8b;">&quot;FROM&quot;</span>, <span style="color: #008000;">help</span>=<span style="color: #483d8b;">&quot;From address&quot;</span><span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">parser</span>.<span style="color: black;">add_option</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;-r&quot;</span>, <span style="color: #483d8b;">&quot;--recipient&quot;</span>, action=<span style="color: #483d8b;">&quot;append&quot;</span>, dest=<span style="color: #483d8b;">&quot;recipients&quot;</span>, metavar=<span style="color: #483d8b;">&quot;RCPT&quot;</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">&quot;string&quot;</span>, <span style="color: #008000;">help</span>=<span style="color: #483d8b;">&quot;Email recipient&quot;</span><span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">parser</span>.<span style="color: black;">add_option</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'-t'</span>, <span style="color: #483d8b;">'--test'</span>, action=<span style="color: #483d8b;">&quot;store_true&quot;</span>, dest=<span style="color: #483d8b;">&quot;test&quot;</span>, metavar=<span style="color: #483d8b;">&quot;TEST&quot;</span>, <span style="color: #008000;">help</span>=<span style="color: #483d8b;">&quot;Run tests&quot;</span><span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">parser</span>.<span style="color: black;">add_option</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'-v'</span>, <span style="color: #483d8b;">'--verbose'</span>, action=<span style="color: #483d8b;">'store_const'</span>, dest=<span style="color: #483d8b;">'log_level'</span>, const=<span style="color: #dc143c;">logging</span>.<span style="color: black;">DEBUG</span>, <span style="color: #008000;">help</span>=<span style="color: #483d8b;">'Verbose output'</span><span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">parser</span>.<span style="color: black;">set_defaults</span><span style="color: black;">&#40;</span>server=<span style="color: #483d8b;">&quot;smtp.gmail.com&quot;</span>, port=<span style="color: #ff4500;">587</span>, <span style="color: #dc143c;">test</span>=<span style="color: #008000;">False</span>, log_level=<span style="color: #dc143c;">logging</span>.<span style="color: black;">INFO</span><span style="color: black;">&#41;</span>
    <span style="color: black;">&#40;</span>options, args<span style="color: black;">&#41;</span> = <span style="color: #dc143c;">parser</span>.<span style="color: black;">parse_args</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    _validate_options_and_args<span style="color: black;">&#40;</span><span style="color: #dc143c;">parser</span>, options, args<span style="color: black;">&#41;</span>
    configure_logging<span style="color: black;">&#40;</span>options.<span style="color: black;">log_level</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">if</span> options.<span style="color: #dc143c;">test</span>:
        _test<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: #808080; font-style: italic;"># Too lazy to write a test for this script.  @TODO - use mocks </span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Retrieve URL and return html</span>
    html = getPage<span style="color: black;">&#40;</span>args<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Send mail with returned html as body </span>
    mail<span style="color: black;">&#40;</span>options.<span style="color: black;">from_email</span>, options.<span style="color: black;">recipients</span>, 
         <span style="color: #483d8b;">'%s @ %s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>args<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>, <span style="color: black;">&#40;</span><span style="color: #dc143c;">datetime</span>.<span style="color: #dc143c;">datetime</span>.<span style="color: black;">now</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">strftime</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;%A %B %d %I:%M:%S %p %Y&quot;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>, 
         html, <span style="color: #483d8b;">'text/html'</span>, 
         server=options.<span style="color: black;">server</span>, port=options.<span style="color: black;">port</span>, username=options.<span style="color: black;">username</span>, password=options.<span style="color: black;">password</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Return with appropriate exit code</span>
    <span style="color: #dc143c;">sys</span>.<span style="color: black;">exit</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> _test<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">doctest</span>
    <span style="color: #dc143c;">doctest</span>.<span style="color: black;">testmod</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">sys</span>.<span style="color: black;">modules</span><span style="color: black;">&#91;</span>__name__<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">'__main__'</span>:
    main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<h3>All right stop, cron time! (imagine a 90&#8242;s pop song)</h3>
<p>As an added bonus, you can install this script to run via <a href="http://en.wikipedia.org/wiki/Cron">cron</a> so you&#8217;ll magically end up with webpages archived in your inbox!  Neat.  You can read my <a href="http://blog.localkinegrinds.com/2008/02/10/backing-up-your-subversion-svn-repository-on-dreamhost-with-cron/">previous post on cron</a>, or you can create the following crontab.</p>
<pre class="script">
MAILTO=ryankanno@CHANGE_TO_YOUR_EMAIL.com
# minute (0-59),
# |      hour (0-23),
# |      |       day of the month (1-31),
# |      |       |       month of the year (1-12),
# |      |       |       |       day of the week (0-6 with 0=Sunday).
# |      |       |       |       |       commands
  0      0       *       *       *      /usr/bin/python2.5 /PATH/TO/web2email.py -u GMAIL_USER -p GMAIL_PWD -f FROM_USER -r RECIPIENT URL
</pre>
<p><strong>As a side note, don&#8217;t forget double quotes around URL if there&#8217;s spaces!</strong></p>
<div style="font:bold 1.1em arial; background-color:#E5F3FF; padding:1em; margin:1em 0; border:1px solid #114477; color:#33415D;">Notice, change the value of ryankanno@CHANGE_TO_YOUR_EMAIL.com to your email address (or comment the line out with a # if you don&#8217;t want emails sent to you), GMAIL_USER to your Google username, GMAIL_PWD to your Google password, FROM_USER to the from address in the mail header, RECIPIENT to the recipient email address, and URL to the URL you want backed up.</div>
<h3>I know, I know. The critics.</h3>
<p>The critics will say that your Gmail username and password are in cleartext.  I know.  <strong>They are.</strong>  So&#8230; I&#8217;m hoping that since you just need an archive of a publicly available URL on the Internets, the data doesn&#8217;t need to be super-duper-Fort-Knox-protected.  If it does, this script isn&#8217;t for you. <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_sad.gif' alt=':(' class='wp-smiley' />   Oh, yeah, before I forget&#8230; here&#8217;s a hint&#8230; *<strong>cough</strong>*create another Google account*<strong>cough</strong>*.  With that said, archive to your heart&#8217;s content!</p>
<h2>Enjoy!</h2>
<img src="http://blog.localkinegrinds.com/?ak_action=api_record_view&id=191&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://blog.localkinegrinds.com/2008/09/02/web2emailpy-a-web-to-email-python-backup-script/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Google App Engine on Win2K (using django-yui-layout-templates)</title>
		<link>http://blog.localkinegrinds.com/2008/04/17/google-app-engine-on-win2k-using-django-yui-layout-templates/</link>
		<comments>http://blog.localkinegrinds.com/2008/04/17/google-app-engine-on-win2k-using-django-yui-layout-templates/#comments</comments>
		<pubDate>Thu, 17 Apr 2008 10:38:38 +0000</pubDate>
		<dc:creator>ryankanno</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[appengine]]></category>
		<category><![CDATA[django-yui-layout-templates]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[templates]]></category>
		<category><![CDATA[yahoo!-ui]]></category>
		<category><![CDATA[yui]]></category>

		<guid isPermaLink="false">http://blog.localkinegrinds.com/?p=171</guid>
		<description><![CDATA[Update : September 1, 2008 I guess Googs finally caught on as their 1.1.2 installer works on Win2K! FTW! Update After finally getting time to play around with the Google App Engine Django helpers, here&#8217;s a few more steps to integrate nicely with the helper suite. Move the appengine installation from C:\AppEngine\ to where the [...]]]></description>
			<content:encoded><![CDATA[<h3>Update : September 1, 2008</h3>
<p>I guess Googs finally caught on as their <a href="http://googleappengine.googlecode.com/files/GoogleAppEngine_1.1.2.msi">1.1.2 installer</a> works on Win2K!  FTW!</p>
<hr/>
<h3>Update</h3>
<p>After finally getting time to play around with the <a href="http://code.google.com/p/google-app-engine-django/">Google App Engine Django helpers</a>, here&#8217;s a few more steps to integrate nicely with the helper suite.  </p>
<ul>
<li>Move the appengine installation from C:\AppEngine\ to where the Windows installer would have installed it to: <strong>C:\Program Files\Google\google_appengine</strong> (make sure to clean up your .pyc files)
<li>Add the following to your PYTHONPATH system variable: <strong>%APPENGINE%\;%APPENGINE%\lib;%APPENGINE%\lib\yaml\lib;%APPENGINE%\lib\webob;</strong></li>
</ul>
<p>After <a href="http://code.google.com/appengine/articles/appengine_helper_for_django.html">following the instructions</a>, you should be good to go with <a href="http://www.djangoproject.com">Django</a> + <a href="http://code.google.com/appengine/">AppEngine</a>! FTW! Whee. <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<hr/>
So I finally get an hour or so to play around with the <a href="http://code.google.com/appengine/">Googs App Engine</a> and luckily for me, all my machines decided to puke except for my Windows 2000 Server.  How ironic is that?  In disbelief, I downloaded <a href="http://code.google.com/appengine/downloads.html">the Google App Engine SDK Windows installer</a> and what do I get?</p>
<p class="center">
<a href='http://blog.localkinegrinds.com/wp-content/uploads/2008/04/google_windows_installer.jpg' rel="lightbox" title="h.a.t.e.r.a.d.e I tell you"><img src="http://blog.localkinegrinds.com/wp-content/uploads/2008/04/google_windows_installer-300x231.jpg" alt="Google App Engine Windows installer" title="google_windows_installer" width="300" height="231" class="thumb"/></a>
</p>
<h4>I sense some pure, unadultered haterade. (j/k)</h4>
<p>Since <a href="http://www.python.org">Python</a> is one of those insert_any_synonym_for_fun languages that just works, here&#8217;s how to get the Google App Engine SDK working in Win2K.  </p>
<ul>
<li><a href="http://code.google.com/appengine/downloads.html">Download</a> the Linux/Other platform package and unzip to somewhere neat.</li>
<li>Add a System Environment variable called &#8216;APP_ENGINE_HOME&#8217; that points to your App Engine installation.  (Notice, I installed mine into <strong>C:\AppEngine</strong>)
<p class="center"><a href='http://blog.localkinegrinds.com/wp-content/uploads/2008/04/system_variable.jpg' rel="lightbox" title="Adding a system environment variable"><img src="http://blog.localkinegrinds.com/wp-content/uploads/2008/04/system_variable-259x300.jpg" alt="Add system variable" title="Add system variable" width="259" height="300" class="thumb" /></a></p>
</li>
<li>Add the System Environment variable to your System Path so the Windows shell can execute the included Python files.</li>
<li>Make sure you have .py files associated with the python.exe executable located in your Python installation.  (Check file types under folder options)</li>
<li>Follow the tutorials: <a href="http://code.google.com/appengine/docs/gettingstarted/">here</a> and <a href="http://www.42topics.com/dumps/django/docs.html">here</a>, or <a href="http://www.appenginebase.com/">learn with others</a> &#8211; just to name a few.</li>
<li>Oh, and before I forget, if you develop an application and realize that you can&#8217;t kill the development appserver (<strong>dev_appserver.py</strong>) by pressing Ctrl-C, I <a href="http://boodebr.org/main/python/cherrypy-under-google-appserver">found a solution here</a>.  Basically, press Ctrl-C, hit the server with your browser one more time and voila, the development application server dies.  Thanks Frank!</li>
</ul>
<h3>As an added bonus&#8230;</h3>
<div class="info">Checkout my <a href="http://blog.localkinegrinds.com/2008/03/21/yahoo-ui-yui-django-templates-google-code-project-ftw/">my previous post</a> using the <a href="http://developer.yahoo.com/yui/">Yahoo UI library</a> to create a set of default <a href="http://http://www.djangoproject.com/documentation/templates/">Django templates</a>.  I&#8217;ve updated <a href="http://code.google.com/p/django-yui-layout-templates/"><strong>django-yui-layout-templates</strong></a> with patches and suggestions, and I&#8217;ve also created a few branches to support the Googly App Engine.  Check out the <a href="http://django-yui-layout-templates.googlecode.com/svn/branches/">branches directory in the Subversion repository</a>!</div>
<h3>Last but not least&#8230;</h3>
<p>Big ups to <a href="http://fitz.blogspot.com/">Mr. Fitz</a> for solving all my Google App Engine issues and thanks to <a href="http://www.nata2.org/">Mr. Harper</a> for causing them. <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<h2>Voila! (Enjoy)</h2>
<img src="http://blog.localkinegrinds.com/?ak_action=api_record_view&id=171&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://blog.localkinegrinds.com/2008/04/17/google-app-engine-on-win2k-using-django-yui-layout-templates/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Yahoo! UI (YUI) + Django templates == Google Code project! FTW!</title>
		<link>http://blog.localkinegrinds.com/2008/03/21/yahoo-ui-yui-django-templates-google-code-project-ftw/</link>
		<comments>http://blog.localkinegrinds.com/2008/03/21/yahoo-ui-yui-django-templates-google-code-project-ftw/#comments</comments>
		<pubDate>Fri, 21 Mar 2008 19:56:50 +0000</pubDate>
		<dc:creator>ryankanno</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[base-css]]></category>
		<category><![CDATA[fonts-css]]></category>
		<category><![CDATA[grids-css]]></category>
		<category><![CDATA[layout]]></category>
		<category><![CDATA[reset-css]]></category>
		<category><![CDATA[templates]]></category>
		<category><![CDATA[yahoo!-ui]]></category>
		<category><![CDATA[yui]]></category>

		<guid isPermaLink="false">http://blog.localkinegrinds.com/2008/03/21/yahoo-ui-yui-django-templates-google-code-project-ftw/</guid>
		<description><![CDATA[Let me first preface this blog by saying that I&#8217;m not a designer. When it comes to art and creativity, I&#8217;m so left brained, I actually wonder if my right brain even partakes in the process. Three things spurred me to release django-yui-layout-templates. I&#8217;ve always wanted to see what GoogleCode offered in relation to SourceForge [...]]]></description>
			<content:encoded><![CDATA[<div class="disclaimer"><b>Let me first preface this blog by saying that I&#8217;m not a designer.</b>  When it comes to art and creativity, I&#8217;m so left brained, I actually wonder if my right brain even partakes in the process.</div>
<h3>Three things spurred me to release <a href="http://code.google.com/p/django-yui-layout-templates/">django-yui-layout-templates</a>.</h3>
<ol>
<li>I&#8217;ve always wanted to see what <a href="http://code.google.com/">GoogleCode</a> offered in relation to <a href="http://sourceforge.net/">SourceForge</a> / <a href="http://www.rubyforge.org/">RubyForge</a>.</li>
<li>I&#8217;m so caught up in corporate America staring at Java / Ruby code all day, not only haven&#8217;t I blogged about anything <a href="http://www.djangoproject.com/">Django</a> related in quite a while, but it&#8217;s nice to get some commentary from the community, i.e. &#8220;your code sucks&#8221;. (Brings me back to reality)</li>
<li>I found myself using the same templates on a variety of projects and figured that I could do my part and help eliminate unncessary cruft/duplication.</li>
</ol>
<p>So without further adieu, check out the project <a href="http://code.google.com/p/django-yui-layout-templates/">here</a>.  I know, I know &#8211; nothing revolutionary here, but I figure since Django is <a href="http://antoniocangiano.com/2008/03/20/djangos-tipping-point/">picking up some steam</a>, these templates might help a Djangonaut get a head start on their next million dollar idea. <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<h2>Voila! Enjoy!</h2>
<img src="http://blog.localkinegrinds.com/?ak_action=api_record_view&id=166&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://blog.localkinegrinds.com/2008/03/21/yahoo-ui-yui-django-templates-google-code-project-ftw/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Backing up your Subversion (SVN) repository on Dreamhost with cron</title>
		<link>http://blog.localkinegrinds.com/2008/02/10/backing-up-your-subversion-svn-repository-on-dreamhost-with-cron/</link>
		<comments>http://blog.localkinegrinds.com/2008/02/10/backing-up-your-subversion-svn-repository-on-dreamhost-with-cron/#comments</comments>
		<pubDate>Sun, 10 Feb 2008 22:02:56 +0000</pubDate>
		<dc:creator>ryankanno</dc:creator>
				<category><![CDATA[Dreamhost]]></category>
		<category><![CDATA[HowTo]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Subversion]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[back-up]]></category>
		<category><![CDATA[backup]]></category>
		<category><![CDATA[cron]]></category>
		<category><![CDATA[crontabs]]></category>
		<category><![CDATA[hotcopy]]></category>
		<category><![CDATA[scheduler]]></category>
		<category><![CDATA[svn]]></category>

		<guid isPermaLink="false">http://blog.localkinegrinds.com/2008/02/10/backing-up-your-subversion-svn-repository-on-dreamhost-with-cron/</guid>
		<description><![CDATA[Two events spurred me to write this blog. First, my 2 year old &#8220;Subversion + Dreamhost + Post-Commit&#8221; blog still gets quite a number of hits. Second, after the latest Dreamhost outage move, I&#8217;m beginning to feel a little more vigilant about backing up my data. As a standard disclaimer, if you&#8217;re not familiar with [...]]]></description>
			<content:encoded><![CDATA[<h3>Two events spurred me to write this blog.</h3>
<p>First, my <a href="http://blog.localkinegrinds.com/2006/12/15/dreamhost-subversion-post-commit-tutorial/">2 year old &#8220;Subversion + Dreamhost + Post-Commit&#8221; blog</a> still gets quite a number of hits.  Second, after the latest <a href="http://www.dreamhoststatus.com/2008/02/08/randy-move-going-ahead/">Dreamhost <strike>outage</strike> move</a>, I&#8217;m beginning to feel a little more vigilant about backing up my data.  </p>
<div class="disclaimer">As a standard disclaimer, if you&#8217;re not familiar with the Unix shell, I <strong>highly suggest</strong> you not try this unless under the supervision of someone who reads Perl books for fun.  By accessing your Dreamhost shell, you can seriously f-up your account and I will not fix it for you.  You have been warned. <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  (Don&#8217;t you just love smileys?)</div>
<h3>Setup</h3>
<p>There are a few prerequisites to being able to back up your <a href="http://subversion.tigris.org/">SVN</a> repository.</p>
<ol>
<li>First and foremost, you must have already installed a SVN repository into your Dreamhost account via the <a href="https://panel.dreamhost.com/index.cgi?tree=goodies.svn&#038;">control panel</a>.</li>
<li>Second, you must know how to <a href="http://wiki.dreamhost.com/Ssh">SSH into your Dreamhost account</a>.  As a FYI, you sorta-kinda-need to know what that means in order to follow this tutorial.</li>
</ol>
<h3>Grabbing the backup script</h3>
<p>Wait, you didn&#8217;t think I was writing my own right?  In any case, if you actually installed/compiled Subversion on your own, it would&#8217;ve contained this file, <a href="http://svn.collab.net/repos/svn/trunk/tools/backup/hot-backup.py.in">hotbackup.py</a>.  Fortunately for us, Dreamhost has this file conveniently available at: <strong>/usr/bin/svn-hot-backup</strong>, but it&#8217;s an older version of the backup script.  There are some subtle differences like being unable to pass in the number of backups you want the script to manage.  Personally, I like to be on the edge, so let&#8217;s get the latest version.  Execute the following commands from your home directory.</p>
<pre class="console">
$ cd ~
$ mkdir scripts
$ cd scripts
$ wget http://svn.collab.net/repos/svn/trunk/tools/backup/hot-backup.py.in
$ mv hot-backup.py.in svn-hot-backup.py
</pre>
<p>The commands issued above created a directory called scripts in your home directory, switched into the directory, downloaded the latest hot-backup.py file from <a href="http://www.collab.net">CollabNet</a>, and renamed it to svn-hot-backup.py.  Now that you have the file, you&#8217;ll need to make a few edits.  Personally, I&#8217;m accustomed to vi, but pick your poison (pico, nano, text editor of your choice) and find these two values (they should be close to the top of the file in consecutive lines).</p>
<pre class="script">
# Path to svnlook utility
svnlook = r"@SVN_BINDIR@/svnlook"

# Path to svnadmin utility
svnadmin = r"@SVN_BINDIR@/svnadmin"
</pre>
<p>and change them to the following:</p>
<pre class="script">
# Path to svnlook utility
svnlook = r"/usr/bin/svnlook"

# Path to svnadmin utility
svnadmin = r"/usr/bin/svnadmin"
</pre>
<p>(If you&#8217;re wondering, if and when you compile/install Subversion yourself, these two variables would have been automagically filled in for you.)</p>
<p>The python script we downloaded not only performs a <a href="http://svnbook.red-bean.com/en/1.0/re33.html">hotcopy</a> of your svn directory, but also can archive it and manage a set number of copies.  <strong>Pretty neat right?</strong></p>
<h3>Preparing for the backups</h3>
<p>Before you can actually back up your SVN repository, you&#8217;ll want to create a directory structure to manage your backups.  Execute the following commands from your home directory.</p>
<pre class="console">
$ cd ~
$ mkdir backup
$ cd backup
$ mkdir svn
$ cd ~/scripts
</pre>
<p>The commands issued above created a directory called <strong>backup</strong> in your home directory, switched into the directory, and created another directory called <strong>svn</strong> within the backup directory.  We&#8217;ll be using this directory to store all your backups.  Finally, we switched back into the scripts directory created in the previous steps.  Now that we have the backup script and directory structure to manage the back ups, let&#8217;s test it out!</p>
<p>Before you can back up your repository, you&#8217;ll have to know the name of the Subversion repository you&#8217;re trying to back up.  To find the name of your repository, you can either look in the svn directory in your home directory, or you can check out the <a href="https://panel.dreamhost.com/index.cgi?tree=goodies.svn&#038;">ID value in your Subversion Goodies control panel</a>.  In any case, remember the name of your SVN repository and issue the following commands.</p>
<pre class="console">
$ cd ~/scripts/
$ python2.4 svn-hot-backup.py --archive-type=zip --num-backups=10 ~/svn/REPOSITORY_NAME_HERE/ ~/backup/svn/
</pre>
<div style="font:bold 1.1em arial; background-color:#E5F3FF; padding:1em; margin:1em 0; border:1px solid #114477; color:#33415D;">Notice, change the value of REPOSITORY_NAME_HERE to the id of the SVN repository you want backed up.</div>
<p>You should see the following in the console:</p>
<pre class="console">
Beginning hot backup of '/home/USERNAME/svn/lkg/'.
Youngest revision is REVISION_NUMBER
Backing up repository to '/home/USERNAME/backup/svn/REPOSITORY_NAME_HERE-701'...
Done.
Archiving backup to '/home/USERNAME/backup/svn/REPOSITORY_NAME_HERE-701.zip'...
Archive created, removing backup '/home/USERNAME/backup/svn/REPOSITORY_NAME_HERE-701'...
</pre>
<div style="font:bold 1.1em arial; background-color:#DDFFDD; border:1px solid #009900; padding:1em; margin:1em 0;">If you see the following, the backup was a <span style="font:bold 1.25em arial; color:#006600;">success</span>!  You can even check on the file by changing into the backup/svn directory!</div>
<h2>Voila! (But there&#8217;s more)</h2>
<h3>Automating the backups</h3>
<p>Now that you actually have the script backing up your SVN repository, let&#8217;s automate them!  To do so, we&#8217;ll use the handy <a href="http://en.wikipedia.org/wiki/Crontab">cron daemon</a>.  Cron has similarities to the Windows task scheduler in that it provides a service that enables a user to execute commands at a specified date/time or set intervals.  To tell cron the tasks you want to execute, you&#8217;ll need to load a configuration file called a crontab.  You can read more about it <a href="http://blog.dreamhosters.com/kbase/index.cgi?area=2506">here</a> and <a href="http://wiki.dreamhost.com/Cron">here</a>. In any case, here&#8217;s what my crontab configuration file looks like.</p>
<pre class="script">
MAILTO=ryankanno@CHANGE_TO_YOUR_EMAIL.com
# minute (0-59),
# |      hour (0-23),
# |      |       day of the month (1-31),
# |      |       |       month of the year (1-12),
# |      |       |       |       day of the week (0-6 with 0=Sunday).
# |      |       |       |       |       commands
  0      0       *       *       *      /usr/bin/python2.4 /home/USERNAME/scripts/svn-hot-backup.py --archive-type=zip --num-backups=10 /home/USERNAME/svn/REPOSITORY_NAME/ /home/USERNAME/backup/svn/
</pre>
<p>Create a file in your scripts directory called svn_backup_once_a_day.cron and copy the contents above into your file.  I&#8217;ve setup my crontab to backup my svn repository once a day.</p>
<div style="font:bold 1.1em arial; background-color:#E5F3FF; padding:1em; margin:1em 0; border:1px solid #114477; color:#33415D;">Notice, change the value of ryankanno@CHANGE_TO_YOUR_EMAIL.com to your email address (or comment the line out with a # if you don&#8217;t want emails sent to you), USERNAME to your Dreamhost username, and REPOSITORY_NAME to your Subversion repository.</div>
<p>Once you have this file called svn_backup_once_a_day.cron in your scripts directory, load the file into your crontab by issuing the following command:</p>
<pre class="console">
$ crontab svn_backup_once_a_day.cron
</pre>
<p>As a FYI, this will replace your old crontab.  If you have other items already running on cron, it&#8217;s a good idea to list them via the crontab -l command first.  If you want to make sure that your cron will run, you can test it out by setting the values in the crontab to the time you want it to run.  I&#8217;ll leave this as an exercise to the reader. <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<h3>Storing your backups</h3>
<p>Though out of scope of this blog, you&#8217;ll still have to store your backups somewhere.  Please just don&#8217;t leave them in your <a href="http://www.dreamhost.com">Dreamhost</a> account.  Your best bet is probably to get an <a href="http://aws.amazon.com/s3">Amazon S3 account</a> and store your backups there.  Personally, I like to run another script immediately after the hotcopy finishes that pushes the backup to my S3 account.  Other options include scp/sftp&#8217;ing the backups to your home machine.  Here&#8217;s a <a href="http://blog.dreamhosters.com/kbase/index.cgi?area=2599">link</a> to read more about that option.</p>
<h2>Voila! Enjoy!</h2>
<img src="http://blog.localkinegrinds.com/?ak_action=api_record_view&id=165&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://blog.localkinegrinds.com/2008/02/10/backing-up-your-subversion-svn-repository-on-dreamhost-with-cron/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Using the extra() QuerySet modifier in Django for WeGoEat</title>
		<link>http://blog.localkinegrinds.com/2007/10/24/using-the-extra-queryset-modifier-in-django-for-wegoeat/</link>
		<comments>http://blog.localkinegrinds.com/2007/10/24/using-the-extra-queryset-modifier-in-django-for-wegoeat/#comments</comments>
		<pubDate>Wed, 24 Oct 2007 15:17:01 +0000</pubDate>
		<dc:creator>ryankanno</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[HowTo]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[extra]]></category>
		<category><![CDATA[hawaii]]></category>
		<category><![CDATA[queryset]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[wegoeat]]></category>

		<guid isPermaLink="false">http://blog.localkinegrinds.com/2007/10/24/using-the-extra-queryset-modifier-in-django-for-wegoeat/</guid>
		<description><![CDATA[Since I actually used this method to reduce the number of Update:&#8221;explicit&#8221; SQL calls made in WeGoEat, I figured I&#8217;d write a little blog explaining the context in which it was used, and maybe, just maybe, it&#8217;ll help shed some light on how others can take advantage of this neat little function. Background As a [...]]]></description>
			<content:encoded><![CDATA[<p>Since I actually used this method to reduce the number of <strong>Update:&#8221;explicit&#8221;</strong> SQL calls made in <a href="http://www.wegoeat.com">WeGoEat</a>, I figured I&#8217;d write a little blog explaining the context in which it was used, and maybe, just maybe, it&#8217;ll help shed some light on how others can take advantage of this neat little function.</p>
<h2>Background</h2>
<p>As a <a href="http://www.djangoproject.com">Django</a> &#8220;proof-of-concept&#8221;, I&#8217;m working on a <a href="http://www.wegoeat.com/">local restaurant review site</a> for my home state of Hawai`i.  (I actually just released it yesterday).  For each restaurant, I want to be able to calculate the average of all reviews and <a href="http://www.wegoeat.com/restaurants/browse/by/island/oahu/page/1/records/50/">display this listing in a paginated view</a>.  (<em>Yes, I do realize there&#8217;s no average rating, but that has to do with there being no users. ;P</em>).</p>
<h2>The Problem</h2>
<p>Having a serious &#8220;wtf was I thinking moment&#8221;, I initially wrote a Restaurant model function that returned the average (review) rating for each restaurant instance.  Little did I realize that when I actually displayed the restaurant&#8217;s average reviews, I would be making an additional SQL avg() call for <strong>every</strong> restaurant.  Though I&#8217;m paging &#8220;n&#8221; records at a time, this function added an additional &#8220;n&#8221; SQL calls for <a href="http://www.wegoeat.com/restaurants/browse/by/letter/a/page/1/records/50/">every</a> <a href="http://www.wegoeat.com/restaurants/browse/by/letter/b/page/1/records/50/">view</a> <a href="http://www.wegoeat.com/restaurants/browse/by/letter/c/page/1/records/50/">that</a> <a href="http://www.wegoeat.com/restaurants/browse/by/letter/d/page/1/records/50/">contained</a> <a href="http://www.wegoeat.com/restaurants/browse/by/letter/g/page/1/records/50/">a restaurant</a> <a href="http://www.wegoeat.com/restaurants/browse/by/letter/h/page/1/records/50/">listing</a>, just to name a few.</p>
<p>In pseudo-code, my initial naive function resembled the following:  (I&#8217;m sure we&#8217;re all guilty of writing something of the sort&#8230; ok, fine, I know I was. ;P)</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">     <span style="color: #ff7700;font-weight:bold;">def</span> get_average_review<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
         query = <span style="color: #483d8b;">'QUERY TO GET AVERAGE (SELECT AVG(rating)...); (I have the query below)'</span>
         <span style="color: #808080; font-style: italic;"># Get cursor from connection</span>
        cursor = connection.<span style="color: black;">cursor</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        cursor.<span style="color: black;">execute</span><span style="color: black;">&#40;</span>query<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> cursor.<span style="color: black;">fetchall</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p><strong>Duh.</strong></p>
<p>Here&#8217;s a picture of the number of queries it took:</p>
<div style="text-align:center; padding:1.5em;"><img src='http://blog.localkinegrinds.com/wp-content/uploads/2007/10/duh.jpg' alt='Duh' style="border:1px solid #aaa;"/></div>
<h2>The &#8220;extra()&#8221; solution</h2>
<p>After profiling my application and realizing what a bone-headed mistake I made, I began researching the <a href="http://www.djangoproject.com/documentation/db-api/#extra-select-none-where-none-params-none-tables-none">extra() Queryset modifier</a>.  Yes, I realize that these extra lookups aren&#8217;t the most portable and often violate the DRY principle, but it&#8217;ll probably suffice for <strike>most of</strike> all my personal projects. <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Since I&#8217;m already retrieving a list of Restaurants and filtering them via <a href="http://wegoeat.com/restaurants/browse/by/letter/">letter</a>, <a href="http://wegoeat.com/restaurants/browse/by/island/">island</a>, and <a href="http://wegoeat.com/restaurants/browse/">what not</a>, I figured I could add an average rating subquery.  The entire call looks as such:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">     restaurants = Restaurant.<span style="color: black;">objects</span>.<span style="color: #008000;">filter</span><span style="color: black;">&#40;</span>name__istartswith = letter<span style="color: black;">&#41;</span>.<span style="color: black;">extra</span><span style="color: black;">&#40;</span>
             <span style="color: #dc143c;">select</span>=<span style="color: black;">&#123;</span><span style="color: #483d8b;">'avg_rating'</span>: <span style="color: #483d8b;">'SELECT AVG(overall_rating) FROM restaurants_restaurant as res, reviews_review, django_content_type <span style="color: #000099; font-weight: bold;">\</span>
                                          WHERE restaurants_restaurant.id = res.id <span style="color: #000099; font-weight: bold;">\</span>
                                          AND res.id = reviews_review.object_id <span style="color: #000099; font-weight: bold;">\</span>
                                          AND reviews_review.content_type_id = django_content_type.id <span style="color: #000099; font-weight: bold;">\</span>
                                          AND django_content_type.model = <span style="color: #000099; font-weight: bold;">\'</span>restaurant<span style="color: #000099; font-weight: bold;">\'</span>'</span><span style="color: black;">&#125;</span>,
                       <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>As you can see, I&#8217;m exploiting the fact that restaurants_restaurant will be available from the Restaurant.objects.filter() call.  (I know, I know&#8230; bad for portability).</p>
<h2>But voila!</h2>
<p>Now, in my templates, when I iterate over the restaurants, I can get issue the following:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: black;">&#123;</span><span style="color: #66cc66;">%</span> <span style="color: #ff7700;font-weight:bold;">for</span> restaurant <span style="color: #ff7700;font-weight:bold;">in</span> restaurant_list <span style="color: #66cc66;">%</span><span style="color: black;">&#125;</span>
<span style="color: #66cc66;">&lt;</span>tr<span style="color: #66cc66;">&gt;</span>
    <span style="color: #66cc66;">&lt;</span>td<span style="color: #66cc66;">&gt;&lt;</span>a href=<span style="color: #483d8b;">&quot;{{restaurant.get_absolute_url}}&quot;</span><span style="color: #66cc66;">&gt;</span><span style="color: black;">&#123;</span><span style="color: black;">&#123;</span> restaurant.<span style="color: black;">name</span> <span style="color: black;">&#125;</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">&lt;</span>/a<span style="color: #66cc66;">&gt;&lt;</span>/td<span style="color: #66cc66;">&gt;</span>
    <span style="color: #66cc66;">&lt;</span>td<span style="color: #66cc66;">&gt;</span><span style="color: black;">&#123;</span><span style="color: #66cc66;">%</span> <span style="color: #ff7700;font-weight:bold;">if</span> restaurant.<span style="color: black;">avg_rating</span> <span style="color: #66cc66;">%</span><span style="color: black;">&#125;</span>
	   <span style="color: black;">&#123;</span><span style="color: #66cc66;">%</span> load show_stars <span style="color: #66cc66;">%</span><span style="color: black;">&#125;</span> 
           <span style="color: #66cc66;">&lt;</span>span <span style="color: #ff7700;font-weight:bold;">class</span>=<span style="color: #483d8b;">&quot;average-rating&quot;</span><span style="color: #66cc66;">&gt;</span>
	   <span style="color: black;">&#123;</span><span style="color: #66cc66;">%</span> show_stars <span style="color: #66cc66;">&lt;</span>strong<span style="color: #66cc66;">&gt;</span>restaurant.<span style="color: black;">avg_rating</span><span style="color: #66cc66;">&lt;</span>/strong<span style="color: #66cc66;">&gt;</span> of <span style="color: #ff4500;">5</span> <span style="color: #008000;">round</span> to quarter <span style="color: #66cc66;">%</span><span style="color: black;">&#125;</span>
           <span style="color: #66cc66;">&lt;</span>/span<span style="color: #66cc66;">&gt;</span>
           <span style="color: black;">&#123;</span><span style="color: #66cc66;">%</span> endif <span style="color: #66cc66;">%</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">&lt;</span>/td<span style="color: #66cc66;">&gt;</span>
<span style="color: #66cc66;">&lt;</span>/tr<span style="color: #66cc66;">&gt;</span>
<span style="color: black;">&#123;</span><span style="color: #66cc66;">%</span> endfor <span style="color: #66cc66;">%</span><span style="color: black;">&#125;</span></pre></td></tr></table></div>

<p>Notice how I used my <a href="http://blog.localkinegrinds.com/2007/10/02/displaying-stars-with-rounding-as-a-django-templatetag/">show_stars template tag</a> that I blogged about a few weeks ago to display the average restaurant rating.  (Cheap shameless plug, but damn effective! <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' /> )  I&#8217;d link to a page in action, but since I just opened up my site to a few select users, I&#8217;ll update this post when I actually have any reviews. <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' /> </p>
<p>Oh, and before I forget, thanks to my co-worker Stephen for assisting me with my SQL issues! <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Here&#8217;s a picture of the final result:</p>
<div style="text-align:center; padding:1.5em;"><img src='http://blog.localkinegrinds.com/wp-content/uploads/2007/10/yay.jpg' alt='Yay'  style="border:1px solid #aaa;"/></div>
<h2>Note:</h2>
<p>As an added bonus, I also realized a few other &#8216;spots&#8217; where the .extra() Queryset modifier would come in handy.  Since I&#8217;m also using the wonderful <a href="http://code.google.com/p/django-voting/">django-voting</a> application from <a href="http://insin.webfactional.com/weblog/">Jonathan Buchanan</a>, I came across <a href="http://groups.google.com/group/django-users/browse_thread/thread/119b5d6581d43dc6/671a25f972a082ed">this post about accessing a dictionary via a template</a> in the <a href="http://groups.google.com/group/django-users">Django-users Google Group</a>.</p>
<p>Basically, I had come across the same issue as the poster.  Since I allow users to vote on reviews (similar to <a href="http://www.amazon.com">Amazon</a>, <a href="http://www.yelp.com">Yelp</a>, etc.), I wanted to retrieve the score of each Review instance to display on a <a href="http://www.wegoeat.com/reviews/browse/latest/">paginated listing of all Reviews</a>.  Using the same extra() modifier, I was able to inject the total number of votes and the score when I retrieved all Reviews as such:</p>
<p><strong>Btw, I just injected most of the code from Jonathan&#8217;s template tag. <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </strong></p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">.<span style="color: black;">extra</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">select</span>=<span style="color: black;">&#123;</span><span style="color: #483d8b;">'total_votes'</span>: <span style="color: #483d8b;">'SELECT COUNT(vote) FROM votes as v, reviews_review as rev, django_content_type <span style="color: #000099; font-weight: bold;">\</span>
                                        WHERE reviews_review.id = rev.id <span style="color: #000099; font-weight: bold;">\</span>
                                        AND v.object_id = reviews_review.id <span style="color: #000099; font-weight: bold;">\</span>
                                        AND v.content_type_id = django_content_type.id <span style="color: #000099; font-weight: bold;">\</span>
                                        AND django_content_type.model = <span style="color: #000099; font-weight: bold;">\'</span>review<span style="color: #000099; font-weight: bold;">\'</span>'</span>, 
&nbsp;
                                        <span style="color: #483d8b;">'score'</span>: <span style="color: #483d8b;">'SELECT SUM(vote) FROM votes as v, reviews_review as rev, django_content_type <span style="color: #000099; font-weight: bold;">\</span>
                                        WHERE reviews_review.id = rev.id <span style="color: #000099; font-weight: bold;">\</span>
                                        AND v.object_id = reviews_review.id <span style="color: #000099; font-weight: bold;">\</span>
                                        AND v.content_type_id = django_content_type.id <span style="color: #000099; font-weight: bold;">\</span>
                                        AND django_content_type.model = <span style="color: #000099; font-weight: bold;">\'</span>review<span style="color: #000099; font-weight: bold;">\'</span>'</span><span style="color: black;">&#125;</span>,<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<h3>Pretty neat right?</h3>
<p>Now, when iterating through the reviews, I can use the following:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: black;">&#123;</span><span style="color: #66cc66;">%</span> <span style="color: #ff7700;font-weight:bold;">for</span> review <span style="color: #ff7700;font-weight:bold;">in</span> object_list <span style="color: #66cc66;">%</span><span style="color: black;">&#125;</span>
	<span style="color: #66cc66;">&lt;</span>tr<span style="color: #66cc66;">&gt;</span>
		<span style="color: #66cc66;">&lt;</span>td<span style="color: #66cc66;">&gt;&lt;</span>a href=<span style="color: #483d8b;">&quot;{{review.content_object.get_absolute_url}}&quot;</span><span style="color: #66cc66;">&gt;</span><span style="color: black;">&#123;</span><span style="color: black;">&#123;</span> review.<span style="color: black;">content_object</span>.<span style="color: black;">name</span> <span style="color: black;">&#125;</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">&lt;</span>/a<span style="color: #66cc66;">&gt;&lt;</span>/td<span style="color: #66cc66;">&gt;</span>
		<span style="color: #66cc66;">&lt;</span>td<span style="color: #66cc66;">&gt;&lt;</span>a href=<span style="color: #483d8b;">&quot;{% url profile-detail username=review.user.username %}&quot;</span><span style="color: #66cc66;">&gt;</span><span style="color: black;">&#123;</span><span style="color: black;">&#123;</span> review.<span style="color: #dc143c;">user</span>.<span style="color: black;">username</span> <span style="color: black;">&#125;</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">&lt;</span>/a<span style="color: #66cc66;">&gt;&lt;</span>/td<span style="color: #66cc66;">&gt;</span>
		<span style="color: #66cc66;">&lt;</span>td<span style="color: #66cc66;">&gt;&lt;</span>nobr<span style="color: #66cc66;">&gt;</span><span style="color: black;">&#123;</span><span style="color: #66cc66;">%</span> load show_stars <span style="color: #66cc66;">%</span><span style="color: black;">&#125;</span>
			<span style="color: #66cc66;">&lt;</span>span <span style="color: #ff7700;font-weight:bold;">class</span>=<span style="color: #483d8b;">&quot;rating&quot;</span><span style="color: #66cc66;">&gt;</span><span style="color: black;">&#123;</span><span style="color: #66cc66;">%</span> show_stars review.<span style="color: black;">overall_rating</span> of <span style="color: #ff4500;">5</span> <span style="color: #008000;">round</span> to half <span style="color: #66cc66;">%</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">&lt;</span>/span<span style="color: #66cc66;">&gt;</span>
			<span style="color: #66cc66;">&lt;</span>/nobr<span style="color: #66cc66;">&gt;</span>
		<span style="color: #66cc66;">&lt;</span>/td<span style="color: #66cc66;">&gt;</span>
		<span style="color: #66cc66;">&lt;</span>td<span style="color: #66cc66;">&gt;&lt;</span>span style=<span style="color: #483d8b;">&quot;font-weight:bold; color:#092e20;&quot;</span><span style="color: #66cc66;">&gt;</span><span style="color: black;">&#123;</span><span style="color: black;">&#123;</span> review.<span style="color: black;">get_recommendation_display</span> <span style="color: black;">&#125;</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">&lt;</span>/span<span style="color: #66cc66;">&gt;&lt;</span>/td<span style="color: #66cc66;">&gt;</span>
		<span style="color: #66cc66;">&lt;</span>td<span style="color: #66cc66;">&gt;&lt;</span>span style=<span style="color: #483d8b;">&quot;font-size:.875em;&quot;</span><span style="color: #66cc66;">&gt;</span><span style="color: black;">&#123;</span><span style="color: black;">&#123;</span> review.<span style="color: black;">submit_date</span>|timesince <span style="color: black;">&#125;</span><span style="color: black;">&#125;</span> ago<span style="color: #66cc66;">&lt;</span>/span<span style="color: #66cc66;">&gt;&lt;</span>/td<span style="color: #66cc66;">&gt;</span>
		<span style="color: #66cc66;">&lt;</span>strong<span style="color: #66cc66;">&gt;&lt;</span>td<span style="color: #66cc66;">&gt;</span>Total of <span style="color: black;">&#123;</span><span style="color: black;">&#123;</span> review.<span style="color: black;">score</span>|default:<span style="color: #ff4500;">0</span> <span style="color: black;">&#125;</span><span style="color: black;">&#125;</span> <span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: black;">&#123;</span><span style="color: black;">&#123;</span> review.<span style="color: black;">total_votes</span> <span style="color: black;">&#125;</span><span style="color: black;">&#125;</span> <span style="color: black;">&#123;</span><span style="color: black;">&#123;</span>  review.<span style="color: black;">total_votes</span>|pluralize:<span style="color: #483d8b;">&quot;person,people&quot;</span> <span style="color: black;">&#125;</span><span style="color: black;">&#125;</span>.<span style="color: #66cc66;">&lt;</span>/td<span style="color: #66cc66;">&gt;&lt;</span>/strong<span style="color: #66cc66;">&gt;</span>
	<span style="color: #66cc66;">&lt;</span>/tr<span style="color: #66cc66;">&gt;</span>
<span style="color: black;">&#123;</span><span style="color: #66cc66;">%</span> endfor <span style="color: #66cc66;">%</span><span style="color: black;">&#125;</span></pre></td></tr></table></div>

<p>Hope y&#8217;all learned something like I did! <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />   Oh, and before I forget my standard disclaimer, &#8220;since this is on my blog, feel free to take/use/steal/distribute/copy/modify any code you see fit, but if you find any bugs, have any comments, or think the code can be cleaner, Iâ€™d love to hear from you.&#8221;</p>
<h2>Enjoy!</h2>
<img src="http://blog.localkinegrinds.com/?ak_action=api_record_view&id=136&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://blog.localkinegrinds.com/2007/10/24/using-the-extra-queryset-modifier-in-django-for-wegoeat/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Displaying stars (with rounding) as a Django templatetag</title>
		<link>http://blog.localkinegrinds.com/2007/10/02/displaying-stars-with-rounding-as-a-django-templatetag/</link>
		<comments>http://blog.localkinegrinds.com/2007/10/02/displaying-stars-with-rounding-as-a-django-templatetag/#comments</comments>
		<pubDate>Wed, 03 Oct 2007 05:15:44 +0000</pubDate>
		<dc:creator>ryankanno</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[leah-culver]]></category>
		<category><![CDATA[pownce]]></category>
		<category><![CDATA[rounded-stars]]></category>
		<category><![CDATA[template-tag]]></category>
		<category><![CDATA[templatetag]]></category>

		<guid isPermaLink="false">http://blog.localkinegrinds.com/2007/10/02/displaying-stars-with-rounding-as-a-django-templatetag/</guid>
		<description><![CDATA[Since my last post was quite popular (by my simple blogging standards), I&#8217;ve decided to post another Django snippet that I&#8217;ve used while coding WeGoEat. (I know, I know&#8230; one of these days I&#8217;ll finish. I&#8217;ve just been really, really lazy busy.) After reading Leah&#8217;s blog (of Pownce fame) about &#8220;rounding to the nearest half&#8221;, [...]]]></description>
			<content:encoded><![CDATA[<p>Since <a href="http://blog.localkinegrinds.com/2007/09/06/digg-style-pagination-in-django/">my last post</a> was <a href="http://del.icio.us/url/f60c41652684b40dc55c0a1b91262f6f">quite popular</a> (by my simple blogging standards), I&#8217;ve decided to post another <a href="http://djangoproject.com">Django</a> snippet that I&#8217;ve used while coding <a href="http://www.wegoeat.com">WeGoEat</a>. (<em>I know, I know&#8230; one of these days I&#8217;ll finish.  I&#8217;ve just been really, really <strike>lazy</strike> busy.</em>)</p>
<p>After reading <a href="http://leahculver.com/2007/04/19/star-ratings/">Leah&#8217;s blog</a> (of <a href="http://www.pownce.com">Pownce</a> fame) about &#8220;rounding to the nearest half&#8221;, I was immediately struck by two sobering realities:</p>
<ol>
<li>I would need that exact same algorithm for my current site.</li>
<li>A few years out of grad school, and I couldn&#8217;t even begin to fathom a solution. Thank you, Internet.</li>
</ol>
<p>In any case, I figured I&#8217;d wrap my solution into a template tag that people could use/steal/copy.  Here&#8217;s a generated test page that illustrates what my template tag would display.</p>
<p><img src='http://blog.localkinegrinds.com/wp-content/uploads/2007/10/stars.jpg' alt='Stars!' /></p>
<p>You can even customize the display a bit:</p>
<p><img src='http://blog.localkinegrinds.com/wp-content/uploads/2007/10/25stars.jpg' alt='25 Stars!' /></p>
<p>I know, I know&#8230; I can do a lot better. <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />   In any case, here&#8217;s the code snippet that I placed in a file called show_stars.py:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">math</span>
&nbsp;
<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, VariableDoesNotExist, resolve_variable
<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;
register = Library<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
IMG_TEMPLATE = <span style="color: #483d8b;">'&lt;img src=&quot;%s&quot; alt=&quot;%s&quot;/&gt;'</span>
&nbsp;
PATH_TO_WHOLE_STAR = IMG_TEMPLATE <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>settings.<span style="color: black;">MEDIA_URL</span> + <span style="color: #483d8b;">'img/stars/star.png'</span>, <span style="color: #483d8b;">&quot;Whole Star&quot;</span><span style="color: black;">&#41;</span>
PATH_TO_THREE_QUARTER_STAR = IMG_TEMPLATE <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>settings.<span style="color: black;">MEDIA_URL</span> + <span style="color: #483d8b;">'img/stars/three-quarter.png'</span>, <span style="color: #483d8b;">&quot;3/4 Star&quot;</span><span style="color: black;">&#41;</span>
PATH_TO_HALF_STAR = IMG_TEMPLATE <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>settings.<span style="color: black;">MEDIA_URL</span> + <span style="color: #483d8b;">'img/stars/half.png'</span>, <span style="color: #483d8b;">&quot;1/2 Star&quot;</span><span style="color: black;">&#41;</span>
PATH_TO_QUARTER_STAR = IMG_TEMPLATE <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>settings.<span style="color: black;">MEDIA_URL</span> + <span style="color: #483d8b;">'img/stars/quarter.png'</span>, <span style="color: #483d8b;">&quot;1/4 Star&quot;</span><span style="color: black;">&#41;</span>
PATH_TO_BLANK_STAR = IMG_TEMPLATE <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>settings.<span style="color: black;">MEDIA_URL</span> + <span style="color: #483d8b;">'img/stars/blank.png'</span>, <span style="color: #483d8b;">&quot;Empty Star&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> ShowStarsNode<span style="color: black;">&#40;</span>Node<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot; Default rounding is to the whole unit &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>, context_var, total_stars, round_to<span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.<span style="color: black;">context_var</span> = context_var
        <span style="color: #008000;">self</span>.<span style="color: black;">total_stars</span> = <span style="color: #008000;">int</span><span style="color: black;">&#40;</span>total_stars<span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">round_to</span> = round_to.<span style="color: black;">lower</span><span style="color: black;">&#40;</span><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: #ff7700;font-weight:bold;">try</span>:
            stars = resolve_variable<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">context_var</span>, context<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">except</span> VariableDoesNotExist:
            <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #483d8b;">''</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">self</span>.<span style="color: black;">round_to</span> == <span style="color: #483d8b;">&quot;half&quot;</span>:
            stars = <span style="color: #008000;">round</span><span style="color: black;">&#40;</span>stars<span style="color: #66cc66;">*</span><span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span>/<span style="color: #ff4500;">2</span>
        <span style="color: #ff7700;font-weight:bold;">elif</span> <span style="color: #008000;">self</span>.<span style="color: black;">round_to</span> == <span style="color: #483d8b;">&quot;quarter&quot;</span>:
            stars = <span style="color: #008000;">round</span><span style="color: black;">&#40;</span>stars<span style="color: #66cc66;">*</span><span style="color: #ff4500;">4</span><span style="color: black;">&#41;</span>/<span style="color: #ff4500;">4</span>
        <span style="color: #ff7700;font-weight:bold;">else</span>:
            stars = <span style="color: #008000;">round</span><span style="color: black;">&#40;</span>stars<span style="color: black;">&#41;</span>
&nbsp;
        fraction, integer = <span style="color: #dc143c;">math</span>.<span style="color: black;">modf</span><span style="color: black;">&#40;</span>stars<span style="color: black;">&#41;</span>
        integer = <span style="color: #008000;">int</span><span style="color: black;">&#40;</span>integer<span style="color: black;">&#41;</span>
        output = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">for</span> whole_star <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span>integer<span style="color: black;">&#41;</span>:
            output.<span style="color: black;">append</span><span style="color: black;">&#40;</span>PATH_TO_WHOLE_STAR<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">self</span>.<span style="color: black;">round_to</span> == <span style="color: #483d8b;">'half'</span> <span style="color: #ff7700;font-weight:bold;">and</span> fraction == .5:
            output.<span style="color: black;">append</span><span style="color: black;">&#40;</span>PATH_TO_HALF_STAR<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">elif</span> <span style="color: #008000;">self</span>.<span style="color: black;">round_to</span> == <span style="color: #483d8b;">'quarter'</span>:
            <span style="color: #ff7700;font-weight:bold;">if</span> fraction == .25:
                output.<span style="color: black;">append</span><span style="color: black;">&#40;</span>PATH_TO_QUARTER_STAR<span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">elif</span> fraction == .5:
                output.<span style="color: black;">append</span><span style="color: black;">&#40;</span>PATH_TO_HALF_STAR<span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">elif</span> fraction == .75:
                output.<span style="color: black;">append</span><span style="color: black;">&#40;</span>PATH_TO_THREE_QUARTER_STAR<span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">if</span> fraction:
            integer += <span style="color: #ff4500;">1</span>
&nbsp;
        blanks = <span style="color: #008000;">int</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">total_stars</span> - integer<span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">for</span> blank_star <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span>blanks<span style="color: black;">&#41;</span>:
            output.<span style="color: black;">append</span><span style="color: black;">&#40;</span>PATH_TO_BLANK_STAR<span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #483d8b;">&quot;&quot;</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span>output<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #483d8b;">&quot;&quot;&quot; show_stars context_var of 5 round to half &quot;&quot;&quot;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> do_show_stars<span style="color: black;">&#40;</span><span style="color: #dc143c;">parser</span>, <span style="color: #dc143c;">token</span><span style="color: black;">&#41;</span>:
    args = <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>args<span style="color: black;">&#41;</span> <span style="color: #66cc66;">!</span>= <span style="color: #ff4500;">7</span>:
        <span style="color: #ff7700;font-weight:bold;">raise</span> TemplateSyntaxError<span style="color: black;">&#40;</span><span style="color: #483d8b;">'%s tag requires exactly six arguments'</span> <span style="color: #66cc66;">%</span> args<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;">if</span> args<span style="color: black;">&#91;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">!</span>= <span style="color: #483d8b;">'of'</span>:
        <span style="color: #ff7700;font-weight:bold;">raise</span> TemplateSyntaxError<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;second argument to '%s' tag must be 'of'&quot;</span> <span style="color: #66cc66;">%</span> args<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;">if</span> args<span style="color: black;">&#91;</span><span style="color: #ff4500;">4</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">!</span>= <span style="color: #483d8b;">'round'</span>:
        <span style="color: #ff7700;font-weight:bold;">raise</span> TemplateSyntaxError<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;fourth argument to '%s' tag must be 'round'&quot;</span> <span style="color: #66cc66;">%</span> args<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;">if</span> args<span style="color: black;">&#91;</span><span style="color: #ff4500;">5</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">!</span>= <span style="color: #483d8b;">'to'</span>:
        <span style="color: #ff7700;font-weight:bold;">raise</span> TemplateSyntaxError<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;fourth argument to '%s' tag must be 'to'&quot;</span> <span style="color: #66cc66;">%</span> args<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> ShowStarsNode<span style="color: black;">&#40;</span>args<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>, args<span style="color: black;">&#91;</span><span style="color: #ff4500;">3</span><span style="color: black;">&#93;</span>, args<span style="color: black;">&#91;</span><span style="color: #ff4500;">6</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>   
&nbsp;
register.<span style="color: black;">tag</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'show_stars'</span>, do_show_stars<span style="color: black;">&#41;</span></pre></div></div>

<p>I&#8217;ve also <a href='http://blog.localkinegrinds.com/wp-content/uploads/2007/10/stars.zip' title='Stars.zipped'>wrapped up the star graphics</a> I used.  A big ups to the <a href="http://www.famfamfam.com/lab/icons/silk/">Silk Icons</a> creator. <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />   For those of you that don&#8217;t care for <a href="http://blog.localkinegrinds.com/wp-content/uploads/2007/10/stars.zip">.zip files</a>, here&#8217;s all the star images I used:</p>
<p><img src='http://blog.localkinegrinds.com/wp-content/uploads/2007/10/blank.png' alt='Blank' /><img src='http://blog.localkinegrinds.com/wp-content/uploads/2007/10/quarter.png' alt='quarter' /><img src='http://blog.localkinegrinds.com/wp-content/uploads/2007/10/half.png' alt='half' /><img src='http://blog.localkinegrinds.com/wp-content/uploads/2007/10/three-quarter.png' alt='three quarter' /><img src='http://blog.localkinegrinds.com/wp-content/uploads/2007/10/star.png' alt='full' /></p>
<p>To use the tag, itâ€™ll look something like this in your templates:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: black;">&#123;</span><span style="color: #66cc66;">%</span> load show_stars <span style="color: #66cc66;">%</span><span style="color: black;">&#125;</span>
     <span style="color: black;">&#123;</span><span style="color: #66cc66;">%</span> show_stars context_var of <span style="color: #ff4500;">5</span> <span style="color: #008000;">round</span> to half <span style="color: #66cc66;">%</span><span style="color: black;">&#125;</span>
<span style="color: black;">&#123;</span><span style="color: #66cc66;">%</span> endif <span style="color: #66cc66;">%</span><span style="color: black;">&#125;</span></pre></div></div>

<p><strong>Note:</strong> You can change the following:</p>
<ol>
<li>context_var is whatever var contains the value you want rounded (it was &#8216;value&#8217; in the screenshots)</li>
<li>5 can be any integer; this is the maximum number of stars to display.  I should probably check to make sure you don&#8217;t put a number smaller than context_var, but I&#8217;ll leave that up to you. <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </li>
<li>half can be any of the following values: whole, half, or quarter</li>
<li>Make sure to edit the paths to the star files in the templatetag file</li>
</ol>
<p>And since this is on my blog, feel totally free to take/use/steal/distribute/copy/modify any code you see fit.  All I ask is if you find any bugs, have any comments, or can think of ways to make my ugly code cleaner &#8211; I&#8217;d love to hear from you. <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<h3>Enjoy!</h3>
<img src="http://blog.localkinegrinds.com/?ak_action=api_record_view&id=124&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://blog.localkinegrinds.com/2007/10/02/displaying-stars-with-rounding-as-a-django-templatetag/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Digg-style pagination in Django</title>
		<link>http://blog.localkinegrinds.com/2007/09/06/digg-style-pagination-in-django/</link>
		<comments>http://blog.localkinegrinds.com/2007/09/06/digg-style-pagination-in-django/#comments</comments>
		<pubDate>Thu, 06 Sep 2007 16:28:42 +0000</pubDate>
		<dc:creator>ryankanno</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[digg]]></category>
		<category><![CDATA[digg-style]]></category>
		<category><![CDATA[generic-list_object-view]]></category>
		<category><![CDATA[pagination]]></category>
		<category><![CDATA[paginatortag]]></category>

		<guid isPermaLink="false">http://blog.localkinegrinds.com/2007/09/06/digg-style-pagination-in-django/</guid>
		<description><![CDATA[Since I&#8217;ve finally been picked up by the Django community aggregator (Thanks Jacob!), I figured I&#8217;d put out a little snippet for people to use/critique; hopefully more use than critique. I really, really liked the PaginatorTag on the Django Wiki, but I&#8217;ve always wanted my sites to have configurable Digg-like behavior; if you wanna know [...]]]></description>
			<content:encoded><![CDATA[<p>Since I&#8217;ve finally been picked up by the <a href="http://www.djangoproject.com/community/">Django community aggregator</a> (Thanks <a href="http://www.jacobian.org/">Jacob!</a>), I figured I&#8217;d put out a little snippet for people to use/critique; hopefully more use than critique. <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />   I really, really liked the <a href="http://code.djangoproject.com/wiki/PaginatorTag">PaginatorTag on the Django Wiki</a>, but I&#8217;ve always wanted my sites to have configurable Digg-like behavior; if you wanna know what I&#8217;m talking about, just check out how pagination works on <a href="http://www.digg.com">Digg</a>.  </p>
<h3 style="margin:.5em 0em;">Here&#8217;s how the PaginatorTag looks compared to the Digg-style tags.</h3>
<p><img src='http://blog.localkinegrinds.com/wp-content/uploads/2007/09/pagination.gif' alt='Different paginators' /></p>
<p>Like the PaginatorTag, this tag is a very basic inclusion tag that builds on the variables already set on the context when paginating with the <a href="http://www.djangoproject.com/documentation/generic_views/#django-views-generic-list-detail-object-list">generic object_list view</a>.  There are a few additional context variables created:</p>
<ul>
<li><strong>page_numbers</strong> &#8211; a list of page numbers to display</li>
<li><strong>in_leading_range</strong> &#8211; boolean if the page is within the leading range</li>
<li><strong>in_trailing_range</strong> &#8211; boolean if the page is within the trailing range</li>
<li><strong>pages_outside_leading_range</strong> &#8211; a list of page numbers outside the leading range</li>
<li><strong>pages_outside_trailing_range</strong> &#8211; a list of page numbers outside the trailing range</li>
</ul>
<p>If you don&#8217;t understand what these are, don&#8217;t worry &#8211; I don&#8217;t remember either. <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />   I could&#8217;ve just appended what needed to be displayed in page_numbers, but instead, I broke it out into what needed to be displayed before and after the actual pages so one could customize the code a little more.  In any case, without further adieu, here&#8217;s the code snippet:</p>
<p>The first thing I did was create a file called digg_paginator.py.</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> django <span style="color: #ff7700;font-weight:bold;">import</span> template
&nbsp;
register = template.<span style="color: black;">Library</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
LEADING_PAGE_RANGE_DISPLAYED = TRAILING_PAGE_RANGE_DISPLAYED = <span style="color: #ff4500;">10</span>
LEADING_PAGE_RANGE = TRAILING_PAGE_RANGE = <span style="color: #ff4500;">8</span>
NUM_PAGES_OUTSIDE_RANGE = <span style="color: #ff4500;">2</span> 
ADJACENT_PAGES = <span style="color: #ff4500;">4</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> digg_paginator<span style="color: black;">&#40;</span>context<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: black;">&#40;</span>context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;is_paginated&quot;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot; Initialize variables &quot;</span>
        in_leading_range = in_trailing_range = <span style="color: #008000;">False</span>
        pages_outside_leading_range = pages_outside_trailing_range = <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><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: black;">&#40;</span>context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;pages&quot;</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">&lt;</span>= LEADING_PAGE_RANGE_DISPLAYED<span style="color: black;">&#41;</span>:
            in_leading_range = in_trailing_range = <span style="color: #008000;">True</span>
            page_numbers = <span style="color: black;">&#91;</span>n <span style="color: #ff7700;font-weight:bold;">for</span> n <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span>, context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;pages&quot;</span><span style="color: black;">&#93;</span> + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">if</span> n <span style="color: #66cc66;">&gt;</span> <span style="color: #ff4500;">0</span> <span style="color: #ff7700;font-weight:bold;">and</span> n <span style="color: #66cc66;">&lt;</span>= context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;pages&quot;</span><span style="color: black;">&#93;</span><span style="color: black;">&#93;</span>           
        <span style="color: #ff7700;font-weight:bold;">elif</span> <span style="color: black;">&#40;</span>context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;page&quot;</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">&lt;</span>= LEADING_PAGE_RANGE<span style="color: black;">&#41;</span>:
            in_leading_range = <span style="color: #008000;">True</span>
            page_numbers = <span style="color: black;">&#91;</span>n <span style="color: #ff7700;font-weight:bold;">for</span> n <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span>, LEADING_PAGE_RANGE_DISPLAYED + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">if</span> n <span style="color: #66cc66;">&gt;</span> <span style="color: #ff4500;">0</span> <span style="color: #ff7700;font-weight:bold;">and</span> n <span style="color: #66cc66;">&lt;</span>= context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;pages&quot;</span><span style="color: black;">&#93;</span><span style="color: black;">&#93;</span>
            pages_outside_leading_range = <span style="color: black;">&#91;</span>n + context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;pages&quot;</span><span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">for</span> n <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span>, -NUM_PAGES_OUTSIDE_RANGE, -<span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
        <span style="color: #ff7700;font-weight:bold;">elif</span> <span style="color: black;">&#40;</span>context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;page&quot;</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">&gt;</span> context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;pages&quot;</span><span style="color: black;">&#93;</span> - TRAILING_PAGE_RANGE<span style="color: black;">&#41;</span>:
            in_trailing_range = <span style="color: #008000;">True</span>
            page_numbers = <span style="color: black;">&#91;</span>n <span style="color: #ff7700;font-weight:bold;">for</span> n <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span>context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;pages&quot;</span><span style="color: black;">&#93;</span> - TRAILING_PAGE_RANGE_DISPLAYED + <span style="color: #ff4500;">1</span>, context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;pages&quot;</span><span style="color: black;">&#93;</span> + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">if</span> n <span style="color: #66cc66;">&gt;</span> <span style="color: #ff4500;">0</span> <span style="color: #ff7700;font-weight:bold;">and</span> n <span style="color: #66cc66;">&lt;</span>= context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;pages&quot;</span><span style="color: black;">&#93;</span><span style="color: black;">&#93;</span>
            pages_outside_trailing_range = <span style="color: black;">&#91;</span>n + <span style="color: #ff4500;">1</span> <span style="color: #ff7700;font-weight:bold;">for</span> n <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span>, NUM_PAGES_OUTSIDE_RANGE<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
        <span style="color: #ff7700;font-weight:bold;">else</span>: 
            page_numbers = <span style="color: black;">&#91;</span>n <span style="color: #ff7700;font-weight:bold;">for</span> n <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span>context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;page&quot;</span><span style="color: black;">&#93;</span> - ADJACENT_PAGES, context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;page&quot;</span><span style="color: black;">&#93;</span> + ADJACENT_PAGES + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">if</span> n <span style="color: #66cc66;">&gt;</span> <span style="color: #ff4500;">0</span> <span style="color: #ff7700;font-weight:bold;">and</span> n <span style="color: #66cc66;">&lt;</span>= context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;pages&quot;</span><span style="color: black;">&#93;</span><span style="color: black;">&#93;</span>
            pages_outside_leading_range = <span style="color: black;">&#91;</span>n + context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;pages&quot;</span><span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">for</span> n <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span>, -NUM_PAGES_OUTSIDE_RANGE, -<span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
            pages_outside_trailing_range = <span style="color: black;">&#91;</span>n + <span style="color: #ff4500;">1</span> <span style="color: #ff7700;font-weight:bold;">for</span> n <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span>, NUM_PAGES_OUTSIDE_RANGE<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span>
            <span style="color: #483d8b;">&quot;base_url&quot;</span>: context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;base_url&quot;</span><span style="color: black;">&#93;</span>,
            <span style="color: #483d8b;">&quot;is_paginated&quot;</span>: context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;is_paginated&quot;</span><span style="color: black;">&#93;</span>,
            <span style="color: #483d8b;">&quot;previous&quot;</span>: context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;previous&quot;</span><span style="color: black;">&#93;</span>,
            <span style="color: #483d8b;">&quot;has_previous&quot;</span>: context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;has_previous&quot;</span><span style="color: black;">&#93;</span>,
            <span style="color: #483d8b;">&quot;next&quot;</span>: context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;next&quot;</span><span style="color: black;">&#93;</span>,
            <span style="color: #483d8b;">&quot;has_next&quot;</span>: context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;has_next&quot;</span><span style="color: black;">&#93;</span>,
            <span style="color: #483d8b;">&quot;results_per_page&quot;</span>: context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;results_per_page&quot;</span><span style="color: black;">&#93;</span>,
            <span style="color: #483d8b;">&quot;page&quot;</span>: context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;page&quot;</span><span style="color: black;">&#93;</span>,
            <span style="color: #483d8b;">&quot;pages&quot;</span>: context<span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;pages&quot;</span><span style="color: black;">&#93;</span>,
            <span style="color: #483d8b;">&quot;page_numbers&quot;</span>: page_numbers,
            <span style="color: #483d8b;">&quot;in_leading_range&quot;</span> : in_leading_range,
            <span style="color: #483d8b;">&quot;in_trailing_range&quot;</span> : in_trailing_range,
            <span style="color: #483d8b;">&quot;pages_outside_leading_range&quot;</span>: pages_outside_leading_range,
            <span style="color: #483d8b;">&quot;pages_outside_trailing_range&quot;</span>: pages_outside_trailing_range
        <span style="color: black;">&#125;</span>
&nbsp;
register.<span style="color: black;">inclusion_tag</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;digg_paginator.html&quot;</span>, takes_context=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span><span style="color: black;">&#40;</span>digg_paginator<span style="color: black;">&#41;</span></pre></div></div>

<p>To give a brief explanation of the file:</p>
<p><img src='http://blog.localkinegrinds.com/wp-content/uploads/2007/09/pagination-explanation.gif' alt='Explanation of pagination file' /></p>
<p>(LEADING_PAGE_RANGE and TRAILING_PAGE_RANGE is the number of pages before the views switch from the top view to the bottom view).</p>
<p>Also, there is a variable passed in from the view function called base_url which is basically the portion of the url before /page/.  Don&#8217;t worry, you&#8217;ll see what I mean after the template file.  Basically, I needed this url because I reconstruct the pagination links to:</p>
<p><strong>http://yoursite.com/model/page/page_number/records/num_records</strong></p>
<p>Next, I created a file called digg_paginator.html.</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">{% spaceless %}
&nbsp;
{% if is_paginated %}
&lt;div class=&quot;paginator&quot;&gt;
{% if has_previous %}&lt;span class=&quot;prev&quot;&gt;&lt;a href=&quot;{{base_url}}/page/{{ previous }}/records/{{results_per_page}}&quot; title=&quot;Previous Page&quot;&gt;&amp;laquo; Previous&lt;/a&gt;&lt;/span&gt;{% else %}&lt;span class=&quot;prev-na&quot;&gt;&amp;laquo; Previous&lt;/span&gt;{% endif %}
&nbsp;
{% if not in_leading_range %}
	{% for num in pages_outside_trailing_range %}
		&lt;span class=&quot;page&quot;&gt;&lt;a href=&quot;{{base_url}}/page/{{ num }}/records/{{results_per_page}}&quot; &gt;{{ num }}&lt;/a&gt;&lt;/span&gt;
	{% endfor %}
	...
{% endif %}
&nbsp;
{% for num in page_numbers %}
  {% ifequal num page %}
    &lt;span class=&quot;curr&quot; title=&quot;Current Page&quot;&gt;{{ num }}&lt;/span&gt;
  {% else %}
  	&lt;span class=&quot;page&quot;&gt;&lt;a href=&quot;{{base_url}}/page/{{ num }}/records/{{results_per_page}}&quot; title=&quot;Page {{ num }}&quot;&gt;{{ num }}&lt;/a&gt;&lt;/span&gt;
  {% endifequal %}
{% endfor %}
&nbsp;
{% if not in_trailing_range %}
	...
	{% for num in pages_outside_leading_range reversed %}
		&lt;span class=&quot;page&quot;&gt;&lt;a href=&quot;{{base_url}}/page/{{ num }}/records/{{results_per_page}}&quot; &gt;{{ num }}&lt;/a&gt;&lt;/span&gt;
	{% endfor %}
{% endif %}
&nbsp;
{% if has_next %}&lt;span class=&quot;next&quot;&gt;&lt;a href=&quot;{{base_url}}/page/{{ next }}/records/{{results_per_page}}&quot; title=&quot;Next Page&quot;&gt;Next &amp;raquo;&lt;/a&gt;&lt;/span&gt;{% else %}&lt;span class=&quot;next-na&quot;&gt;Next &amp;raquo;&lt;/span&gt;{% endif %}
&lt;/div&gt; 
{% endif %}
&nbsp;
{% endspaceless %}</pre></div></div>

<p>Notice what the next, previous, and page urls look like.  You&#8217;ll have to make sure your urls.py file reflects this.  Here&#8217;s a snippet of what my urls.py looks like:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">url<span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'^browse/all/restaurants/page/(?P&lt;page&gt;[<span style="color: #000099; font-weight: bold;">\d</span>]+)/records/(?P&lt;records&gt;[<span style="color: #000099; font-weight: bold;">\d</span>]+)/$'</span>, restaurant_views.<span style="color: black;">browse_all</span><span style="color: black;">&#41;</span>,</pre></div></div>

<p>Finally, just so you can have the same Django-like color-scheme, here&#8217;s the relevant css.</p>

<div class="wp_syntax"><div class="code"><pre class="css" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">/** PAGINATOR **/</span>
<span style="color: #6666ff;">.paginator</span> <span style="color: #00AA00;">&#123;</span> <span style="color: #000000; font-weight: bold;">padding</span><span style="color: #00AA00;">:</span><span style="color: #933;">.5em</span> <span style="color: #933;">.75em</span><span style="color: #00AA00;">;</span> <span style="color: #000000; font-weight: bold;">float</span><span style="color: #00AA00;">:</span><span style="color: #000000; font-weight: bold;">left</span><span style="color: #00AA00;">;</span> <span style="color: #000000; font-weight: bold;">font</span><span style="color: #00AA00;">:</span><span style="color: #993333;">normal</span> <span style="color: #933;">.8em</span> arial<span style="color: #00AA00;">;</span> <span style="color: #00AA00;">&#125;</span>
&nbsp;
<span style="color: #6666ff;">.paginator</span> .prev-na<span style="color: #00AA00;">,</span>
<span style="color: #6666ff;">.paginator</span> <span style="color: #6666ff;">.next-na</span> <span style="color: #00AA00;">&#123;</span>
	<span style="color: #000000; font-weight: bold;">padding</span><span style="color: #00AA00;">:</span><span style="color: #933;">.3em</span><span style="color: #00AA00;">;</span>
	<span style="color: #000000; font-weight: bold;">font</span><span style="color: #00AA00;">:</span><span style="color: #993333;">bold</span> <span style="color: #933;">.875em</span> arial<span style="color: #00AA00;">;</span>
<span style="color: #00AA00;">&#125;</span>
&nbsp;
<span style="color: #6666ff;">.paginator</span> .prev-na<span style="color: #00AA00;">,</span>
<span style="color: #6666ff;">.paginator</span> <span style="color: #6666ff;">.next-na</span> <span style="color: #00AA00;">&#123;</span>
	<span style="color: #000000; font-weight: bold;">border</span><span style="color: #00AA00;">:</span><span style="color: #933;">1px</span> <span style="color: #993333;">solid</span> <span style="color: #cc00cc;">#ccc</span><span style="color: #00AA00;">;</span>
	<span style="color: #000000; font-weight: bold;">background-color</span><span style="color: #00AA00;">:</span><span style="color: #cc00cc;">#f9f9f9</span><span style="color: #00AA00;">;</span>
	<span style="color: #000000; font-weight: bold;">color</span><span style="color: #00AA00;">:</span><span style="color: #cc00cc;">#aaa</span><span style="color: #00AA00;">;</span>
	<span style="color: #000000; font-weight: bold;">font-weight</span><span style="color: #00AA00;">:</span><span style="color: #993333;">normal</span><span style="color: #00AA00;">;</span>
<span style="color: #00AA00;">&#125;</span>
&nbsp;
<span style="color: #6666ff;">.paginator</span> <span style="color: #6666ff;">.prev</span> a<span style="color: #00AA00;">,</span> <span style="color: #6666ff;">.paginator</span> <span style="color: #6666ff;">.prev</span> a<span style="color: #3333ff;">:visited</span><span style="color: #00AA00;">,</span>
<span style="color: #6666ff;">.paginator</span> <span style="color: #6666ff;">.next</span> a<span style="color: #00AA00;">,</span> <span style="color: #6666ff;">.paginator</span> <span style="color: #6666ff;">.next</span> a<span style="color: #3333ff;">:visited </span><span style="color: #00AA00;">&#123;</span>
	<span style="color: #000000; font-weight: bold;">border</span><span style="color: #00AA00;">:</span><span style="color: #933;">1px</span> <span style="color: #993333;">solid</span> <span style="color: #cc00cc;">#c2ee62</span><span style="color: #00AA00;">;</span>
	<span style="color: #000000; font-weight: bold;">background-color</span><span style="color: #00AA00;">:</span><span style="color: #cc00cc;">#edfdd0</span><span style="color: #00AA00;">;</span>
	<span style="color: #000000; font-weight: bold;">color</span><span style="color: #00AA00;">:</span><span style="color: #cc00cc;">#234f32</span><span style="color: #00AA00;">;</span>
	<span style="color: #000000; font-weight: bold;">padding</span><span style="color: #00AA00;">:</span><span style="color: #933;">.3em</span><span style="color: #00AA00;">;</span>
	<span style="color: #000000; font-weight: bold;">font</span><span style="color: #00AA00;">:</span><span style="color: #993333;">bold</span> <span style="color: #933;">.875em</span> arial<span style="color: #00AA00;">;</span>
<span style="color: #00AA00;">&#125;</span>
&nbsp;
<span style="color: #6666ff;">.paginator</span> <span style="color: #6666ff;">.prev</span><span style="color: #00AA00;">,</span> <span style="color: #6666ff;">.paginator</span> <span style="color: #6666ff;">.prev-na</span> <span style="color: #00AA00;">&#123;</span> <span style="color: #000000; font-weight: bold;">margin-right</span><span style="color: #00AA00;">:</span><span style="color: #933;">.5em</span><span style="color: #00AA00;">;</span> <span style="color: #00AA00;">&#125;</span>
<span style="color: #6666ff;">.paginator</span> <span style="color: #6666ff;">.next</span><span style="color: #00AA00;">,</span> <span style="color: #6666ff;">.paginator</span> <span style="color: #6666ff;">.next-na</span> <span style="color: #00AA00;">&#123;</span> <span style="color: #000000; font-weight: bold;">margin-left</span><span style="color: #00AA00;">:</span><span style="color: #933;">.5em</span><span style="color: #00AA00;">;</span> <span style="color: #00AA00;">&#125;</span>
&nbsp;
<span style="color: #6666ff;">.paginator</span> .<span style="color: #000000; font-weight: bold;">page</span> a<span style="color: #00AA00;">,</span> <span style="color: #6666ff;">.paginator</span> .<span style="color: #000000; font-weight: bold;">page</span> a<span style="color: #3333ff;">:visited</span><span style="color: #00AA00;">,</span> <span style="color: #6666ff;">.paginator</span> <span style="color: #6666ff;">.curr</span> <span style="color: #00AA00;">&#123;</span>
	<span style="color: #000000; font-weight: bold;">padding</span><span style="color: #00AA00;">:</span><span style="color: #933;">.25em</span><span style="color: #00AA00;">;</span>
	<span style="color: #000000; font-weight: bold;">font</span><span style="color: #00AA00;">:</span><span style="color: #993333;">normal</span> <span style="color: #933;">.875em</span> verdana<span style="color: #00AA00;">;</span>
	<span style="color: #000000; font-weight: bold;">border</span><span style="color: #00AA00;">:</span><span style="color: #933;">1px</span> <span style="color: #993333;">solid</span> <span style="color: #cc00cc;">#C2EE62</span><span style="color: #00AA00;">;</span>
	<span style="color: #000000; font-weight: bold;">background-color</span><span style="color: #00AA00;">:</span><span style="color: #cc00cc;">#EDFDD0</span><span style="color: #00AA00;">;</span>
	<span style="color: #000000; font-weight: bold;">margin</span><span style="color: #00AA00;">:</span><span style="color: #933;">0em</span> <span style="color: #933;">.25em</span><span style="color: #00AA00;">;</span>	
	<span style="color: #000000; font-weight: bold;">color</span><span style="color: #00AA00;">:</span><span style="color: #cc00cc;">#006000</span><span style="color: #00AA00;">;</span>
<span style="color: #00AA00;">&#125;</span>
&nbsp;
<span style="color: #6666ff;">.paginator</span> <span style="color: #6666ff;">.curr</span> <span style="color: #00AA00;">&#123;</span> 
	<span style="color: #000000; font-weight: bold;">background-color</span><span style="color: #00AA00;">:</span><span style="color: #cc00cc;">#234f32</span><span style="color: #00AA00;">;</span>
	<span style="color: #000000; font-weight: bold;">color</span><span style="color: #00AA00;">:</span><span style="color: #cc00cc;">#fff</span><span style="color: #00AA00;">;</span>
	<span style="color: #000000; font-weight: bold;">border</span><span style="color: #00AA00;">:</span><span style="color: #933;">1px</span> <span style="color: #993333;">solid</span> <span style="color: #cc00cc;">#234f32</span><span style="color: #00AA00;">;</span>
	<span style="color: #000000; font-weight: bold;">font-weight</span><span style="color: #00AA00;">:</span><span style="color: #993333;">bold</span><span style="color: #00AA00;">;</span>
	<span style="color: #000000; font-weight: bold;">font-size</span><span style="color: #00AA00;">:</span><span style="color: #933;">1em</span><span style="color: #00AA00;">;</span>
<span style="color: #00AA00;">&#125;</span>
&nbsp;
<span style="color: #6666ff;">.paginator</span> .<span style="color: #000000; font-weight: bold;">page</span> a<span style="color: #3333ff;">:hover</span><span style="color: #00AA00;">,</span>
<span style="color: #6666ff;">.paginator</span> <span style="color: #6666ff;">.curr</span> a<span style="color: #3333ff;">:hover</span><span style="color: #00AA00;">,</span>
<span style="color: #6666ff;">.paginator</span> <span style="color: #6666ff;">.prev</span> a<span style="color: #3333ff;">:hover</span><span style="color: #00AA00;">,</span>
<span style="color: #6666ff;">.paginator</span> <span style="color: #6666ff;">.next</span> a<span style="color: #3333ff;">:hover </span><span style="color: #00AA00;">&#123;</span>
	<span style="color: #000000; font-weight: bold;">color</span><span style="color: #00AA00;">:</span><span style="color: #cc00cc;">#fff</span><span style="color: #00AA00;">;</span>
	<span style="color: #000000; font-weight: bold;">background-color</span><span style="color: #00AA00;">:</span><span style="color: #cc00cc;">#234f32</span><span style="color: #00AA00;">;</span>
	<span style="color: #000000; font-weight: bold;">border</span><span style="color: #00AA00;">:</span><span style="color: #933;">1px</span> <span style="color: #993333;">solid</span> <span style="color: #cc00cc;">#234f32</span><span style="color: #00AA00;">;</span>
<span style="color: #00AA00;">&#125;</span></pre></div></div>

<p>Btw, I do realize that I didn&#8217;t do any checking of the variables in the digg_paginator.py.  I&#8217;m assuming that the developer won&#8217;t put in strange values.  (I know, I know &#8211; bad assumption).  If you wanna see what I&#8217;m talking about, try these values:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">LEADING_PAGE_RANGE_DISPLAYED = TRAILING_PAGE_RANGE_DISPLAYED = <span style="color: #ff4500;">10</span>
LEADING_PAGE_RANGE = TRAILING_PAGE_RANGE = <span style="color: #ff4500;">4</span>
NUM_PAGES_OUTSIDE_RANGE = <span style="color: #ff4500;">6</span>
ADJACENT_PAGES = <span style="color: #ff4500;">6</span></pre></div></div>

<p>Oh, one last thing.  To use the tag, it&#8217;ll look something like this in your templates:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: black;">&#123;</span><span style="color: #66cc66;">%</span> load utils.<span style="color: black;">paginator</span>.<span style="color: black;">digg_paginator</span> <span style="color: #66cc66;">%</span><span style="color: black;">&#125;</span>
<span style="color: black;">&#123;</span><span style="color: #66cc66;">%</span> digg_paginator <span style="color: #66cc66;">%</span><span style="color: black;">&#125;</span></pre></div></div>

<p>Oh, and since this is on my blog, feel free to take/use/steal/distribute/copy/modify any code you see fit, but since I threw this together in a few hours, if you find any bugs, have any comments, or think the code can be cleaner, I&#8217;d love to hear from you. <img src='http://blog.localkinegrinds.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<h3>Enjoy!</h3>
<img src="http://blog.localkinegrinds.com/?ak_action=api_record_view&id=121&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://blog.localkinegrinds.com/2007/09/06/digg-style-pagination-in-django/feed/</wfw:commentRss>
		<slash:comments>32</slash:comments>
		</item>
		<item>
		<title>Custom Python installation for Django on Dreamhost</title>
		<link>http://blog.localkinegrinds.com/2007/08/20/custom-python-installation-for-django-on-dreamhost/</link>
		<comments>http://blog.localkinegrinds.com/2007/08/20/custom-python-installation-for-django-on-dreamhost/#comments</comments>
		<pubDate>Mon, 20 Aug 2007 07:09:39 +0000</pubDate>
		<dc:creator>ryankanno</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Dreamhost]]></category>
		<category><![CDATA[Life]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[custom]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[mysql-python]]></category>
		<category><![CDATA[upgrade]]></category>

		<guid isPermaLink="false">http://blog.localkinegrinds.com/2007/08/20/custom-python-installation-for-django-on-dreamhost/</guid>
		<description><![CDATA[Now that my MBA class is finally done for the summer, I can focus on more important things&#8230; like upgrading my Python installation on Dreamhost for my Django application. Seeing as how Dreamhost is still behind the Python times, with Python 2.4 hidden in Dreamhost obscurity, I figured I&#8217;d blog about updating your Dreamhost Python [...]]]></description>
			<content:encoded><![CDATA[<p>Now that my MBA class is finally done for the summer, I can focus on more important things&#8230; like upgrading my <a href="http://www.python.org/">Python</a> installation on <a href="http://www.dreamhost.com/">Dreamhost</a> for my <a href="http://www.djangoproject.com/ ">Django</a> application.  Seeing as how Dreamhost is still behind the Python times, with <a href="http://lazutkin.com/blog/2005/oct/14/dreamhost_python_241/">Python 2.4 hidden in Dreamhost obscurity</a>, I figured I&#8217;d blog about updating your Dreamhost Python installation (and subsequent <a href="http://sourceforge.net/projects/mysql-python">MySQLdb libraries</a>) to Python 2.5.</p>
<p>The very first thing I did was search <a href="http://www.google.com">Google</a>.  You know, I really don&#8217;t know how people lived pre-Googs.  In any case, I found this <a href="http://badpopcorn.com/2006/10/29/python-eggs-and-dreamhost/">blog posting</a> describing exactly what I wanted to do.  Thanks Ben!  Since I&#8217;m not a big fan of running one large batch script people create in their blogs, I&#8217;ll break it down for the non-*nix fans out there.  </p>
<p>Before I begin, I&#8217;m assuming that you already have Django running on Dreamhost.  If you&#8217;re having a &#8220;wtf&#8221; moment, make sure to stop by <a href="http://www2.jeffcroft.com/">Jeff&#8217;s blog</a> and read &#8220;<a href="http://www2.jeffcroft.com/blog/2006/may/11/django-dreamhost/">Setting up Django on Dreamhost</a>&#8220;. (This is how I set mine up).  To follow my short tutorial, you&#8217;ll need shell access to your Dreamhost account.</p>
<p>After ssh&#8217;ing into your Dreamhost account, you should be in your home directory (/home/username).  According to the <a href="http://www.pathname.com/fhs/pub/fhs-2.3.html#OPTADDONAPPLICATIONSOFTWAREPACKAGES">Filesystem Hierarchy Standard</a>, the /opt dir &#8220;<em>is reserved for the installation of add-on application software packages</em>.&#8221;  With that said, issue the following commands:</p>
<pre class="console">
$ mkdir opt
$ mkdir downloads
$ cd downloads
$ wget http://www.python.org/ftp/python/2.5.1/Python-2.5.1.tgz
$ tar xvzf Python-2.5.1.tgz
</pre>
<p>First, create a directory named opt.  Next, create a directory named downloads for all your files.  Change into the downloads directory, then download the latest Python from <a href="http://www.python.org">http://www.python.org</a>.  Finally, unzip and untar the package into the download directory.  Everything will extract into a directory named Python-2.5.1.</p>
<pre class="console">
$ cd Python-2.5.1
$ ./configure --prefix=$HOME/opt/ --enable-unicode=ucs4
$ make
$ make install
</pre>
<p>Change into the Python-2.5.1 directory and type in the following configure command.  Basically, configure prepares your installation for compilation.  The &#8211;prefix flag will install machine-independent data files in subdirectories of the specified directory. The default is to install in /usr/local, but it&#8217;s overwritten with the opt directory created earlier.  Finally, run make and make install which will install your custom Python installation.</p>
<pre class="console">
$ cd ..
$ rm -rf Python-2.5.1
</pre>
<p>Finally, delete the Python-2.5.1 directory.  Before you can use this Python installation, you have to add the /opt/bin directory to your path.  To do this, add /opt/bin to your .bash_profile file in your home directory.  To do so, you&#8217;ll have to add the following to your .bash_profile.</p>
<pre class="script">export PATH=$HOME/opt/bin/:$PATH</pre>
<p>Basically, this allows you to type &#8216;python&#8217; in your shell and reach the custom Python 2.5.1 installation instead of the Dreamhost one.  To make sure that our Python installation is working, type the following in your home directory (cd ~):</p>
<pre class="console">
$ source .bash_profile
$ python --version
</pre>
<p>After the last command, you should see the following: Python 2.5.1.  If that displays, your upgrade was successful!  After upgrading your Python installation, you&#8217;re not done yet.  Since Dreamhost uses an old <a href="http://mysql-python.sourceforge.net/">MySQL-Python</a> installation, we&#8217;ll upgrade that as well.  Type the following in your home directory:</p>
<pre class="console">
$ cd downloads
$ wget http://internap.dl.sourceforge.net/sourceforge/mysql-python/MySQL-python-1.2.2.tar.gz
$ tar xvzf MySQL-python-1.2.2.tar.gz
$ cd MySQL-python-1.2.2
$ python setup.py install
</pre>
<p>First, change into the downloads directory and issue the wget command to download the latest MySQL-Python files from Sourceforge.  Once you&#8217;ve received the files, unzip and untar the package.  All the files will extract into a directory called MySQL-python-1.2.2.  Change into this directory and install the files by typing python setup.py install.  If you followed the custom Python installation, the files should build and extract into the ~/opt/lib/python2.5/site-packages/ directory as an egg file. </p>
<p>You now have a custom Python installation and a MySQL-Python upgrade!</p>
<h2>Voila!</h2>
<p><strong>Update</strong>:  Just so you don&#8217;t get caught up in the same mistake that I made, to be sure that your Django fcgi installation is using your custom Python installation, make sure the <strong>dispatch.fcgi</strong> file reads as such:</p>
<pre class="console">
#!/home/USERNAME_GOES_HERE/opt/bin/python
import sys

sys.path += ['WHATEVER_PATHS_YOU_NEED']

from fcgi import WSGIServer
from django.core.handlers.wsgi import WSGIHandler
import os

os.environ['DJANGO_SETTINGS_MODULE'] = 'wegoeat.settings'
WSGIServer(WSGIHandler()).run()
</pre>
<img src="http://blog.localkinegrinds.com/?ak_action=api_record_view&id=114&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://blog.localkinegrinds.com/2007/08/20/custom-python-installation-for-django-on-dreamhost/feed/</wfw:commentRss>
		<slash:comments>25</slash:comments>
		</item>
		<item>
		<title>My Dreamhost + Django + Subversion Setup</title>
		<link>http://blog.localkinegrinds.com/2007/03/29/my-dreamhost-django-subversion-setup/</link>
		<comments>http://blog.localkinegrinds.com/2007/03/29/my-dreamhost-django-subversion-setup/#comments</comments>
		<pubDate>Thu, 29 Mar 2007 12:13:22 +0000</pubDate>
		<dc:creator>ryankanno</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Dreamhost]]></category>
		<category><![CDATA[HowTo]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Subversion]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[development-setup]]></category>
		<category><![CDATA[fastcgi]]></category>
		<category><![CDATA[http://www.wegoeat.com]]></category>
		<category><![CDATA[post-commit-hook]]></category>
		<category><![CDATA[restaurants]]></category>
		<category><![CDATA[svn]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[wegoeat]]></category>

		<guid isPermaLink="false">http://blog.localkinegrinds.com/2007/03/29/my-dreamhost-django-subversion-setup/</guid>
		<description><![CDATA[Since I haven&#8217;t put out a technical article in a while, this blog will explain how I&#8217;ve setup Dreamhost + Django + Subversion to play nicely together in a seamless development environment via a shared hosting provider. Hopefully &#8211; someone, somewhere can find this information useful and insightful in their own development environment. The very [...]]]></description>
			<content:encoded><![CDATA[<p>Since I haven&#8217;t put out a technical article in a while, this blog will explain how I&#8217;ve setup <a href="http://www.dreamhost.com">Dreamhost</a> + <a href="http://www.djangoproject.com">Django</a> + <a href="http://subversion.tigris.org/">Subversion</a> to play nicely together in a seamless development environment via a shared hosting provider.  Hopefully &#8211; someone, somewhere can find this information useful and insightful in their own development environment.</p>
<p>The very first thing I did was unleash my first <a href="http://www.djangoproject.com/">Django</a> web application on <a href="http://www.dreamhost.com">Dreamhost</a>.  Thanks to an excellent tutorial from <a href="http://www2.jeffcroft.com/blog/2006/may/11/django-dreamhost/">Jeff Croft</a>, a detailed explanation about <a href="http://www.djangoproject.com/documentation/fastcgi/">FastCGI contained within the Django documentation</a>, and a few helpful pointers on the <a href="http://wiki.dreamhost.com/index.php/Django">Dreamhost wiki</a>, I was able to get my application deployed in a matter of a few hours.</p>
<h2>You can check it out <a href="http://www.wegoeat.com/">here</a>!</h2>
<p>However, after going through Jeff&#8217;s excellent tutorial, I still wasn&#8217;t completely satisfied with my Django deployment on Dreamhost.  <em>Something was missing</em>.  There wasn&#8217;t a seamless way to continue development on my home machine, deploy to a test environment, and still keep <a href="http://www.wegoeat.com">my live site</a> intact.  After all, I&#8217;m a true believer in the open source dictum of  &#8216;<em>release early, release often</em>&#8216;, and without a way to test my application on a live server, I wasn&#8217;t happy with my configuration management.</p>
<p>Ideally, I envisioned having a live web application (i.e. <a href="http://www.wegoeat.com">http://www.wegoeat.com/</a>) and another url that I could deploy my beta releases to (i.e. <a href="http://beta.wegoeat.com">http://beta.wegoeat.com/</a>).  From a configuration management standpoint, I would tag major release builds and to maintain that release over its life (via bug fixes, minor enhancements), I would create a branch of the tag.  Thus, the live site would be updated from the branches directory, while the beta url would update from the trunk in my Subversion repository.  So to summarize the &#8216;extra&#8217; steps I did to ensure a smoother deployment cycle, I&#8217;ve conjured up the following action list.</p>
<ol>
<li>The very first thing I did was follow <a href="http://www2.jeffcroft.com/blog/2006/may/11/django-dreamhost/">Jeff&#8217;s tutorial</a> &#8211; instead of creating a single directory in my django_projects directory, I created two.  One was named &#8216;project_live&#8217; and the other &#8216;project_beta&#8217;.</li>
<li>Next, I checked out the appropriate source files from the appropriate locations in my Subversion repository.  The &#8216;project_live&#8217; directory came from my branches directory and represents my &#8216;live&#8217; site.  The &#8216;project_beta&#8217; directory came from the trunk and represents my &#8216;beta&#8217; site.  Obviously, the settings.py file for the Django applications as well as the configuration files for FastCGI were different according to the directories.  Since my settings will probably be very different then your settings, I&#8217;ll leave this as an exercise to the reader.</li>
<li>Note, as far as Dreamhost goes, I created two domain entries, one @ <a href="http://www.wegoeat.com">http://www.wegoeat.com</a> that will host my live site, and another @ <a href="http://beta.wegoeat.com">http://beta.wegoeat.com</a> that will be my beta site.</li>
<li>I followed <a href="http://blog.localkinegrinds.com/2006/12/15/dreamhost-subversion-post-commit-tutorial/">my own tutorial</a> and created a post-commit hook to update the appropriate Dreamhost directories when I committed to the repository.</li>
</ol>
<h2 style="margin-top:1em;">And voila!  We&#8217;re done.</h2>
<p>Now, I can develop on my home machine where I&#8217;ve checked out the trunk of my Subversion repository.  Whenever I commit, the post-commit hook updates the project_beta directory on my Dreamhost account, and all the while, <a href="http://www.wegoeat.com">my live site</a> is still functioning.</p>
<p>Stay tuned for my next blog where I discuss how to get <a href="http://wiki.dreamhost.com/index.php/Installing_PHP5#Custom_PHP_5">Custom PHP</a> + <a href="www.mediawiki.org/">MediaWiki</a> + <a href="http://www.eaccelerator.net/">EAccelerator</a> playing nicely together on Dreamhost!</p>
<p><!--adsense--></p>
<img src="http://blog.localkinegrinds.com/?ak_action=api_record_view&id=96&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://blog.localkinegrinds.com/2007/03/29/my-dreamhost-django-subversion-setup/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Served from: blog.localkinegrinds.com @ 2012-02-07 21:06:53 -->
