<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4123748873183487963</id><updated>2011-08-24T07:15:04.372-05:00</updated><category term='beard'/><category term='Python'/><category term='PIL'/><category term='postgresql'/><category term='Hack'/><category term='web'/><category term='Family'/><category term='Image'/><category term='OpenCV'/><category term='Math'/><category term='graphviz'/><category term='Programming'/><category term='OS X'/><category term='BarCampMemphis'/><category term='applescript'/><category term='audio'/><category term='Games'/><category term='Backup'/><category term='fabric'/><category term='Apps'/><category term='bread'/><category term='Mac'/><category term='Food'/><category term='Safari'/><category term='unicode'/><category term='vim'/><category term='hg'/><category term='Spam'/><category term='cron'/><category term='virtualenv'/><category term='Video'/><category term='OpenGL'/><category term='apache'/><category term='BeautifulSoup'/><category term='meme'/><category term='cx_oracle'/><category term='The Girls'/><category term='SiteSprint'/><category term='mysql'/><category term='wifi'/><category term='Javascript'/><category term='php'/><category term='ajax'/><category term='awesome'/><category term='music'/><category term='robots'/><category term='Prototype'/><category term='Fun'/><category term='django'/><category term='ascii'/><category term='networking'/><category term='Python PDF'/><category term='Fink'/><category term='iPhone'/><category term='iTunes'/><category term='homebrew'/><category term='#bcmem'/><category term='Linux'/><category term='Hardware'/><category term='humanity'/><category term='Ubuntu'/><category term='pyglet'/><category term='mercurial'/><title type='text'>brad's blog</title><subtitle type='html'>Now at &lt;a href="http://blog.bradmontgomery.net"&gt;blog.bradmontgomery.net&lt;/a&gt;</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://bradmontgomery.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default?start-index=101&amp;max-results=100'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>121</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-2770750782513065143</id><published>2010-11-02T17:22:00.002-05:00</published><updated>2010-11-03T08:46:37.694-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='SiteSprint'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>SiteSprint III - The Reclaimation</title><content type='html'>&lt;h1&gt;SiteSprint III - The Reclaimation&lt;/h1&gt;&lt;p&gt;My personal website is a disgrace.  As a web developer, I find it embarrassing that my own site is a cobbled-together mess of PHP and static html. Only slightly less embarrassing is my blog, which is obviously hosted on Blogger.  Even though Blogger has recently added a few new themes, I'm still unhappy with the look-and-feel of my blog.  So, since &lt;a href="http://sitesprint.info/"&gt;SiteSprint III&lt;/a&gt; has officially started, I'm taking this opportunity to Reclaim&amp;#8482; my personal website.&lt;/p&gt;&lt;h2&gt;Tech Specs - the easy part&lt;/h2&gt;&lt;p&gt;I've thoroughly enjoyed using Django over the last couple of years, and as any self-respecting djangonaught would, I'm moving my site over to that platform.  Here's the planned tech:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;nginx + gunicorn + postgresql on rackspace cloud&lt;/li&gt;&lt;li&gt;&lt;a href="http://github.com/stephenmcd/mezzanine"&gt;mezzanine&lt;/a&gt;&lt;/li&gt;&lt;li&gt;a revamp of my own &lt;a href="http://bitbucket.org/bkmontgomery/django-notes"&gt;django-notes&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Design - the not so easy part&lt;/h2&gt;&lt;p&gt;I'm not a graphic designer by any means, but I know I can do better than what I've got.  I like Minimal designs, and I'd really like to focus on typography in my site.  I've still got a lot of homework to do here. I also want make use of as much HTML5 as is feasible.  In the next few days I'll be reading up on: &lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.modernizr.com/"&gt;modernizr&lt;/a&gt;&lt;/li&gt;&lt;li&gt;font foundries (&lt;a href="http://typekit.com/"&gt;typekit&lt;/a&gt;, &lt;a href="http://www.typotheque.com/"&gt;Typotheque&lt;/a&gt;, or &lt;a href="http://kernest.com/"&gt;kernest&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/typogrify/"&gt;django-typogrify&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Wish me luck, and feel free to point out any tools I might find interesting.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-2770750782513065143?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2770750782513065143'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2770750782513065143'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2010/11/sitesprint-iii-reclaimation.html' title='SiteSprint III - The Reclaimation'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-5282351448916778629</id><published>2010-10-15T11:26:00.003-05:00</published><updated>2010-10-15T11:31:36.129-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Python: stray commas cause tuples?</title><content type='html'>As I try to debug a strange problem in a Django view, I notice a stray comma after a dictionary definition. So I jump over to a python shell, and guess what?  Ending a literal dict with a comma creates a tuple.  &lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #c65d09; font-weight: bold"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;d &lt;span style="color: #666666"&gt;=&lt;/span&gt; {&lt;span style="color: #40a070"&gt;1&lt;/span&gt;:&lt;span style="color: #4070a0"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;},&lt;br /&gt;&lt;span style="color: #c65d09; font-weight: bold"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span style="color: #007020"&gt;type&lt;/span&gt;(d)&lt;br /&gt;&lt;span style="color: #808080"&gt;&amp;lt;type &amp;#39;tuple&amp;#39;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #808080"&gt;&lt;/span&gt;&lt;span style="color: #c65d09; font-weight: bold"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;d&lt;br /&gt;&lt;span style="color: #808080"&gt;({1: &amp;#39;foo&amp;#39;},)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #808080"&gt;&lt;/span&gt;&lt;span style="color: #c65d09; font-weight: bold"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-5282351448916778629?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/5282351448916778629'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/5282351448916778629'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2010/10/python-stray-commas-cause-tuples.html' title='Python: stray commas cause tuples?'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-3619337419322141293</id><published>2010-07-19T16:25:00.002-05:00</published><updated>2010-07-19T16:54:14.901-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>A case for values_list</title><content type='html'>Here's the Scenario:  I have a model (lets call it Contact) with two Foreign Keys, one of which is related to User in Django's contrib.auth app.  I need to build a form that lets me select an existing object, and a new user.  &lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;ContactType&lt;/span&gt;(Model):&lt;br /&gt;    name &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #007020"&gt;CharField&lt;/span&gt;(max_length&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #40a070"&gt;128&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;Contact&lt;/span&gt;(Model):&lt;br /&gt;    user &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #007020"&gt;ForeignKey&lt;/span&gt;(User)&lt;br /&gt;    contact_type &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #007020"&gt;ForeignKey&lt;/span&gt;(ContactType)&lt;br /&gt;    &lt;span style="color: #60a0b0; font-style: italic"&gt;# possibly more fields...&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;I need to select from existing models, so my first thought might be to build a form that uses two &lt;code&gt;&lt;a href="http://docs.djangoproject.com/en/1.2/ref/forms/fields/#django.forms.ModelChoiceField"&gt;ModelChoiceField&lt;/a&gt;&lt;/code&gt;'s.  I also want to modify the way that my form displays each choice, so I &lt;em&gt;could&lt;/em&gt; extend ModelChoiceField by overriding the &lt;code&gt;label_from_instance&lt;/code&gt; method:&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;UserModelChoiceField&lt;/span&gt;(ModelChoiceField):&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;label_from_instance&lt;/span&gt;(&lt;span style="color: #007020"&gt;self&lt;/span&gt;, obj):&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold"&gt;return&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%s&lt;/span&gt;&lt;span style="color: #4070a0"&gt; (&lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%s&lt;/span&gt;&lt;span style="color: #4070a0"&gt;)&amp;quot;&lt;/span&gt;&lt;span style="color: #666666"&gt;%&lt;/span&gt;(obj&lt;span style="color: #666666"&gt;.&lt;/span&gt;get_full_name(), obj&lt;span style="color: #666666"&gt;.&lt;/span&gt;username)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;ContactModelChoiceField&lt;/span&gt;(ModelChoiceField):&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;label_from_instance&lt;/span&gt;(&lt;span style="color: #007020"&gt;self&lt;/span&gt;, obj):&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold"&gt;return&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%s&lt;/span&gt;&lt;span style="color: #4070a0"&gt; (&lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%s&lt;/span&gt;&lt;span style="color: #4070a0"&gt;)&amp;#39;&lt;/span&gt; &lt;span style="color: #666666"&gt;%&lt;/span&gt; (obj&lt;span style="color: #666666"&gt;.&lt;/span&gt;type, obj&lt;span style="color: #666666"&gt;.&lt;/span&gt;user&lt;span style="color: #666666"&gt;.&lt;/span&gt;get_full_name())&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;CopyContactForm&lt;/span&gt;(forms&lt;span style="color: #666666"&gt;.&lt;/span&gt;Form):&lt;br /&gt;    contact &lt;span style="color: #666666"&gt;=&lt;/span&gt; ContactModelChoiceField(Contact&lt;span style="color: #666666"&gt;.&lt;/span&gt;objects&lt;span style="color: #666666"&gt;.&lt;/span&gt;all())&lt;br /&gt;    new_user &lt;span style="color: #666666"&gt;=&lt;/span&gt; UserModelChoiceField(User&lt;span style="color: #666666"&gt;.&lt;/span&gt;objects&lt;span style="color: #666666"&gt;.&lt;/span&gt;all()&lt;span style="color: #666666"&gt;.&lt;/span&gt;order_by(&lt;span style="color: #4070a0"&gt;&amp;#39;first_name&amp;#39;&lt;/span&gt;, &lt;span style="color: #4070a0"&gt;&amp;#39;last_name&amp;#39;&lt;/span&gt;, &lt;span style="color: #4070a0"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;)) &lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;This actually provides a solution to my original problem, but it's not very efficient.  Notice that both the &lt;code&gt;UserModelChoiceField&lt;/code&gt; and the &lt;code&gt;ContactModelChoiceField&lt;/code&gt; call methods on each object with the latter accessing a foreign key.  In an app with 600 Users and 600 Contacts, this form would generate around 1200 queries!&lt;br /&gt;&lt;br /&gt;There's actually a very efficient way to generate the same sort of form using &lt;code&gt;&lt;a href="http://docs.djangoproject.com/en/dev/topics/db/optimization/#use-queryset-values-and-values-list"&gt;values_list&lt;/a&gt;&lt;/code&gt;, especially, when you realize that the form really just needs to contain something like the following:&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;select&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;option&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;1&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;John Doe&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;option&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;2&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;Jane Doe&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;So a more efficient solution to my problem looks something like the code below, which yields two queries.&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;CopyContactForm&lt;/span&gt;(forms&lt;span style="color: #666666"&gt;.&lt;/span&gt;Form):&lt;br /&gt;    contact &lt;span style="color: #666666"&gt;=&lt;/span&gt; forms&lt;span style="color: #666666"&gt;.&lt;/span&gt;ChoiceField(choices&lt;span style="color: #666666"&gt;=&lt;/span&gt;[(c[&lt;span style="color: #40a070"&gt;0&lt;/span&gt;], &lt;span style="color: #4070a0"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%s&lt;/span&gt;&lt;span style="color: #4070a0"&gt; (&lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%s&lt;/span&gt;&lt;span style="color: #4070a0"&gt; &lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%s&lt;/span&gt;&lt;span style="color: #4070a0"&gt;)&amp;#39;&lt;/span&gt;&lt;span style="color: #666666"&gt;%&lt;/span&gt;(c[&lt;span style="color: #40a070"&gt;1&lt;/span&gt;],c[&lt;span style="color: #40a070"&gt;2&lt;/span&gt;],c[&lt;span style="color: #40a070"&gt;3&lt;/span&gt;])) \&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold"&gt;for&lt;/span&gt; c &lt;span style="color: #007020; font-weight: bold"&gt;in&lt;/span&gt; Contact&lt;span style="color: #666666"&gt;.&lt;/span&gt;objects&lt;span style="color: #666666"&gt;.&lt;/span&gt;values_list(&lt;span style="color: #4070a0"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;, &lt;span style="color: #4070a0"&gt;&amp;#39;type__name&amp;#39;&lt;/span&gt;, &lt;span style="color: #4070a0"&gt;&amp;#39;user__first_name&amp;#39;&lt;/span&gt;, &lt;span style="color: #4070a0"&gt;&amp;#39;user__last_name&amp;#39;&lt;/span&gt;)])&lt;br /&gt;    new_user &lt;span style="color: #666666"&gt;=&lt;/span&gt; forms&lt;span style="color: #666666"&gt;.&lt;/span&gt;ChoiceField(choices&lt;span style="color: #666666"&gt;=&lt;/span&gt;[(u[&lt;span style="color: #40a070"&gt;0&lt;/span&gt;], &lt;span style="color: #4070a0"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%s&lt;/span&gt;&lt;span style="color: #4070a0"&gt; &lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%s&lt;/span&gt;&lt;span style="color: #4070a0"&gt; (&lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%s&lt;/span&gt;&lt;span style="color: #4070a0"&gt;)&amp;#39;&lt;/span&gt;&lt;span style="color: #666666"&gt;%&lt;/span&gt;(u[&lt;span style="color: #40a070"&gt;1&lt;/span&gt;],u[&lt;span style="color: #40a070"&gt;2&lt;/span&gt;],u[&lt;span style="color: #40a070"&gt;3&lt;/span&gt;])) \&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold"&gt;for&lt;/span&gt; u &lt;span style="color: #007020; font-weight: bold"&gt;in&lt;/span&gt; User&lt;span style="color: #666666"&gt;.&lt;/span&gt;objects&lt;span style="color: #666666"&gt;.&lt;/span&gt;values_list(&lt;span style="color: #4070a0"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;, &lt;span style="color: #4070a0"&gt;&amp;#39;first_name&amp;#39;&lt;/span&gt;, &lt;span style="color: #4070a0"&gt;&amp;#39;last_name&amp;#39;&lt;/span&gt;, &lt;span style="color: #4070a0"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;)])&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-3619337419322141293?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/3619337419322141293'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/3619337419322141293'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2010/07/case-for-valueslist.html' title='A case for values_list'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-671214602676980877</id><published>2010-07-14T22:14:00.002-05:00</published><updated>2010-07-14T22:30:02.980-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='virtualenv'/><category scheme='http://www.blogger.com/atom/ns#' term='OS X'/><category scheme='http://www.blogger.com/atom/ns#' term='graphviz'/><category scheme='http://www.blogger.com/atom/ns#' term='Mac'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>PyGraphviz on OS X (SL) with virtualenv</title><content type='html'>There's this cool project called &lt;a href="http://github.com/django-extensions/"&gt;django-extensions&lt;/a&gt; that (among other things) adds a lot of commands to django's &lt;code&gt;manage.py&lt;/code&gt; offerings.  One of which is &lt;code&gt;./manage.py graph_models [appname]&lt;/code&gt; which will generate a nice graph displaying the relationships among all of your Models.  This comand needs pyGraphViz, though, and I was a little disappointed when i discovered I couldn't install pyGraphViz with &lt;code&gt;pip install pygraphviz&lt;/code&gt;. (ok, a lot disappointed). &lt;br /&gt;&lt;br /&gt;I eventually got this working, and here's how:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;First, install the development snapshot of GraphViz: &lt;a href="http://graphviz.org/Download_macos.php"&gt;http://graphviz.org/Download_macos.php&lt;/a&gt; (I used graphviz-2.27.20100713.0445.pkg)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Next, download the pygraphviz source code from &lt;a href="http://networkx.lanl.gov/pygraphviz/download.html"&gt;http://networkx.lanl.gov/pygraphviz/download.html&lt;/a&gt;. (Again, I got pygraphviz-0.99.1/ )&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Here's the fun part.  You need to edit pygraphviz's setup.py file (located at pygraphviz-0.99.1/setup.py for me). Find the following two lines of code:&lt;pre&gt;&lt;br /&gt;library_path=None&lt;br /&gt;include_path=None&lt;/pre&gt;&lt;br /&gt;And change them to&lt;pre&gt;&lt;br /&gt;library_path='/usr/local/lib/graphviz/'&lt;br /&gt;include_path='/usr/local/include/graphviz/'&lt;/pre&gt;&lt;br /&gt;You also might want to verify that the above directories exist.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Now, active your virtualenv (workon &amp;lt;whatever_your_virtualenv_is_named&amp;gt;)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Finally, just run &lt;code&gt;python setup.py install&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;That's it! This installed pygraphviz in my activated virtualenv, and I can now use &lt;code&gt;./manage.py graph_models&lt;/code&gt; to my heart's desire.&lt;br /&gt;&lt;br /&gt;I should mention that I'm using &lt;a href="http://pypi.python.org/pypi/distribute"&gt;distribute&lt;/a&gt; (and it may be safe to assume that if you're using virtualenv, you're probably also using distribute).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-671214602676980877?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/671214602676980877'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/671214602676980877'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2010/07/pygraphviz-on-os-x-sl-with-virtualenv.html' title='PyGraphviz on OS X (SL) with virtualenv'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-5589084931666424010</id><published>2010-06-09T09:12:00.004-05:00</published><updated>2010-09-08T14:31:52.724-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='apache'/><title type='text'>Vim Syntax highlighting for Apache Config Files</title><content type='html'>I use Django with Apache and mod_wsgi.  Each project that I work on has different apache config files, so I like to keep those in the same mercurial repo that contains my django project's code.&lt;br /&gt;&lt;br /&gt;For some time now, it's been bugging me that vim doesn't do syntax highlighting for those apache configs (nor the wsgi files). I finally decided to do something about it, and I'm glad I did, because it's a fairly simple fix:&lt;br /&gt;&lt;br /&gt;My project directory layout often look like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;~/django_projects/projectname/&lt;br /&gt;    __init__.py&lt;br /&gt;    apache_configs/&lt;br /&gt;        projectname&lt;br /&gt;        projectname.wsgi&lt;br /&gt;    manage.py&lt;br /&gt;    settings.py&lt;br /&gt;    myapp/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Vim does syntax highlighting for apache files based on their path. To get vim to recognize the files in the &lt;em&gt;apache_configs&lt;/em&gt; directory, you need to edit (or create) &lt;em&gt;~/.vim/filetype.vim&lt;/em&gt; and add the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;au BufNewFile,BufRead ~/django_projects/de_concierge/apache_configs/*wsgi setf python&lt;br /&gt;au BufNewFile,BufRead ~/django_projects/de_concierge/apache_configs/* setf apache &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You'd have to add variants of this for each separate project, but this works well for me. &lt;br /&gt;&lt;br /&gt;There's several other ways you can modify this, and the solution that I used comes from &lt;a href="http://groups.google.com/group/vim_mac/browse_thread/thread/26c3a097e6e69c5f"&gt;this thread on the vim_mac mailing list&lt;/a&gt;.  (There's also a lot of good info in &lt;em&gt;:help new-filetype &lt;/em&gt;).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-5589084931666424010?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/5589084931666424010'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/5589084931666424010'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2010/06/syntax-highlighting-for-apache-config.html' title='Vim Syntax highlighting for Apache Config Files'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-2232988455138779924</id><published>2010-05-28T15:39:00.004-05:00</published><updated>2010-05-28T15:52:44.497-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='BeautifulSoup'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Convert Tables to Unordered Lists</title><content type='html'>If you've ever had the pleasure of working with old HTML content, you've surely seen some &amp;lt;table&amp;gt;'s where they don't belong.  Lately, that's the sort of thing I've been dealing with on a regular basis, and for some reason, I often see a list of information in a table.&lt;br /&gt;&lt;br /&gt;Wouldn't it be nice if there were an easy way to turn these tables into unordered lists?  Thanks to &lt;a href="http://www.crummy.com/software/BeautifulSoup/"&gt;BeautifulSoup&lt;/a&gt;, this is really not that difficult.&lt;br /&gt;&lt;br /&gt;Here's the code:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;BeautifulSoup&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; BeautifulSoup, Tag &lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;table2ul&lt;/span&gt;(content, flatten_rows&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #007020"&gt;False&lt;/span&gt;):&lt;br /&gt;    &lt;span style="color: #4070a0; font-style: italic"&gt;&amp;quot;&amp;quot;&amp;quot; &lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    Convert a &amp;lt;table&amp;gt; into a &amp;lt;ul&amp;gt;.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    Each cell, &amp;lt;td&amp;gt;, gets converted into a list item &amp;lt;li&amp;gt; unless&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    the flatten_rows paramter is given. In this case, all content from &lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    a table row, &amp;lt;tr&amp;gt;, gets converted into a list item.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;    soup &lt;span style="color: #666666"&gt;=&lt;/span&gt; BeautifulSoup(content, convertEntities&lt;span style="color: #666666"&gt;=&lt;/span&gt;BeautifulSoup&lt;span style="color: #666666"&gt;.&lt;/span&gt;HTML_ENTITIES, smartQuotesTo&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #4070a0"&gt;&amp;quot;html&amp;quot;&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;for&lt;/span&gt; table &lt;span style="color: #007020; font-weight: bold"&gt;in&lt;/span&gt; soup&lt;span style="color: #666666"&gt;.&lt;/span&gt;findAll(&lt;span style="color: #4070a0"&gt;&amp;#39;table&amp;#39;&lt;/span&gt;):&lt;br /&gt;        ul &lt;span style="color: #666666"&gt;=&lt;/span&gt; Tag(soup, &lt;span style="color: #4070a0"&gt;&amp;#39;ul&amp;#39;&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold"&gt;if&lt;/span&gt; flatten_rows:&lt;br /&gt;            &lt;span style="color: #007020; font-weight: bold"&gt;for&lt;/span&gt; row &lt;span style="color: #007020; font-weight: bold"&gt;in&lt;/span&gt; table&lt;span style="color: #666666"&gt;.&lt;/span&gt;findAll(&lt;span style="color: #4070a0"&gt;&amp;#39;tr&amp;#39;&lt;/span&gt;):&lt;br /&gt;                li &lt;span style="color: #666666"&gt;=&lt;/span&gt; Tag(soup, &lt;span style="color: #4070a0"&gt;&amp;#39;li&amp;#39;&lt;/span&gt;)&lt;br /&gt;                &lt;span style="color: #007020; font-weight: bold"&gt;for&lt;/span&gt; cell &lt;span style="color: #007020; font-weight: bold"&gt;in&lt;/span&gt; row&lt;span style="color: #666666"&gt;.&lt;/span&gt;findAll(&lt;span style="color: #4070a0"&gt;&amp;#39;td&amp;#39;&lt;/span&gt;):&lt;br /&gt;                    li&lt;span style="color: #666666"&gt;.&lt;/span&gt;contents&lt;span style="color: #666666"&gt;.&lt;/span&gt;extend(cell&lt;span style="color: #666666"&gt;.&lt;/span&gt;contents)&lt;br /&gt;                ul&lt;span style="color: #666666"&gt;.&lt;/span&gt;append(li)&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold"&gt;else&lt;/span&gt;:&lt;br /&gt;            &lt;span style="color: #007020; font-weight: bold"&gt;for&lt;/span&gt; cell &lt;span style="color: #007020; font-weight: bold"&gt;in&lt;/span&gt; table&lt;span style="color: #666666"&gt;.&lt;/span&gt;findAll(&lt;span style="color: #4070a0"&gt;&amp;#39;td&amp;#39;&lt;/span&gt;):&lt;br /&gt;                li &lt;span style="color: #666666"&gt;=&lt;/span&gt; Tag(soup, &lt;span style="color: #4070a0"&gt;&amp;#39;li&amp;#39;&lt;/span&gt;)&lt;br /&gt;                li&lt;span style="color: #666666"&gt;.&lt;/span&gt;contents &lt;span style="color: #666666"&gt;=&lt;/span&gt; cell&lt;span style="color: #666666"&gt;.&lt;/span&gt;contents&lt;br /&gt;                ul&lt;span style="color: #666666"&gt;.&lt;/span&gt;append(li)&lt;br /&gt;        table&lt;span style="color: #666666"&gt;.&lt;/span&gt;replaceWith(ul)&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;return&lt;/span&gt; soup&lt;span style="color: #666666"&gt;.&lt;/span&gt;prettify()&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Now, suppose we had the following HTML snippet:&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Some heading&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Some paragraph with stuff in it&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;table&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt; row 1, &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;col1&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt;row 1, col2&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt; row 2, col1&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;em&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span style="color: #4070a0"&gt;href=&amp;quot;http://google.com&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;row 2&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/a&amp;gt;&amp;lt;/em&amp;gt;&lt;/span&gt;, col2&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;A second heading&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;more peee&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Passing this in to &lt;strong&gt;table2ul&lt;/strong&gt; would convert each cell into a list item, &amp;lt;li&amp;gt;. &lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #c65d09; font-weight: bold"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;table2ul(content)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;&lt;br /&gt; Some heading&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;&lt;br /&gt; Some paragraph with stuff in it&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;br /&gt;  row 1,&lt;br /&gt;  &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;&lt;br /&gt;   col1&lt;br /&gt;  &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;br /&gt;  row 1, col2&lt;br /&gt; &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;br /&gt;  row 2, col1&lt;br /&gt; &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;em&amp;gt;&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;a&lt;/span&gt; &lt;span style="color: #4070a0"&gt;href=&amp;quot;http://google.com&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    row 2&lt;br /&gt;   &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/em&amp;gt;&lt;/span&gt;&lt;br /&gt;  , col2&lt;br /&gt; &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;&lt;br /&gt; A second heading&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;&lt;br /&gt; more peee&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;But what if we don't want each &amp;lt;td&amp;gt; converted into an &amp;lt;li%gt&gt;? What if we want all the content from entire row in an &amp;lt;li&amp;gt;?  In that case, just set the optional &lt;strong&gt;flatten_rows&lt;/strong&gt; parameter:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #c65d09; font-weight: bold"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;table2ul(content, flatten_rows&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #007020"&gt;True&lt;/span&gt;)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;&lt;br /&gt; Some heading&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;&lt;br /&gt; Some paragraph with stuff in it&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;br /&gt;  row 1,&lt;br /&gt;  &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;&lt;br /&gt;   col1&lt;br /&gt;  &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;  row 1, col2&lt;br /&gt; &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;&lt;br /&gt; &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;br /&gt;  row 2, col1&lt;br /&gt;  &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;em&amp;gt;&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;a&lt;/span&gt; &lt;span style="color: #4070a0"&gt;href=&amp;quot;http://google.com&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    row 2&lt;br /&gt;   &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/em&amp;gt;&lt;/span&gt;&lt;br /&gt;  , col2&lt;br /&gt; &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;&lt;br /&gt; A second heading&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;&lt;br /&gt; more peee&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;This has been somewhat useful for me.  Hope it's useful for you!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-2232988455138779924?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2232988455138779924'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2232988455138779924'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2010/05/convert-tables-to-unordered-lists.html' title='Convert Tables to Unordered Lists'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-2524432454961244471</id><published>2010-04-22T09:07:00.004-05:00</published><updated>2010-04-22T10:27:33.175-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Pretty options for Django's auth.User</title><content type='html'>Several of my Django Apps have Foreign Key relationships to django.contrib.auth.model.User.  In Django's admin app, these show up a select elements displaying the username attribute.  For some people, that may be OK, but for most of the people with which I work, it's not.  We want to see &lt;em&gt;prettier&lt;/em&gt; options, i.e. each User's full name as the options in that select element.&lt;br /&gt;&lt;br /&gt;So, here's how it works. We override the ModelChoiceField (for ForeignKeys) and the ModelMultipleChoiceField (for ManyToMany Fields):&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;django.forms&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; ModelChoiceField, ModelMultipleChoiceField&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;UserModelChoiceField&lt;/span&gt;(ModelChoiceField):&lt;br /&gt;    &lt;span style="color: #4070a0; font-style: italic"&gt;&amp;#39;&amp;#39;&amp;#39; &lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    A ModelChoiceField to represent User &lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    select boxes in the Auto Admin &lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    &amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;label_from_instance&lt;/span&gt;(&lt;span style="color: #007020"&gt;self&lt;/span&gt;, obj):&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold"&gt;return&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%s&lt;/span&gt;&lt;span style="color: #4070a0"&gt; (&lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%s&lt;/span&gt;&lt;span style="color: #4070a0"&gt;)&amp;quot;&lt;/span&gt;&lt;span style="color: #666666"&gt;%&lt;/span&gt;(obj&lt;span style="color: #666666"&gt;.&lt;/span&gt;get_full_name(), obj&lt;span style="color: #666666"&gt;.&lt;/span&gt;username)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;UserModelMultipleChoiceField&lt;/span&gt;(ModelMultipleChoiceField):&lt;br /&gt;    &lt;span style="color: #4070a0; font-style: italic"&gt;&amp;#39;&amp;#39;&amp;#39; &lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    Similar to UserModelChoiceField, provide a nicer-looking &lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    list of user names for ManyToMany Relations...&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    &amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;label_from_instance&lt;/span&gt;(&lt;span style="color: #007020"&gt;self&lt;/span&gt;, obj):&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold"&gt;return&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%s&lt;/span&gt;&lt;span style="color: #4070a0"&gt; (&lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%s&lt;/span&gt;&lt;span style="color: #4070a0"&gt;)&amp;quot;&lt;/span&gt;&lt;span style="color: #666666"&gt;%&lt;/span&gt;(obj&lt;span style="color: #666666"&gt;.&lt;/span&gt;get_full_name(), obj&lt;span style="color: #666666"&gt;.&lt;/span&gt;username)&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Then, to customize the admin, you need to create a custome ModelForm for your Model. So, if I had a Model that looked like this: &lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;MyModel&lt;/span&gt;(models&lt;span style="color: #666666"&gt;.&lt;/span&gt;Model):&lt;br /&gt;    user &lt;span style="color: #666666"&gt;=&lt;/span&gt; models&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;ForeignKey&lt;/span&gt;(User)&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;You'd need to create the following ModelForm:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;MyModelAdminForm&lt;/span&gt;(forms&lt;span style="color: #666666"&gt;.&lt;/span&gt;ModelForm):&lt;br /&gt;    user &lt;span style="color: #666666"&gt;=&lt;/span&gt; UserModelChoiceField(User&lt;span style="color: #666666"&gt;.&lt;/span&gt;objects&lt;span style="color: #666666"&gt;.&lt;/span&gt;all()&lt;span style="color: #666666"&gt;.&lt;/span&gt;order_by(&lt;span style="color: #4070a0"&gt;&amp;#39;first_name&amp;#39;&lt;/span&gt;, &lt;span style="color: #4070a0"&gt;&amp;#39;last_name&amp;#39;&lt;/span&gt;, &lt;span style="color: #4070a0"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;))&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;Meta&lt;/span&gt;:&lt;br /&gt;        model &lt;span style="color: #666666"&gt;=&lt;/span&gt; MyModel&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Now, when you create a ModelAdmin class for the MyModel, you specify the above form:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;models&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; MyModel&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;forms&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; MyModelAdminForm&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;MyModelAdmin&lt;/span&gt;(admin&lt;span style="color: #666666"&gt;.&lt;/span&gt;ModelAdmin):&lt;br /&gt;    form &lt;span style="color: #666666"&gt;=&lt;/span&gt; MyModelAdminForm&lt;br /&gt;admin&lt;span style="color: #666666"&gt;.&lt;/span&gt;site&lt;span style="color: #666666"&gt;.&lt;/span&gt;register(MyModel, MyModelAdmin)&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;At this point, the choices for User objects in the admin should contain the user's full name and their username in parenthesis.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-2524432454961244471?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2524432454961244471'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2524432454961244471'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2010/04/pretty-options-for-djangos-authuser.html' title='Pretty options for Django&apos;s auth.User'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-5730495084310549311</id><published>2010-03-25T15:21:00.002-05:00</published><updated>2010-03-25T15:38:00.484-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='unicode'/><category scheme='http://www.blogger.com/atom/ns#' term='ascii'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Dealing with Unicode and ASCII using Python</title><content type='html'>Dealing with Character Encodings is (sometimes) hard.  It's especially confusing for those who've never done it before.  Converting text from unicode to ascii can be tricky.  &lt;br /&gt;&lt;br /&gt;A lot of times, I'll import some data from a text file, and I just want to convert everything to ASCII and ignore anything that's not ascii (like MS Word's smart quotes).  Luckily, this is fairly easy:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;mystring &lt;span style="color: #666666"&gt;=&lt;/span&gt; mystring&lt;span style="color: #666666"&gt;.&lt;/span&gt;decode(&lt;span style="color: #4070a0"&gt;&amp;#39;ascii&amp;#39;&lt;/span&gt;, &lt;span style="color: #4070a0"&gt;&amp;#39;ignore&amp;#39;&lt;/span&gt;)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;There's tons of great Python resources (and code!) for all your character encoding needs. In no particular order, here are a few I've found:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.pyzine.com/Issue008/Section_Articles/article_Encodings.html"&gt;A Crash Course in Character Encoding&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://diveintopython.org/xml_processing/unicode.html"&gt;Dive Into Python's Chapter on Unicode&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.crummy.com/software/BeautifulSoup/"&gt;Beautiful Soup&lt;/a&gt; gives you &lt;a href="http://www.crummy.com/software/BeautifulSoup/documentation.html#Beautiful Soup Gives You Unicode, Dammit"&gt;Unicode, Dammit&lt;/a&gt; and there's the companion: &lt;a href="http://www.crummy.com/cgi-bin/msm/map.cgi/ASCII,+Dammit"&gt;ASCII, Dammit&lt;/a&gt;&lt;/li&gt;&lt;li&gt;There's also &lt;a href="http://effbot.python-hosting.com/file/stuff/sandbox/text/unaccent.py"&gt;unaccent.py&lt;/a&gt;, which seems to convert various unicode characters to their ascii equivalent.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;There's probably more, but most of these have helped me get the job done.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-5730495084310549311?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/5730495084310549311'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/5730495084310549311'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2010/03/dealing-with-unicode-and-ascii-using.html' title='Dealing with Unicode and ASCII using Python'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-275783365579766815</id><published>2010-03-23T15:02:00.003-05:00</published><updated>2010-03-23T15:07:14.894-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='awesome'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>On select_related()</title><content type='html'>If you use &lt;a href="http://www.djangoproject.com"&gt;Django&lt;/a&gt;, and your models have relationships that span across multiple tables, you need to read this: &lt;a href="http://docs.djangoproject.com/en/dev/ref/models/querysets/#id4"&gt;http://docs.djangoproject.com/en/dev/ref/models/querysets/#id4&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: xx-large;"&gt;select_related() is awesome.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;That is all.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-275783365579766815?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/275783365579766815'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/275783365579766815'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2010/03/on-selectrelated.html' title='On select_related()'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-3675678730344892934</id><published>2010-02-25T15:40:00.002-06:00</published><updated>2010-02-25T15:42:12.358-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PIL'/><category scheme='http://www.blogger.com/atom/ns#' term='virtualenv'/><category scheme='http://www.blogger.com/atom/ns#' term='OS X'/><category scheme='http://www.blogger.com/atom/ns#' term='homebrew'/><category scheme='http://www.blogger.com/atom/ns#' term='Mac'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Building PIL on OS X: Snow Leopard</title><content type='html'>There are &lt;a href="http://jetfar.com/libjpeg-and-python-imaging-pil-on-snow-leopard/"&gt;several&lt;/a&gt; &lt;a href="http://stackoverflow.com/questions/1438270/installing-python-imaging-library-pil-on-snow-leopard-with-updated-python-2-6-2"&gt;places&lt;/a&gt; &lt;a href="http://proteus-tech.com/blog/cwt/install-pil-in-snow-leopard/"&gt;online&lt;/a&gt; that discuss problems installing PIL on Mac OS X Snow Leopard&lt;br /&gt;&lt;br /&gt;This is  how I got it to work.&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Install lib jpeg using &lt;a href="http://github.com/mxcl/homebrew"&gt;homebrew&lt;/a&gt; (which is super-aweseome!) &lt;pre&gt;brew intall jpeg&lt;/pre&gt;.This installs the library into &lt;pre&gt;/usr/local/Cellar/jpeg/7&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Install &lt;a href="http://www.freetype.org/"&gt;libfreetype&lt;/a&gt; the old-fashioned way (./configure, make, sudo make install).  I used &lt;em&gt;freetype-2.1.10.pre-20050511&lt;/em&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Download, unpack PIL (I used &lt;a href="http://effbot.org/downloads/Imaging-1.1.6.tar.gz"&gt;Imaging-1.1.6&lt;/a&gt;). I had to make the following changes to setup.py&lt;pre&gt;FREETYPE_ROOT = "/usr/local"&lt;br /&gt;JPEG_ROOT = ("/usr/local/Cellar/jpeg/7/lib", "/usr/local/Cellar/jpeg/7/include")&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Then, build PIL:&lt;pre&gt;python setup.py build_ext -i&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If the build works without any errors, you can run the tests:&lt;pre&gt;python selftest.py&lt;/pre&gt; which should yield the following: &lt;strong&gt;57 tests passed.&lt;/strong&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;(optional) I like to use virtualenv, so I activate that: &lt;pre&gt;workon myproject&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Install PIL:&lt;pre&gt;python setup.py install&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;At this point, code like the following works for me:&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;Image&lt;/span&gt;&lt;br /&gt;im &lt;span style="color: #666666"&gt;=&lt;/span&gt; Image&lt;span style="color: #666666"&gt;.&lt;/span&gt;open(&lt;span style="color: #4070a0"&gt;&amp;quot;/path/to/pretty/picture.jpg&amp;quot;&lt;/span&gt;)&lt;br /&gt;im&lt;span style="color: #666666"&gt;.&lt;/span&gt;show()&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-3675678730344892934?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/3675678730344892934'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/3675678730344892934'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2010/02/building-pil-on-os-x-snow-leopard.html' title='Building PIL on OS X: Snow Leopard'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-763202299204946052</id><published>2010-02-24T13:50:00.002-06:00</published><updated>2010-02-24T14:01:28.071-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Fun'/><category scheme='http://www.blogger.com/atom/ns#' term='humanity'/><title type='text'>Specialization</title><content type='html'>Thanks to &lt;a href="http://twitter.com/mxcl/"&gt;@mxcl&lt;/a&gt; for &lt;a href="http://twitter.com/mxcl/status/9585025686"&gt;tweeting a link&lt;/a&gt; to this: &lt;a href="http://gist.github.com/313640"&gt;http://gist.github.com/313640&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Things I've done (or similar)&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;change a diaper&lt;/li&gt;&lt;li&gt;plan an invasion (warcraft counts, right?)&lt;/li&gt;&lt;li&gt;butcher a &lt;del&gt;hog&lt;/del&gt; &lt;ins&gt;squirrel&lt;/ins&gt;&lt;/li&gt;&lt;li&gt;design a building (with legos!)&lt;/li&gt;&lt;li&gt;write a &lt;del&gt;sonnet&lt;/del&gt; &lt;ins&gt;song&lt;/ins&gt;&lt;/li&gt;&lt;li&gt;balance accounts&lt;/li&gt;&lt;li&gt;build a &lt;del&gt;wall&lt;/del&gt; &lt;ins&gt;fence&lt;/ins&gt;&lt;/li&gt;&lt;li&gt;comfort the dying&lt;/li&gt;&lt;li&gt;take orders&lt;/li&gt;&lt;li&gt;give orders&lt;/li&gt;&lt;li&gt;cooperate&lt;/li&gt;&lt;li&gt;act alone&lt;/li&gt;&lt;li&gt;solve equations&lt;/li&gt;&lt;li&gt;analyze a new problem&lt;/li&gt;&lt;li&gt;pitch manure&lt;/li&gt;&lt;li&gt;program a computer&lt;/li&gt;&lt;li&gt;cook a tasty meal (yum!)&lt;/li&gt;&lt;li&gt;fight efficiently (yay for taekwondo!)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h2&gt;...not quite there yet:&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;conn a ship&lt;/li&gt;&lt;li&gt;set a bone&lt;/li&gt;&lt;li&gt;die gallantly (here's hoping!)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Hooray for Humanity!&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-763202299204946052?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/763202299204946052'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/763202299204946052'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2010/02/specialization.html' title='Specialization'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-7762217279198189179</id><published>2010-01-23T09:57:00.003-06:00</published><updated>2010-01-23T10:11:55.093-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Food'/><category scheme='http://www.blogger.com/atom/ns#' term='bread'/><title type='text'>Adventures in Bread-Baking: Part 1</title><content type='html'>Last weekend I attempted to make Mark Bittman's &lt;a href="http://www.nytimes.com/2008/10/08/dining/081mrex.html?_r=1&amp;ref=dining"&gt;Speedy No-Knead Bread&lt;/a&gt;... sort of.  Being the adventurous experimenter that I am, I altered the ingredients:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;2 cups whole-grain flour + 1 cup all purpose flour&lt;/li&gt;&lt;li&gt;2 cups of really warm water (instead of the 1.5 cups)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;I followed the rest of the recipe perfectly.  However, the results were really unsatisfactory.  The  center was super dense and gooey, and the bread had a really really hard crust (ok, I also cooked it an extra 30 minutes because of the gooey center).&lt;br /&gt;&lt;br /&gt;&lt;em&gt;I didn't take any pictures, because the results were so embarrassing.&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;Here's what I learned:  &lt;strong&gt;Bread Flour != All Purpose Flour&lt;/strong&gt; (for the non-programmer, that means they are NOT the same thing!)&lt;br /&gt;&lt;br /&gt;Fortunately, there are &lt;a href="http://www.ehow.com/how_2316531_make-bread-flour.html"&gt;several&lt;/a&gt; &lt;a href="http://www.nestle.com/NutritionHealthAndWellness/Cooking/AskGilla/CookingTips/Bread+flour.htm"&gt;articles&lt;/a&gt; &lt;a hrer="http://smittenkitchen.com/tips/how-to-make-your-own-bread-flour/"&gt;online&lt;/a&gt; that expound on the &lt;a href="http://www.ochef.com/97.htm"&gt;differences between bread flour and all-purpose flour&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The secret is &lt;em&gt;wheat gluten&lt;/em&gt; (or &lt;em&gt;gluten additive&lt;/em&gt;).  Essentially, you must add 1tsp to each cup of all-purpose flour to create bread flour.  Unfortunately, this is not something I keep.  So part 2 will have to wait until I go shopping...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-7762217279198189179?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7762217279198189179'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7762217279198189179'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2010/01/adventures-in-bread-baking-part-1.html' title='Adventures in Bread-Baking: Part 1'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-7146046294850823266</id><published>2010-01-15T15:03:00.004-06:00</published><updated>2010-01-15T15:11:19.888-06:00</updated><title type='text'>Add JSON syntax highlighting in Vim on OS X</title><content type='html'>This is how I got Vim to do syntax highlighting for JSON files (Max OS X - Snow Leopard)&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Download the syntax file from &lt;a href="http://www.vim.org/scripts/script.php?script_id=1945"&gt;http://www.vim.org/scripts/script.php?script_id=1945&lt;/a&gt;. (I got &lt;b&gt;json.vim&lt;/b&gt; version 0.4)&lt;/li&gt;&lt;li&gt;If it doesn't already exist, create a &lt;b&gt;.vim/ftplugin&lt;/b&gt; in your home directory.&lt;/li&gt;&lt;li&gt;Put &lt;b&gt;json.vim&lt;/b&gt; into &lt;b&gt;.vim/ftplugin&lt;/b&gt;&lt;/li&gt;&lt;li&gt;Add the following lines in your &lt;b&gt;.vimrc&lt;/b&gt; file (mine is located at ~/.vimrc)&lt;pre&gt;au BufRead,BufNewFile *.json set filetype=json&lt;br /&gt;au! Syntax json source /Users/brad/.vim/ftplugin/json.vim&lt;/pre&gt;Note that you'll have to change the path to your json.vim file so that it works on your computer.&lt;/li&gt;&lt;li&gt;Now, got edit a .json file!&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-7146046294850823266?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7146046294850823266'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7146046294850823266'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2010/01/add-json-syntax-highlighting-in-vim-on.html' title='Add JSON syntax highlighting in Vim on OS X'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-8179727769750930704</id><published>2010-01-14T09:47:00.003-06:00</published><updated>2010-01-14T10:09:07.114-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='iTunes'/><category scheme='http://www.blogger.com/atom/ns#' term='Mac'/><category scheme='http://www.blogger.com/atom/ns#' term='applescript'/><title type='text'>Resize the iTunes Window</title><content type='html'>My Macbook Pro is my main machine. At the office, I connect it to a 20" Cinema display. At home, I connect it to a 24" Samsung Monitory (Model 2494SW Glossy Black, which I really like, btw.)&lt;br /&gt;&lt;br /&gt;There's a down-side to all this, though.  I mostly just notice it with iTunes. When I'm working on the laptop without an external monitor, my iTunes window is larger than my screen :(   Unfortunately, clicking the Maximize  button doesn't do what I want (which is to make the window fit in the screen.  So here's a little Applescript to to fix it:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;tell application "iTunes"&lt;br /&gt; set the bounds of the first window to {50, 50, 1024, 640}&lt;br /&gt;end tell&lt;/pre&gt;&lt;br /&gt;The arguments are the distance from the top and left of the screen (both 50), the width of the iTunes window (1024) followed by its height (640). &lt;br /&gt;&lt;br /&gt;I just saved this in a file called &lt;em&gt;resize_itunes.scpt&lt;/em&gt; and put it in my &lt;em&gt;Documents/Applescripts/&lt;/em&gt; folder. I'm a big fan of &lt;a href="http://www.blacktree.com/projects/quicksilver.html"&gt;Quicksilver&lt;/a&gt;, so when I need to resize my iTunes window, I just activate Quicksilver (Ctrl+Spacebar), type in "resize" (which finds the applescript file), and then hit "enter" to run the script.  Easy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-8179727769750930704?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/8179727769750930704'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/8179727769750930704'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2010/01/resize-itunes-window.html' title='Resize the iTunes Window'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-8164638269643552852</id><published>2009-12-13T14:57:00.003-06:00</published><updated>2009-12-13T15:13:47.264-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hardware'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='audio'/><category scheme='http://www.blogger.com/atom/ns#' term='Ubuntu'/><title type='text'>Ubuntu Audio, YES! Windows XP, NO.</title><content type='html'>I run a dual-boot Ubuntu64&lt;a href="#one"&gt;[1]&lt;/a&gt; and Windows XP system.  The windows partition really exists for a &lt;a href="http://en.wikipedia.org/wiki/Unreal_Tournament_2004"&gt;single purpose&lt;/a&gt;, and I occasionally just reboot the machine, choose the XP partition from the Grub menu, and all is well.&lt;br /&gt;&lt;br /&gt;However, a few weeks ago, I upgraded Ubuntu, but when I rebooted the machine and chose the XP partition, I noticed the sound&lt;a href="#two"&gt;[2]&lt;/a&gt; stopped working. What!?  The audio worked fine in ubuntu!&lt;br /&gt;&lt;br /&gt;The Secret: &lt;b&gt;I had to &lt;em&gt;completely power down the machine&lt;/em&gt; before booting into Windows.&lt;/b&gt;  Then, audio works just fine.  &lt;br /&gt;&lt;br /&gt;Unfortunately, I didn't discover this until I'd re-installed the windows drivers (which didn't work), reformatted/reinstalled windows and all drivers (which only worked because I powered down in the process), &lt;a href="http://www.howtogeek.com/howto/ubuntu/reinstall-ubuntu-grub-bootloader-after-windows-wipes-it-out/"&gt;fixed my grub boot loader&lt;/a&gt;, and incorrectly assumed the problem was fixed until I booted back into Ubuntu and back into Windows only to find that the problem remained.&lt;br /&gt;&lt;br /&gt;Sheesh. So, if this has happened to you, I'm sure you've found the myriad article on how to get sound working in Linux... which for me, has always worked just fine.&lt;br /&gt;&lt;br /&gt;&lt;a name="one"&gt;[1]&lt;/a&gt; Output of &lt;b&gt;uname -a&lt;/b&gt;:&lt;pre&gt;Linux homeserver 2.6.31-16-generic #53-Ubuntu SMP Tue Dec 8 04:02:15 UTC 2009 x86_64 GNU/Linux&lt;/pre&gt;&lt;a name="two"&gt;[2]&lt;/a&gt; A Soundblaster Audigy LS. Output of &lt;b&gt;lspci&lt;/b&gt;:&lt;pre&gt;02:02.0 Multimedia audio controller: Creative Labs CA0106 Soundblaster&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-8164638269643552852?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/8164638269643552852'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/8164638269643552852'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2009/12/ubuntu-audio-yes-windows-xp-no.html' title='Ubuntu Audio, YES! Windows XP, NO.'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-6881096023935249692</id><published>2009-11-24T13:47:00.005-06:00</published><updated>2010-03-22T16:14:59.618-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='virtualenv'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='cx_oracle'/><category scheme='http://www.blogger.com/atom/ns#' term='apache'/><title type='text'>Gahhh!! Django, virtualenv, and cx_Oracle</title><content type='html'>&lt;ins&gt;UPDATE:&lt;/ins&gt; Thanks in advance to the comments from &lt;a href="http://blog.dscpl.com.au/"&gt;Graham Dumpleton&lt;/a&gt; whose comments below pointed me in the right direction!&lt;br /&gt;&lt;br /&gt;This &lt;ins&gt;was&lt;/ins&gt;&lt;del&gt;is&lt;/del&gt; a plea for help.&lt;br /&gt;&lt;br /&gt;I've got django installed and configured with apache and virtualenv. I also have one particular app (named &lt;b&gt;myapp&lt;/b&gt;) that queries an Oracle database directly (django is configured to use MySQL). All of the apps work, &lt;strong&gt;except&lt;/strong&gt; for anything thatbrequires the &lt;b&gt;myapp&lt;/b&gt; app... which includes the admin!&lt;br /&gt;&lt;br /&gt;Requesting any view that uses cx_Oracle results in a cryptic error simiar to:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;ViewDoesNotExist at /some/url/&lt;/strong&gt;&lt;br /&gt;&lt;em&gt;Could not import myproject.myapp.views. Error was: libclntsh.so.10.1: cannot open shared object file: No such file or directory&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;The full traceback follows:&lt;pre&gt;Environment:&lt;br /&gt;&lt;br /&gt;Request Method: GET&lt;br /&gt;Request URL: http://mydomain.com/myapp/foo/&lt;br /&gt;Django Version: 1.1.1&lt;br /&gt;Python Version: 2.6.2&lt;br /&gt;Installed Applications:&lt;br /&gt;['django.contrib.auth',&lt;br /&gt; 'django.contrib.contenttypes',&lt;br /&gt; 'django.contrib.sessions',&lt;br /&gt; 'django.contrib.sites',&lt;br /&gt; 'django.contrib.admin',&lt;br /&gt; 'django.contrib.admindocs',&lt;br /&gt; 'django.contrib.flatpages',&lt;br /&gt; 'django_extensions',&lt;br /&gt; 'myproject.userprofile',&lt;br /&gt; &lt;b&gt;'myproject.myapp',&lt;/b&gt;&lt;br /&gt;&lt;br /&gt; ... &lt;em&gt;snip&lt;/em&gt;...&lt;br /&gt;&lt;br /&gt; 'myproject.utils']&lt;br /&gt;Installed Middleware:&lt;br /&gt;('django.middleware.gzip.GZipMiddleware',&lt;br /&gt; 'django.middleware.common.CommonMiddleware',&lt;br /&gt; 'django.contrib.sessions.middleware.SessionMiddleware',&lt;br /&gt; 'django.contrib.auth.middleware.AuthenticationMiddleware',&lt;br /&gt; 'django.middleware.doc.XViewMiddleware')&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Traceback:&lt;br /&gt;File "/home/django/.virtualenvs/myproject/lib/python2.6/site-packages/django/core/handlers/base.py" in get_response&lt;br /&gt;  83.                     request.path_info)&lt;br /&gt;File "/home/django/.virtualenvs/myproject/lib/python2.6/site-packages/django/core/urlresolvers.py" in resolve&lt;br /&gt;  218.                     sub_match = pattern.resolve(new_path)&lt;br /&gt;File "/home/django/.virtualenvs/myproject/lib/python2.6/site-packages/django/core/urlresolvers.py" in resolve&lt;br /&gt;  218.                     sub_match = pattern.resolve(new_path)&lt;br /&gt;File "/home/django/.virtualenvs/myproject/lib/python2.6/site-packages/django/core/urlresolvers.py" in resolve&lt;br /&gt;  125.             return self.callback, args, kwargs&lt;br /&gt;File "/home/django/.virtualenvs/myproject/lib/python2.6/site-packages/django/core/urlresolvers.py" in _get_callback&lt;br /&gt;  134.             raise ViewDoesNotExist, "Could not import %s. Error was: %s" % (mod_name, str(e))&lt;br /&gt;&lt;br /&gt;Exception Type: ViewDoesNotExist at /myapp/foo/&lt;br /&gt;Exception Value: Could not import myproject.myapp.views. Error was: libclntsh.so.10.1: cannot open shared object file: No such file or directory&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The strange thing is that tests using cx_Oracle run correctly (as does code typed into the interpreter via ./manage.py shell), so cx_Oracle is installed correctly in the virtualenv.  Orignally, I thought that apache was somehow misconfigured. The problem, however, is that Apache just doesn't know where to find the shared libraries for cx_Oracle's C extension modules. On my system (Ubuntu), shared libraries are located in /usr/lib and /usr/local/lib .&lt;br /&gt;&lt;br /&gt;When building cx_Oracle, you must include an environment variable for ORACLE_HOME and update the LD_LIBRARY_PATH so that it also includes ORACLE_HOME. While logged in as the &lt;em&gt;django&lt;/em&gt; user, I updated .bashrc so that it includes the following:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020"&gt;export &lt;/span&gt;&lt;span style="color: #bb60d5"&gt;ORACLE_HOME&lt;/span&gt;&lt;span style="color: #666666"&gt;=&lt;/span&gt;/home/django/oracle/instantclient_10_2&lt;br /&gt;&lt;span style="color: #007020"&gt;export &lt;/span&gt;&lt;span style="color: #bb60d5"&gt;LD_LIBRARY_PATH&lt;/span&gt;&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #bb60d5"&gt;$ORACLE_HOME&lt;/span&gt;:$LD_LIBRARY_PATH&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;For me, all of the cx_Oracle shared libraries are located in &lt;b&gt;/home/django/oracle/instantclient_10_2&lt;/b&gt;. So, (with my virtualenv activated) I built, installed, and tested cx_Oracle, and everything worked fine, because my user environment was set up so that these shared libraries are available.&lt;br /&gt;&lt;br /&gt;To make these available to Apache, I created &lt;b&gt;hard&lt;/b&gt; links to all of the shared libraries  from /usr/local/lib, and then ran ldconfig. Note that ldconfig ignores symbolic links!&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020"&gt;cd&lt;/span&gt; /usr/local/lib&lt;br /&gt;sudo ln /home/django/oracle/instantclient_10_2/libclntsh.so.10.1&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;# also creating links to all other libs&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;&lt;/span&gt;sudo ldconfig&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;After restarting Apache, my django apps worked as expected.  For the curious, my wsgi script and apache config follows.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;My .wsgi script&lt;/b&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;sys&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;site&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;os&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;vepath &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;#39;/home/django/.virtualenvs/myproject/lib/python2.6/site-packages&amp;#39;&lt;/span&gt;&lt;br /&gt;prev_sys_path &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #007020"&gt;list&lt;/span&gt;(sys&lt;span style="color: #666666"&gt;.&lt;/span&gt;path)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;# add the site-packages of our virtualenv as a site dir&lt;/span&gt;&lt;br /&gt;site&lt;span style="color: #666666"&gt;.&lt;/span&gt;addsitedir(vepath)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;# add the app&amp;#39;s directory to the PYTHONPATH&lt;/span&gt;&lt;br /&gt;sys&lt;span style="color: #666666"&gt;.&lt;/span&gt;path&lt;span style="color: #666666"&gt;.&lt;/span&gt;append(&lt;span style="color: #4070a0"&gt;&amp;#39;/home/django/django_projects/myproject_root/myproject/&amp;#39;&lt;/span&gt;)&lt;br /&gt;sys&lt;span style="color: #666666"&gt;.&lt;/span&gt;path&lt;span style="color: #666666"&gt;.&lt;/span&gt;append(&lt;span style="color: #4070a0"&gt;&amp;#39;/home/django/django_projects/myproject_root/&amp;#39;&lt;/span&gt;)&lt;br /&gt;os&lt;span style="color: #666666"&gt;.&lt;/span&gt;environ[&lt;span style="color: #4070a0"&gt;&amp;#39;PYTHON_EGG_CACHE&amp;#39;&lt;/span&gt;] &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;#39;/home/django/.python-eggs&amp;#39;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;new_sys_path &lt;span style="color: #666666"&gt;=&lt;/span&gt; [p &lt;span style="color: #007020; font-weight: bold"&gt;for&lt;/span&gt; p &lt;span style="color: #007020; font-weight: bold"&gt;in&lt;/span&gt; sys&lt;span style="color: #666666"&gt;.&lt;/span&gt;path &lt;span style="color: #007020; font-weight: bold"&gt;if&lt;/span&gt; p &lt;span style="color: #007020; font-weight: bold"&gt;not&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;in&lt;/span&gt; prev_sys_path]&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;for&lt;/span&gt; item &lt;span style="color: #007020; font-weight: bold"&gt;in&lt;/span&gt; new_sys_path: &lt;br /&gt;    sys&lt;span style="color: #666666"&gt;.&lt;/span&gt;path&lt;span style="color: #666666"&gt;.&lt;/span&gt;remove(item)&lt;br /&gt;sys&lt;span style="color: #666666"&gt;.&lt;/span&gt;path[:&lt;span style="color: #40a070"&gt;0&lt;/span&gt;] &lt;span style="color: #666666"&gt;=&lt;/span&gt; new_sys_path&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;django.core.handlers.wsgi&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; WSGIHandler&lt;br /&gt;os&lt;span style="color: #666666"&gt;.&lt;/span&gt;environ[&lt;span style="color: #4070a0"&gt;&amp;#39;DJANGO_SETTINGS_MODULE&amp;#39;&lt;/span&gt;] &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;#39;myproject.settings&amp;#39;&lt;/span&gt;&lt;br /&gt;application &lt;span style="color: #666666"&gt;=&lt;/span&gt; WSGIHandler()&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Pertinent part of the Apache Config, located within a VirtualHost Directive:&lt;/b&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020"&gt;Alias&lt;/span&gt; &lt;span style="color: #c65d09"&gt;/media/&lt;/span&gt; &lt;span style="color: #c65d09"&gt;/var/www/media/&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #007020"&gt;Alias&lt;/span&gt; &lt;span style="color: #c65d09"&gt;/site_media/&lt;/span&gt; &lt;span style="color: #c65d09"&gt;/var/www/static/&lt;/span&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;# Run WSGI in Daemon Mode:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #007020"&gt;WSGIDaemonProcess&lt;/span&gt; myproject &lt;span style="color: #007020; font-weight: bold"&gt;user&lt;/span&gt;=www-data &lt;span style="color: #007020; font-weight: bold"&gt;group&lt;/span&gt;=www-data threads=25&lt;br /&gt;&lt;span style="color: #007020"&gt;WSGIProcessGroup&lt;/span&gt; myproject&lt;br /&gt;&lt;span style="color: #007020"&gt;WSGIScriptAlias&lt;/span&gt; / &lt;span style="color: #c65d09"&gt;/home/django/django_projects/myproject_root/apache_mod_wsgi_conf/myproject.wsgi&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;NOTE: Graham Dumpleton suggests four options to get around the original problem (and I paraphrase):&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;em&gt;Rebuild cx_Oracle setting the LD_RUN_PATH&lt;/em&gt; - I tried this, but was unnsuccessful.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;em&gt;Put the shared libraries in /usr/lib&lt;/em&gt; - I hard linked to the libraries from /usr/local/lib.  This is the solution that worked.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;em&gt;Modify /etc/ld.conf so that it includes the path to the shared libraries&lt;/em&gt; - I considered adding a file to /etc/ld.so.conf.d/ which would have included the path to my ORACLE_HOME directory.  However, the above options worked, so I went with that instead.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;em&gt;Edit /etc/apache/envvars so that it includes the appropriate environment variables&lt;/em&gt; - I also tried this approach, including the same environment variables that I added to my .bashrc, but this also did not work&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;Hope this helps someone else!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-6881096023935249692?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/6881096023935249692'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/6881096023935249692'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2009/11/gahhh-django-virtualenv-and-cxoracle.html' title='Gahhh!! Django, virtualenv, and cx_Oracle'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-7915480253587113591</id><published>2009-11-24T12:07:00.003-06:00</published><updated>2009-11-24T12:19:04.875-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Sticky Groups</title><content type='html'>I often deploy web projects in a directory that's not &lt;em&gt;owned&lt;/em&gt; by the user under which my webserver runs.  Therefor, I often have to change permissions so the webserver can read from or write to certain files. So, for this example, let's assume I'm logged in to my linux box as &lt;em&gt;brad&lt;/em&gt;, and I'm using apache which runs under the user &lt;em&gt;www-data&lt;/em&gt;. &lt;br /&gt;To give apache access to my public_html directory, I'd change ownership for the directory and all of its contents:&lt;pre&gt;chown -R brad:www-data public_html&lt;/pre&gt;&lt;br /&gt;I may also need to allow apache to write to a certain directory:&lt;pre&gt;chmod g+w public_html/somedir&lt;/pre&gt;&lt;br /&gt;Now, if I modify any files in &lt;em&gt;public_html&lt;/em&gt;, I don't want to have to change the group permissions (that is, apache's permissions), so lets make the group ownership &lt;strong&gt;sticky&lt;/strong&gt;:&lt;br /&gt;&lt;pre&gt;chmod g+s public_html&lt;/pre&gt;&lt;br /&gt;Now, any new files that I add beneath the &lt;em&gt;public_html&lt;/em&gt; directory will be part of the &lt;em&gt;www-data&lt;/em&gt; group.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-7915480253587113591?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7915480253587113591'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7915480253587113591'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2009/11/sticky-groups.html' title='Sticky Groups'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-3322153686297622685</id><published>2009-09-30T12:15:00.006-05:00</published><updated>2009-09-30T12:29:11.209-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mercurial'/><category scheme='http://www.blogger.com/atom/ns#' term='OS X'/><category scheme='http://www.blogger.com/atom/ns#' term='Mac'/><category scheme='http://www.blogger.com/atom/ns#' term='hg'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Mercurial installation woes on Mac OS X</title><content type='html'>I started using mercurial around version 1.2, and I'm pretty sure I used the Mac OS X installer (from &lt;a href="http://mercurial.berkwood.com/"&gt;http://mercurial.berkwood.com/&lt;/a&gt;) to install 1.2.1.  This placed hg in &lt;span style="font-family: courier"&gt;/Library/Frameworks/Python.framework/Versions/Current/bin/&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Now, I've decided to upgrade to 1.3.1, and I again grab the Mac OS X installer (again from &lt;a href="http://mercurial.berkwood.com/"&gt;http://mercurial.berkwood.com/&lt;/a&gt;), which installs hg in &lt;span style="font-family: courier"&gt;/usr/local/bin/&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Ok, but my path is set up so that the older version is used by default... Is there a preferred way to remove the older version of mercurial?&lt;br /&gt;&lt;br /&gt;I just renamed the file: &lt;span style="font-family: courier"&gt;/Library/Frameworks/Python.framework/Versions/Current/bin/hg&lt;/span&gt; to hg-1.2.1, and source'd my .profile so that the new version (&lt;span style="font-family: courier"&gt;/usr/local/bin/hg&lt;/span&gt;) gets used instead.&lt;br /&gt;&lt;br /&gt;This does not seem like a good way to upgrade.  :(&lt;br /&gt;&lt;br /&gt;&lt;em&gt;this is all relevant to Mac OS X 10.5.8&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-3322153686297622685?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/3322153686297622685'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/3322153686297622685'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2009/09/mercurial-installation-woes-on-mac-os-x.html' title='Mercurial installation woes on Mac OS X'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-5043486322654878551</id><published>2009-07-14T11:42:00.008-05:00</published><updated>2009-07-14T12:37:45.825-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='Prototype'/><title type='text'>A for AJAX - OR - Dynamically generating options for a select element.</title><content type='html'>I don't do a lot of AJAXy web development, but when I do, I usually make use of &lt;a href="http://prototypejs.org/"&gt;Prototype&lt;/a&gt;.  I've recently created a form containing a &lt;em&gt;&amp;lt;select&amp;gt;&lt;/em&gt; element whose &lt;em&gt;&amp;lt;option&amp;gt;&lt;/em&gt;s are dynamically generated via an AJAX request.  The problem however, is that a &lt;em&gt;&lt;strong&gt;selected&lt;/strong&gt; option&lt;/em&gt; was already in the form.  So before the AJAX request, my HTML looked something like this:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;select&lt;/span&gt; &lt;span style="color: #4070a0"&gt;name=&amp;quot;s&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;id=&amp;quot;s&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;option&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;val1&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;Value 1&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;option&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;val2&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;selected=&amp;quot;selected&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;Value 2&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;option&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;val3&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;Value 3&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;We can then use &lt;a href="http://prototypejs.org/api/ajax/request"&gt;Ajax.Request&lt;/a&gt; method to dynamically add items into this list. (Note here that &lt;em&gt;http://example.com/dynamic/options/&lt;/em&gt; would be a server-side script giving us our dynamically generated &lt;em&gt;&amp;lt;option&amp;gt;&lt;/em&gt; tags.)  &lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;new&lt;/span&gt; &lt;span style="color: #666666"&gt;A&lt;/span&gt;jax.&lt;span style="color: #666666"&gt;R&lt;/span&gt;equest(&lt;span style="color: #4070a0"&gt;&amp;#39;http://example.com/dynamic/options/&amp;#39;&lt;/span&gt;&lt;span style="color: #666666"&gt;,&lt;/span&gt; {&lt;br /&gt;    method&lt;span style="color: #666666"&gt;:&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;#39;post&amp;#39;&lt;/span&gt;&lt;span style="color: #666666"&gt;,&lt;/span&gt;&lt;br /&gt;    onSuccess&lt;span style="color: #666666"&gt;:&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;function&lt;/span&gt;(transport) {&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold"&gt;if&lt;/span&gt; (transport.responseText)&lt;br /&gt;            &lt;span style="color: #666666"&gt;E&lt;/span&gt;lement.insert($(&lt;span style="color: #4070a0"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;)&lt;span style="color: #666666"&gt;,&lt;/span&gt; transport.responseText);&lt;br /&gt;    }&lt;br /&gt;}); &lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Our resulting HTML might look something like the following:&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;select&lt;/span&gt; &lt;span style="color: #4070a0"&gt;name=&amp;quot;s&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;id=&amp;quot;s&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;option&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;val1&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;Value 1&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;option&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;val2&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;selected=&amp;quot;selected&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;Value 2&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;option&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;val3&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;Value 3&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;option&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;val4&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;Value 4&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;option&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;val5&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;Value 5&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;option&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;val6&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;Value 6&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;One problem that arises, is that once the above &lt;em&gt;&amp;lt;option&amp;gt;s&lt;/em&gt; are inserted into the original &lt;em&gt;&amp;lt;select&amp;gt;&lt;/em&gt; element, the originally selected option is no longer selected. (In my experiments using Firefox 3.5 on OS X, the last item &amp;mdash; in this case, &lt;em&gt;Value 6&lt;/em&gt;&amp;mdash; becomes selected).&lt;br /&gt;&lt;br /&gt;That's not so bad, though, because a little more code will just set the correct &amp;lt;option&amp;gt; as selected.  The following code looks at all the options until it finds one with the attribute: &lt;em&gt;selected="selected"&lt;/em&gt;, and then ... selects it!&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;var&lt;/span&gt; selected_index &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #40a070"&gt;0&lt;/span&gt;&lt;span style="color: #666666"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;while&lt;/span&gt; (&lt;span style="color: #666666"&gt;!&lt;/span&gt;$(&lt;span style="color: #4070a0"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;).down(selected_index).defaultSelected) {&lt;br /&gt;    selected_index &lt;span style="color: #666666"&gt;+=&lt;/span&gt; &lt;span style="color: #40a070"&gt;1&lt;/span&gt;&lt;span style="color: #666666"&gt;;&lt;/span&gt;&lt;br /&gt;}               &lt;br /&gt;$(&lt;span style="color: #4070a0"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;).selectedIndex &lt;span style="color: #666666"&gt;=&lt;/span&gt; selected_index&lt;span style="color: #666666"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;However...&lt;/strong&gt; one must be careful where one puts this code!  You &lt;strong&gt;must&lt;/strong&gt; take into account that the Ajax Request is &lt;strong&gt;asynchronous&lt;/strong&gt; (that's the &lt;em&gt;A&lt;/em&gt; in AJAX)!  &lt;br /&gt;&lt;br /&gt;Here's what NOT to do:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;function&lt;/span&gt; append_options() {&lt;br /&gt;    &lt;span style="color: #60a0b0; font-style: italic"&gt;// Request the dynamically generated options&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;&lt;/span&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;new&lt;/span&gt; &lt;span style="color: #666666"&gt;A&lt;/span&gt;jax.&lt;span style="color: #666666"&gt;R&lt;/span&gt;equest(&lt;span style="color: #4070a0"&gt;&amp;#39;http://example.com/dynamic/options/&amp;#39;&lt;/span&gt;&lt;span style="color: #666666"&gt;,&lt;/span&gt; {&lt;br /&gt;        method&lt;span style="color: #666666"&gt;:&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;#39;post&amp;#39;&lt;/span&gt;&lt;span style="color: #666666"&gt;,&lt;/span&gt;&lt;br /&gt;        onSuccess&lt;span style="color: #666666"&gt;:&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;function&lt;/span&gt;(transport) {&lt;br /&gt;            &lt;span style="color: #007020; font-weight: bold"&gt;if&lt;/span&gt; (transport.responseText)&lt;br /&gt;                &lt;span style="color: #666666"&gt;E&lt;/span&gt;lement.insert($(&lt;span style="color: #4070a0"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;)&lt;span style="color: #666666"&gt;,&lt;/span&gt; transport.responseText);&lt;br /&gt;        }   &lt;br /&gt;    }); &lt;br /&gt;    &lt;span style="color: #60a0b0; font-style: italic"&gt;// Make sure the correct on is selected.    var selected_index = 0;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;&lt;/span&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;while&lt;/span&gt; (&lt;span style="color: #666666"&gt;!&lt;/span&gt;$(&lt;span style="color: #4070a0"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;).down(selected_index).defaultSelected) {&lt;br /&gt;        selected_index &lt;span style="color: #666666"&gt;+=&lt;/span&gt; &lt;span style="color: #40a070"&gt;1&lt;/span&gt;&lt;span style="color: #666666"&gt;;&lt;/span&gt;&lt;br /&gt;    }&lt;br /&gt;    $(&lt;span style="color: #4070a0"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;).selectedIndex &lt;span style="color: #666666"&gt;=&lt;/span&gt; selected_index&lt;span style="color: #666666"&gt;;&lt;/span&gt;&lt;br /&gt;}&lt;br /&gt;&lt;span style="color: #007020"&gt;document&lt;/span&gt;.observe(&lt;span style="color: #4070a0"&gt;&amp;#39;dom:loaded&amp;#39;&lt;/span&gt;&lt;span style="color: #666666"&gt;,&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;function&lt;/span&gt;() {&lt;br /&gt;    append_options();&lt;br /&gt;});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The Ajax request is sent, but the portion of the code that selects the option containing selected="selected" may get run &lt;strong&gt;before&lt;/strong&gt; there is a response to that Request.  Therefore, the selected option gets set, then the dynamically generated options get inserted.  This is not what we want!&lt;br /&gt;&lt;br /&gt;The proper way to do this is to use the &lt;em&gt;onComplete&lt;/em&gt; callback in Ajax.Request.  This will ensure that the code to select the appropriate option is run &lt;strong&gt;after&lt;/strong&gt; the Request is completed. &lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;function&lt;/span&gt; append_options() {&lt;br /&gt;    &lt;span style="color: #60a0b0; font-style: italic"&gt;// Request the dynamically generated options&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;&lt;/span&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;new&lt;/span&gt; &lt;span style="color: #666666"&gt;A&lt;/span&gt;jax.&lt;span style="color: #666666"&gt;R&lt;/span&gt;equest(&lt;span style="color: #4070a0"&gt;&amp;#39;http://example.com/dynamic/options/&amp;#39;&lt;/span&gt;&lt;span style="color: #666666"&gt;,&lt;/span&gt; {&lt;br /&gt;        method&lt;span style="color: #666666"&gt;:&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;#39;post&amp;#39;&lt;/span&gt;&lt;span style="color: #666666"&gt;,&lt;/span&gt;&lt;br /&gt;        onSuccess&lt;span style="color: #666666"&gt;:&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;function&lt;/span&gt;(transport) {&lt;br /&gt;            &lt;span style="color: #007020; font-weight: bold"&gt;if&lt;/span&gt; (transport.responseText)&lt;br /&gt;                &lt;span style="color: #666666"&gt;E&lt;/span&gt;lement.insert($(&lt;span style="color: #4070a0"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;)&lt;span style="color: #666666"&gt;,&lt;/span&gt; transport.responseText);&lt;br /&gt;        }&lt;span style="color: #666666"&gt;,&lt;/span&gt;  &lt;br /&gt;        onComplete&lt;span style="color: #666666"&gt;:&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;function&lt;/span&gt;(transport) {&lt;br /&gt;            &lt;span style="color: #60a0b0; font-style: italic"&gt;// Make sure the correct on is selected.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;&lt;/span&gt;            &lt;span style="color: #007020; font-weight: bold"&gt;var&lt;/span&gt; selected_index &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #40a070"&gt;0&lt;/span&gt;&lt;span style="color: #666666"&gt;;&lt;/span&gt;&lt;br /&gt;            &lt;span style="color: #007020; font-weight: bold"&gt;while&lt;/span&gt; (&lt;span style="color: #666666"&gt;!&lt;/span&gt;$(&lt;span style="color: #4070a0"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;).down(selected_index).defaultSelected) {&lt;br /&gt;                selected_index &lt;span style="color: #666666"&gt;+=&lt;/span&gt; &lt;span style="color: #40a070"&gt;1&lt;/span&gt;&lt;span style="color: #666666"&gt;;&lt;/span&gt;&lt;br /&gt;            }&lt;br /&gt;            $(&lt;span style="color: #4070a0"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;).selectedIndex &lt;span style="color: #666666"&gt;=&lt;/span&gt; selected_index&lt;span style="color: #666666"&gt;;&lt;/span&gt;&lt;br /&gt;        }&lt;br /&gt;    }); &lt;br /&gt;}&lt;br /&gt;&lt;span style="color: #007020"&gt;document&lt;/span&gt;.observe(&lt;span style="color: #4070a0"&gt;&amp;#39;dom:loaded&amp;#39;&lt;/span&gt;&lt;span style="color: #666666"&gt;,&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;function&lt;/span&gt;() {&lt;br /&gt;    append_options();&lt;br /&gt;});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;This is fairly basic AJAX behavior, but for those of us who don't live, breathe, eat, and sleep AJAX, this is an easy mistake to make.&lt;br /&gt;&lt;br /&gt;So the moral of this story?  Know your tools!  It's always good to &lt;em&gt;Know what's going on!&lt;/em&gt;&amp;trade;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-5043486322654878551?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/5043486322654878551'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/5043486322654878551'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2009/07/for-ajax-or-dynamically-generating.html' title='A for AJAX - OR - Dynamically generating options for a select element.'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-3810087032644746773</id><published>2009-06-30T14:23:00.003-05:00</published><updated>2009-06-30T14:31:58.178-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>My PYTHONPATH bit me.</title><content type='html'>I'd just finished the first version of a new django app (&lt;em&gt;myapp&lt;/em&gt;), and so I pushed it out to my development server.  All the new code was in place, so I ran &lt;em&gt;python manage.py syncdb&lt;/em&gt;.  The result?&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Traceback (most recent call last):&lt;br /&gt;  File "manage.py", line 11, in &amp;lt;module&amp;gt;&lt;br /&gt;    execute_manager(settings)&lt;br /&gt;  File "/usr/local/lib/python2.6/site-packages/django/core/management/__init__.py", line 340, in execute_manager&lt;br /&gt;    &lt;br /&gt;  File "/usr/local/lib/python2.6/site-packages/django/core/management/__init__.py", line 295, in execute&lt;br /&gt;    &lt;br /&gt;  File "/usr/local/lib/python2.6/site-packages/django/core/management/base.py", line 192, in run_from_argv&lt;br /&gt;  File "/usr/local/lib/python2.6/site-packages/django/core/management/base.py", line 210, in execute&lt;br /&gt;  File "/usr/local/lib/python2.6/site-packages/django/utils/translation/__init__.py", line 73, in activate&lt;br /&gt;    &lt;br /&gt;  File "/usr/local/lib/python2.6/site-packages/django/utils/translation/__init__.py", line 43, in delayed_loader&lt;br /&gt;    &lt;br /&gt;  File "/usr/local/lib/python2.6/site-packages/django/utils/translation/trans_real.py", line 209, in activate&lt;br /&gt;  File "/usr/local/lib/python2.6/site-packages/django/utils/translation/trans_real.py", line 198, in translation&lt;br /&gt;  File "/usr/local/lib/python2.6/site-packages/django/utils/translation/trans_real.py", line 181, in _fetch&lt;br /&gt;AttributeError: 'module' object has no attribute 'myapp'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;What!?&lt;/strong&gt;  I had all the correct files in place, and &lt;em&gt;myapp&lt;/em&gt; was listed in my settings' INSTALLED_APPS.  I was confused.    As it turns out, my PYTHONPATH pointed to my production code (which is actually on the same system, configured for a separate virtual host).  So no, it couldn't find my new app because my path said to look in my production code.... :(&lt;br /&gt;&lt;br /&gt;So, I just temporarily reset my PYTHONPATH, ran my &lt;em&gt;python manage.py syncdb&lt;/em&gt;, and I'm rolling for now...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-3810087032644746773?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/3810087032644746773'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/3810087032644746773'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2009/06/my-pythonpath-bit-me.html' title='My PYTHONPATH bit me.'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-102694916469535041</id><published>2009-06-26T09:47:00.003-05:00</published><updated>2009-06-26T10:26:04.278-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>"Adding" Q objects in Django</title><content type='html'>I've got a Django app with the following Model:&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;Story&lt;/span&gt;(models&lt;span style="color: #666666"&gt;.&lt;/span&gt;Model):&lt;br /&gt;    title &lt;span style="color: #666666"&gt;=&lt;/span&gt; models&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;CharField&lt;/span&gt;(max_length&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #40a070"&gt;255&lt;/span&gt;)&lt;br /&gt;    content &lt;span style="color: #666666"&gt;=&lt;/span&gt; models&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;TextField&lt;/span&gt;()&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The Problem:  &lt;/span&gt;&lt;br /&gt;I wanted to build a simple search feature that OR'ed all the search terms.  Essentially, I wanted SQL resembling the following:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;SELECT&lt;/span&gt; &lt;span style="color: #666666"&gt;*&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; myapp_stories &lt;span style="color: #007020; font-weight: bold"&gt;where&lt;/span&gt; &lt;br /&gt;    title &lt;span style="color: #007020; font-weight: bold"&gt;LIKE&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;#39;%term1%&amp;#39;&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;OR&lt;/span&gt; content &lt;span style="color: #007020; font-weight: bold"&gt;LIKE&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;#39;%term1%&amp;#39;&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;OR&lt;/span&gt; &lt;br /&gt;    title &lt;span style="color: #007020; font-weight: bold"&gt;LIKE&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;#39;%term2%&amp;#39;&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;OR&lt;/span&gt; content &lt;span style="color: #007020; font-weight: bold"&gt;LIKE&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;#39;%term2%&amp;#39;&lt;/span&gt;; &lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The Solution: &lt;/span&gt;&lt;br /&gt;You can &lt;em&gt;add&lt;/em&gt; django's Q objects together!  This is a feature not currently discussed in the docs, but I dug through the source code and I discovered that a Q object is really just a node in a Tree! More specifically, Q is a subclass of django.utils.tree.Node (check it out, it's cool!)  A Node has a attribute called a &lt;em&gt;connector&lt;/em&gt;.  Q objects have two possible connectors: &lt;em&gt;AND&lt;/em&gt; and &lt;em&gt;OR&lt;/em&gt;.  But how do we connect Q objects?  Well, a Node has a handy &lt;em&gt;add(node, conn_type)&lt;/em&gt; method whose parameters include another Node and a connection type.&lt;br /&gt;&lt;br /&gt;As previously mentioned, the possible connection types for Q objects are &lt;em&gt;AND&lt;/em&gt; and &lt;em&gt;OR&lt;/em&gt;, so Q objects can be added together by doing something like this:&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;# ANDing Q objects&lt;/span&gt;&lt;br /&gt;q_object &lt;span style="color: #666666"&gt;=&lt;/span&gt; Q()&lt;br /&gt;q_object&lt;span style="color: #666666"&gt;.&lt;/span&gt;add(Q(), Q&lt;span style="color: #666666"&gt;.&lt;/span&gt;AND)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;# ORing Q objects&lt;/span&gt;&lt;br /&gt;q_object &lt;span style="color: #666666"&gt;=&lt;/span&gt; Q()&lt;br /&gt;q_object&lt;span style="color: #666666"&gt;.&lt;/span&gt;add(Q(), Q&lt;span style="color: #666666"&gt;.&lt;/span&gt;OR)&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;So, the solution to my Search view is as follows:&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;django.db.models&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; Q&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;models&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; Story&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;search&lt;/span&gt;(request):&lt;br /&gt;    &lt;span style="color: #4070a0; font-style: italic"&gt;&amp;#39;&amp;#39;&amp;#39; &lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    Generic Search: GET should contain the following: &lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    terms - the search keywords separated by spaces&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    &amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;br /&gt;    terms &lt;span style="color: #666666"&gt;=&lt;/span&gt; request&lt;span style="color: #666666"&gt;.&lt;/span&gt;GET&lt;span style="color: #666666"&gt;.&lt;/span&gt;get(&lt;span style="color: #4070a0"&gt;&amp;#39;terms&amp;#39;&lt;/span&gt;, &lt;span style="color: #007020"&gt;None&lt;/span&gt;)&lt;br /&gt;    term_list &lt;span style="color: #666666"&gt;=&lt;/span&gt; terms&lt;span style="color: #666666"&gt;.&lt;/span&gt;split(&lt;span style="color: #4070a0"&gt;&amp;#39; &amp;#39;&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;    stories &lt;span style="color: #666666"&gt;=&lt;/span&gt; Story&lt;span style="color: #666666"&gt;.&lt;/span&gt;objects&lt;span style="color: #666666"&gt;.&lt;/span&gt;all()&lt;br /&gt;&lt;br /&gt;    q &lt;span style="color: #666666"&gt;=&lt;/span&gt; Q(content__icontains&lt;span style="color: #666666"&gt;=&lt;/span&gt;term_list[&lt;span style="color: #40a070"&gt;0&lt;/span&gt;]) &lt;span style="color: #666666"&gt;|&lt;/span&gt; Q(title__icontains&lt;span style="color: #666666"&gt;=&lt;/span&gt;term_list[&lt;span style="color: #40a070"&gt;0&lt;/span&gt;])&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;for&lt;/span&gt; term &lt;span style="color: #007020; font-weight: bold"&gt;in&lt;/span&gt; term_list[&lt;span style="color: #40a070"&gt;1&lt;/span&gt;:]:&lt;br /&gt;        q&lt;span style="color: #666666"&gt;.&lt;/span&gt;add((Q(content__icontains&lt;span style="color: #666666"&gt;=&lt;/span&gt;term) &lt;span style="color: #666666"&gt;|&lt;/span&gt; Q(title__icontains&lt;span style="color: #666666"&gt;=&lt;/span&gt;term)), q&lt;span style="color: #666666"&gt;.&lt;/span&gt;connector)&lt;br /&gt;&lt;br /&gt;    stories &lt;span style="color: #666666"&gt;=&lt;/span&gt; stories&lt;span style="color: #666666"&gt;.&lt;/span&gt;filter(q)&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;return&lt;/span&gt; render_to_response(&lt;span style="color: #4070a0"&gt;&amp;#39;myapp/search.html&amp;#39;&lt;/span&gt;, &lt;span style="color: #007020"&gt;locals&lt;/span&gt;(), \&lt;br /&gt;            context_instance&lt;span style="color: #666666"&gt;=&lt;/span&gt;RequestContext(request))&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Needless to say, Q objects are quite powerful!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-102694916469535041?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/102694916469535041'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/102694916469535041'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2009/06/adding-q-objects-in-django.html' title='&quot;Adding&quot; Q objects in Django'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-3946931358470469763</id><published>2009-05-06T15:03:00.004-05:00</published><updated>2009-05-06T15:30:27.439-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Data Truncated Errors</title><content type='html'>I recently ran into some of the &lt;b&gt;Data truncated for column ...&lt;/b&gt; errors in my django apps.  After a little digging, I've discovered that my particular problem lie in the structure of the underlying MySQL tables.  Particularly with &lt;em&gt;varchar&lt;/em&gt; columns.  &lt;br /&gt;&lt;br /&gt;I have a model that contains a FileField:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;MyModel&lt;/span&gt;(models&lt;span style="color: #666666"&gt;.&lt;/span&gt;Model):&lt;br /&gt;    &lt;span style="color: #007020"&gt;file&lt;/span&gt; &lt;span style="color: #666666"&gt;=&lt;/span&gt; models&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;FileField&lt;/span&gt;(upload_to&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #4070a0"&gt;&amp;quot;files/%Y/%m/&lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%d&lt;/span&gt;&lt;span style="color: #4070a0"&gt;&amp;quot;&lt;/span&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Note that the MySQL table generated by this model will look something like the following:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;+-------------+--------------+------+-----+---------------------+----------------+&lt;br /&gt;| Field       | Type         | Null | Key | Default             | Extra          |&lt;br /&gt;+-------------+--------------+------+-----+---------------------+----------------+&lt;br /&gt;| id          | int(11)      |      | PRI | NULL                | auto_increment |&lt;br /&gt;| file        | varchar(100) |      |     |                     |                |&lt;br /&gt;+-------------+--------------+------+-----+---------------------+----------------+&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;See the &lt;b&gt;varchar(100)&lt;/b&gt;!  If you're users are uploading files with long names (mine are!), this may not be enough!  Note that my model also has an &lt;em&gt;upload_to&lt;/em&gt; that includes a year, month, and day.  That's already 17 characters...&lt;br /&gt;&lt;br /&gt;So when someone tries to upload a file named:&lt;br /&gt;&lt;pre&gt;Who_Said_One_Hundred_Characters_Would_Ever_Be_Enough_For_A_REALLY_LONG_Filename_draft_one_.docx&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It will get pre-pended with '/files/2009/05/06/', and what gets stored in your table?&lt;br /&gt;&lt;pre&gt;files/2009/05/06/Who_Said_One_Hundred_Characters_Would_Ever_Be_Enough_For_A_REALLY_LONG_Filename_draft_one_.docx&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's 112 characters... and it won't work!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The solution?&lt;/b&gt;&lt;br /&gt;You could alter your table so that the file column is a varchar(255)... which may help.  &lt;br /&gt;&lt;pre&gt;alter table myapp_mymodel modify file varchar(255);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;OR, use shorter filenames!&lt;br /&gt;&lt;br /&gt;Good Luck!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-3946931358470469763?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/3946931358470469763'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/3946931358470469763'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2009/05/data-truncated-errors.html' title='Data Truncated Errors'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-9046149690255549987</id><published>2009-05-01T14:27:00.003-05:00</published><updated>2009-05-01T14:33:25.995-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='apache'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Have Apache Force File Downloads</title><content type='html'>I have a Django app that lets users upload files.  Any kind of file. It's nice that Apache will let me force file downloads based on the files extension.  &lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;LocationMatch&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;quot;\.(gz|tar|pdf|docx|doc|xls|xlsx|bz2|zip)$&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #007020"&gt;SetHandler&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;None&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #007020"&gt;Header&lt;/span&gt; set Content-Disposition attachment&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/LocationMatch&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;So, in my HTML/templates: all I have to do is this:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;a&lt;/span&gt; &lt;span style="color: #4070a0"&gt;href=&amp;quot;SomFile.docx&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;Some File&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-9046149690255549987?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/9046149690255549987'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/9046149690255549987'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2009/05/have-apache-force-file-downloads.html' title='Have Apache Force File Downloads'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-1296451718188870554</id><published>2009-04-30T15:45:00.005-05:00</published><updated>2009-04-30T15:57:53.818-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fabric'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Push Your SSH Public keys using Fabric</title><content type='html'>This came across my twitter radar today from &lt;em&gt;&lt;a href="http://twitter.com/bitprophet/"&gt;@bitprophet&lt;/a&gt;&lt;/em&gt; (aka: Jeff Forcier), who just happens to be the new maintainer for &lt;a href="http://www.nongnu.org/fab/"&gt;Fabric&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;push_key&lt;/span&gt;():&lt;br /&gt;    keyfile &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;#39;/tmp/&lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%s&lt;/span&gt;&lt;span style="color: #4070a0"&gt;.pub&amp;#39;&lt;/span&gt; &lt;span style="color: #666666"&gt;%&lt;/span&gt; env&lt;span style="color: #666666"&gt;.&lt;/span&gt;user&lt;br /&gt;    run(&lt;span style="color: #4070a0"&gt;&amp;#39;mkdir -p ~/.ssh &amp;amp;&amp;amp; chmod 700 ~/.ssh&amp;#39;&lt;/span&gt;)&lt;br /&gt;    put(&lt;span style="color: #4070a0"&gt;&amp;#39;~/.ssh/id_rsa.pub&amp;#39;&lt;/span&gt;, keyfile)&lt;br /&gt;    run(&lt;span style="color: #4070a0"&gt;&amp;#39;cat &lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%s&lt;/span&gt;&lt;span style="color: #4070a0"&gt; &amp;gt;&amp;gt; ~/.ssh/authorized_keys&amp;#39;&lt;/span&gt; &lt;span style="color: #666666"&gt;%&lt;/span&gt; keyfile)&lt;br /&gt;    run(&lt;span style="color: #4070a0"&gt;&amp;#39;rm &lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%s&lt;/span&gt;&lt;span style="color: #4070a0"&gt;&amp;#39;&lt;/span&gt; &lt;span style="color: #666666"&gt;%&lt;/span&gt; keyfile)&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Everything you need to push your public key to an external server using Fabric.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-1296451718188870554?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/1296451718188870554'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/1296451718188870554'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2009/04/push-your-ssh-public-keys-using-fabric.html' title='Push Your SSH Public keys using Fabric'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-1124320250064783173</id><published>2009-04-24T18:36:00.006-05:00</published><updated>2009-04-27T10:36:56.383-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Restricting Access by Group in Django</title><content type='html'>Django's &lt;a href="http://docs.djangoproject.com/en/1.0/topics/auth/"&gt;authentication&lt;/a&gt; system provides built-in support for Groups.  When developing an app, you may want to prevent users in a particular group from accessing part of your app.&lt;br /&gt;&lt;br /&gt;For example, if you were building a tool to be used by &lt;em&gt;Faculty&lt;/em&gt; and &lt;em&gt;Students&lt;/em&gt;, it's quite possible that there would be parts of the app you wouldn't want &lt;em&gt;Students&lt;/em&gt; to access (like the part that allows a User to change grades!)&lt;br /&gt;&lt;br /&gt;Luckily, there's a decorator called &lt;em&gt;&lt;a href="http://docs.djangoproject.com/en/1.0/topics/auth/#limiting-access-to-logged-in-users-that-pass-a-test"&gt;user_passes_test&lt;/a&gt;&lt;/em&gt; that allows you to easily perform this sort of thing.  Let's see an example:&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;django.contrib.auth.decorators&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; login_required, user_passes_test&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #555555; font-weight: bold"&gt;@login_required&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #555555; font-weight: bold"&gt;@user_passes_test&lt;/span&gt;(&lt;span style="color: #007020; font-weight: bold"&gt;lambda&lt;/span&gt; u: u&lt;span style="color: #666666"&gt;.&lt;/span&gt;groups&lt;span style="color: #666666"&gt;.&lt;/span&gt;filter(name&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #4070a0"&gt;&amp;#39;Student&amp;#39;&lt;/span&gt;)&lt;span style="color: #666666"&gt;.&lt;/span&gt;count() &lt;span style="color: #666666"&gt;==&lt;/span&gt; &lt;span style="color: #40a070"&gt;0&lt;/span&gt;, login_url&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #4070a0"&gt;&amp;#39;/myapp/denied/&amp;#39;&lt;/span&gt;)&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;some_view&lt;/span&gt;(request):&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #60a0b0; font-style: italic"&gt;# Do whatever this view should do&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The view above (which lacks any content) actually uses two decorators.  The first &lt;em&gt;login_required&lt;/em&gt; simply requires that a user be logged in.  The second, &lt;em&gt;user_passes_test&lt;/em&gt;, requires a function as the first argument.  This function must accept a User object and return True or False.  If True, the User can view the page.  If False, the user cannot view the page.&lt;br /&gt;&lt;br /&gt;We define this function using a python &lt;a href="http://docs.python.org/reference/expressions.html#lambdas"&gt;Lambda Expression&lt;/a&gt;.  It simply uses the ORM to check if a User is in the &lt;em&gt;Student&lt;/em&gt; group.  In this example, &lt;b&gt;u&lt;/b&gt; would be an instance of django.contrib.auth.models.User.&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;lambda&lt;/span&gt; u: u&lt;span style="color: #666666"&gt;.&lt;/span&gt;groups&lt;span style="color: #666666"&gt;.&lt;/span&gt;filter(name&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #4070a0"&gt;&amp;#39;Student&amp;#39;&lt;/span&gt;)&lt;span style="color: #666666"&gt;.&lt;/span&gt;count() &lt;span style="color: #666666"&gt;==&lt;/span&gt; &lt;span style="color: #40a070"&gt;0&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Additionally, you can specify the keyword argument &lt;em&gt;login_url&lt;/em&gt; to the &lt;em&gt;user_passes_test&lt;/em&gt; decorator.  Normally, if the user failed the test this would redirect them to a login page.  However, in our case, they're already logged in (because of the &lt;em&gt;login_required&lt;/em&gt; decorator), so this just acts as a redirect page.  In this example, it would redirect to a url that we've specified which might contain an Access Denied message (with &lt;a href="http://bradmontgomery.net/accessdenied/"&gt;giant red blinking letters&lt;/a&gt;).  Just kidding.&lt;br /&gt;&lt;br /&gt;UPDATE: There is one slight caveat to this solution so far... it breaks if your user is not already logged in.  In fact, this will result in the following &lt;b&gt;AttributeError&lt;/b&gt;:&lt;pre&gt;'NoneType' object has no attribute '_meta'&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So, to account for that, we need to put the logic that tests for Student-group membership into its own function.  The result would look something like the following:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;not_in_student_group&lt;/span&gt;(user):&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;if&lt;/span&gt; user:&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold"&gt;return&lt;/span&gt; user&lt;span style="color: #666666"&gt;.&lt;/span&gt;groups&lt;span style="color: #666666"&gt;.&lt;/span&gt;filter(name&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #4070a0"&gt;&amp;#39;Student&amp;#39;&lt;/span&gt;)&lt;span style="color: #666666"&gt;.&lt;/span&gt;count() &lt;span style="color: #666666"&gt;==&lt;/span&gt; &lt;span style="color: #40a070"&gt;0&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;return&lt;/span&gt; &lt;span style="color: #007020"&gt;False&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;This makes sure the user object exists before trying the test, and if not, we assume the test fails (by returning False).&lt;br /&gt;&lt;br /&gt;Now, our decorator will look something like this:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #555555; font-weight: bold"&gt;@login_required&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #555555; font-weight: bold"&gt;@user_passes_test&lt;/span&gt;(not_in_student_group, login_url&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #4070a0"&gt;&amp;#39;/advising/denied/&amp;#39;&lt;/span&gt;)&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;some_view&lt;/span&gt;(request):&lt;br /&gt;    &lt;span style="color: #60a0b0; font-style: italic"&gt;# ...&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-1124320250064783173?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/1124320250064783173'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/1124320250064783173'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2009/04/restricting-access-by-group-in-django.html' title='Restricting Access by Group in Django'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-7486424570660550130</id><published>2009-04-21T13:31:00.005-05:00</published><updated>2009-04-21T14:47:51.686-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Dynamically Displaying Fields in a ModelForm</title><content type='html'>&lt;b&gt;The Problem&lt;/b&gt;: I want to dynamically include some fields in a ModelForm based on some external criteria.  Sometimes I want the fields displayed, sometimes I don't.  &lt;br /&gt;&lt;br /&gt;I'm going to try to explain this scenario through a (albeit contrived) example.  I have a Model that looks like the following:&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;Suff&lt;/span&gt;(models&lt;span style="color: #666666"&gt;.&lt;/span&gt;Model):&lt;br /&gt;    foo &lt;span style="color: #666666"&gt;=&lt;/span&gt; models&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;CharField&lt;/span&gt;(max_length&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #40a070"&gt;255&lt;/span&gt;)&lt;br /&gt;    bar &lt;span style="color: #666666"&gt;=&lt;/span&gt; models&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;BooleanField&lt;/span&gt;(default&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #007020"&gt;False&lt;/span&gt;, blank&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #007020"&gt;True&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;is_foo_bar&lt;/span&gt;(&lt;span style="color: #007020"&gt;self&lt;/span&gt;):&lt;br /&gt;        &lt;span style="color: #4070a0; font-style: italic"&gt;&amp;#39;&amp;#39;&amp;#39; is this model&amp;#39;s foo attribute set to &amp;#39;bar&amp;#39; &amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold"&gt;return&lt;/span&gt; &lt;span style="color: #007020"&gt;self&lt;/span&gt;&lt;span style="color: #666666"&gt;.&lt;/span&gt;foo &lt;span style="color: #666666"&gt;==&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Normally, if I needed a Form for this Model, I would subclass a ModelForm like the following:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;StuffForm&lt;/span&gt;(models&lt;span style="color: #666666"&gt;.&lt;/span&gt;ModelForm):&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;Meta&lt;/span&gt;:&lt;br /&gt;        model &lt;span style="color: #666666"&gt;=&lt;/span&gt; Stuff&lt;br /&gt;        fields &lt;span style="color: #666666"&gt;=&lt;/span&gt; (&lt;span style="color: #4070a0"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;, &lt;span style="color: #4070a0"&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;However, if I do NOT want the 'bar' field to be displayed by default I would need to remove it from the ModelForms list of fields (or use something like &lt;em&gt;exclude = ('bar', )&lt;/em&gt; ).  But, if this form is created with an instance of Stuff whose &lt;em&gt;foo&lt;/em&gt; attribute contains the string &lt;em&gt;bar&lt;/em&gt;, I would like for the Form's 'bar' field to be displayed.&lt;br /&gt;&lt;br /&gt;I originally tried to accomplish this task by overridding StuffForm's __init__ method, and adding a new BooleanField when the desired circumstances arose...   However, I stumpled across Ross Poulton's &lt;a href="http://www.rossp.org/blog/2008/dec/15/modelforms/"&gt;Dynamic ModelForms in Django&lt;/a&gt;, and then I realized it would be much easier to &lt;b&gt;prevent a ModelForm's Field from being displayed&lt;/b&gt; than it would be to dynamically create one.&lt;br /&gt;&lt;br /&gt;In order to accomplish this, the StuffForms's __init__ method would look something like the following:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;__init__&lt;/span&gt;(&lt;span style="color: #007020"&gt;self&lt;/span&gt;, &lt;span style="color: #666666"&gt;*&lt;/span&gt;args, &lt;span style="color: #666666"&gt;**&lt;/span&gt;kwargs):&lt;br /&gt;    &lt;span style="color: #007020"&gt;super&lt;/span&gt;(StuffForm, &lt;span style="color: #007020"&gt;self&lt;/span&gt;)&lt;span style="color: #666666"&gt;.&lt;/span&gt;__init__(&lt;span style="color: #666666"&gt;*&lt;/span&gt;args, &lt;span style="color: #666666"&gt;**&lt;/span&gt;kwargs)&lt;br /&gt;    &lt;br /&gt;    &lt;span style="color: #60a0b0; font-style: italic"&gt;# If this form is created without an instance, OR&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #60a0b0; font-style: italic"&gt;# If the instance&amp;#39;s foo field is != &amp;#39;bar&amp;#39;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;if&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;not&lt;/span&gt; kwargs&lt;span style="color: #666666"&gt;.&lt;/span&gt;has_key(&lt;span style="color: #4070a0"&gt;&amp;#39;instance&amp;#39;&lt;/span&gt;) &lt;span style="color: #007020; font-weight: bold"&gt;or&lt;/span&gt; (kwargs&lt;span style="color: #666666"&gt;.&lt;/span&gt;has_key(&lt;span style="color: #4070a0"&gt;&amp;#39;instance&amp;#39;&lt;/span&gt;) &lt;span style="color: #007020; font-weight: bold"&gt;and&lt;/span&gt; \&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold"&gt;not&lt;/span&gt; kwargs[&lt;span style="color: #4070a0"&gt;&amp;#39;instance&amp;#39;&lt;/span&gt;]&lt;span style="color: #666666"&gt;.&lt;/span&gt;is_foo_bar()):&lt;br /&gt;        &lt;span style="color: #60a0b0; font-style: italic"&gt;# Remove this field from the form.&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold"&gt;del&lt;/span&gt; &lt;span style="color: #007020"&gt;self&lt;/span&gt;&lt;span style="color: #666666"&gt;.&lt;/span&gt;fields[&lt;span style="color: #4070a0"&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;]&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;Done.  I get all the benefits of a ModelForm, and the &lt;em&gt;bar&lt;/em&gt; field is not displayed unless it should be.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-7486424570660550130?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7486424570660550130'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7486424570660550130'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2009/04/dynamically-displaying-fields-in.html' title='Dynamically Displaying Fields in a ModelForm'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-4345063366065028230</id><published>2009-04-01T14:49:00.004-05:00</published><updated>2009-07-14T16:18:16.912-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>How to Set up a Foreign Key Constraint in MySQL</title><content type='html'>&lt;p&gt;The default &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/storage-engines.html"&gt;storage engine&lt;/a&gt; in MySQL (&lt;a href="http://dev.mysql.com/doc/refman/5.0/en/myisam-storage-engine.html"&gt;MyISAM&lt;/a&gt;) does not support Foreign Key constraints.  If you want to use Foreign Keys in Mysql, you need to use &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/using-innodb-tables.html"&gt;InnoDB&lt;/a&gt;.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The following is a simple example that illustrates Foreign Key constraints, we'll create tables to store information about Authors and their Books. The Foreign key will link a book to an Author. Note, that in MySQL we need to use the &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/using-innodb-tables.html"&gt;InnoDB&lt;/a&gt; storage engine to support &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/innodb-foreign-key-constraints.html"&gt;Foreign Key Constraints&lt;/a&gt;.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;First, we need to create a simple table for Authors. There are only two columns: a primary key and the author's name&lt;/p&gt;&lt;pre&gt;CREATE TABLE author (id integer primary key auto_increment, name text) ENGINE=InnoDB;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Next, we create a simple table for Books. Again, we need a primary key (id), the title of the book, and the column that will be used as the Foreign Kye (author_id).  The author_id column will be a Foreign Key that references the author table's id column (i.e. it's primary key).&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;CREATE TABLE books (id integer primary key auto_increment, title text, author_id integer NOT NULL) ENGINE=InnoDB;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Finally, we alter the books table to add the Foreign Key constraint. Below, the &lt;em&gt;author_id_refs&lt;/em&gt; is just a name for the constraint, and this could be anything that we want (as long as it's sensible!)&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;ALTER TABLE `books` ADD CONSTRAINT author_id_refs FOREIGN KEY (`author_id`) REFERENCES `author` (`id`);&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Another example is available in the MySQL documentation that covers &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/innodb-foreign-key-constraints.html"&gt;Foreign Key Constraints&lt;/a&gt;.&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;An Example&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;Insert a couple of Authors:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;insert into author (name) values ('Brad Montgomery');&lt;br /&gt;insert into author (name) values ('John Doe');&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Let's see what's in the author table:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mysql&gt; select * from author;&lt;br /&gt;+----+-----------------+&lt;br /&gt;| id | name            |&lt;br /&gt;+----+-----------------+&lt;br /&gt;|  1 | Brad Montgomery | &lt;br /&gt;|  2 | John Doe        | &lt;br /&gt;+----+-----------------+&lt;br /&gt;2 rows in set (0.00 sec)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Lets put some stuff in the Books table. Note that author_id column corresponds to the id column in the author table above.&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;insert into books (title, author_id) values ('Brads book', 1);&lt;br /&gt;insert into books (title, author_id) values ('John Does book', 2);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Lets see what the books table looks like and what's in it:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mysql&gt; describe books;&lt;br /&gt;+-----------+---------+------+-----+---------+----------------+&lt;br /&gt;| Field     | Type    | Null | Key | Default | Extra          |&lt;br /&gt;+-----------+---------+------+-----+---------+----------------+&lt;br /&gt;| id        | int(11) | NO   | PRI | NULL    | auto_increment | &lt;br /&gt;| title     | text    | YES  |     | NULL    |                | &lt;br /&gt;| author_id | int(11) | NO   | MUL | NULL    |                | &lt;br /&gt;+-----------+---------+------+-----+---------+----------------+&lt;br /&gt;3 rows in set (0.00 sec)&lt;br /&gt;&lt;br /&gt;mysql&gt; select * from books;&lt;br /&gt;+----+----------------+-----------+&lt;br /&gt;| id | title          | author_id |&lt;br /&gt;+----+----------------+-----------+&lt;br /&gt;|  1 | Brads book     |         1 | &lt;br /&gt;|  2 | John Does book |         2 | &lt;br /&gt;+----+----------------+-----------+&lt;br /&gt;2 rows in set (0.00 sec)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Try to Delete Something&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;When you try to delete an author, an Error will occur&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;delete from author where id=2;&lt;br /&gt;ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails &lt;br /&gt;(`brad/books`, CONSTRAINT `author_id_refs` FOREIGN KEY (`author_id`) REFERENCES `author` (`id`))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;This happens because the data in the books table depends on the data in the author table.  The Default constraint prevents you from deleting these books, without first deleting the author&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mysql&gt; delete from books where author_id=2;&lt;br /&gt;Query OK, 1 row affected (0.00 sec)&lt;br /&gt;&lt;br /&gt;mysql&gt; delete from author where id=2;&lt;br /&gt;Query OK, 1 row affected (0.01 sec)&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;When you delete the author's books first, the author no longer has any dependencies.  You can therefore delete the author.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-4345063366065028230?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/4345063366065028230'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/4345063366065028230'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2009/04/how-to-set-up-foreign-key-constraint-in.html' title='How to Set up a Foreign Key Constraint in MySQL'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-5225263534188052703</id><published>2009-03-25T15:20:00.003-05:00</published><updated>2009-03-25T15:27:57.003-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='Backup'/><title type='text'>Public Keys don't like write access!</title><content type='html'>The first part of my &lt;a href="http://bradmontgomery.blogspot.com/2007/01/automatic-backups-with-cron-tar-and-ssh.html"&gt;Automatic Backups with cron, tar, and SSH&lt;/a&gt; details how to set up remote login (sans passwords) using RSA public key.&lt;br /&gt;&lt;br /&gt;Apparently this doesn't work if your home directory allows groups or others write access. So if used &lt;em&gt;ssh-keygen -t rsa&lt;/em&gt; to generate a public key which you added to a remote host (under &lt;em&gt;.ssh/authorized_keys&lt;/em&gt;), but ssh is still prompting you for a password, try setting your home directory's permissions to something like 0755 (or remove write permissions from other and groups)!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-5225263534188052703?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/5225263534188052703'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/5225263534188052703'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2009/03/rsa-public-keys-dont-like-groups.html' title='Public Keys don&apos;t like write access!'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-3395389466546588268</id><published>2009-03-24T09:28:00.003-05:00</published><updated>2009-03-24T09:33:58.813-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='Fun'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>mod_python checks your blood pressure</title><content type='html'>I'm deploying a &lt;a href="http://www.djangoproject.com/"&gt;django&lt;/a&gt; project using &lt;a href="http://www.modpython.org/"&gt;mod_python&lt;/a&gt;... now, usually I will just use my package management tools to install a binary version, but this time I need to build it from source.&lt;br /&gt;&lt;br /&gt;As I get ready to go through the whole configure/make/make install process, I peruse the output of configure just to make sure everything is ok...&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: 'Courier-New', courier;"&gt;&lt;br /&gt;checking for gcc... gcc&lt;br /&gt;checking for C compiler default output file name... a.out&lt;br /&gt;checking whether the C compiler works... yes&lt;br /&gt;checking whether we are cross compiling... no&lt;br /&gt;checking for suffix of executables... &lt;br /&gt;checking for suffix of object files... o&lt;br /&gt;checking whether we are using the GNU C compiler... yes&lt;br /&gt;checking whether gcc accepts -g... yes&lt;br /&gt;checking for gcc option to accept ANSI C... none needed&lt;br /&gt;checking for ar... ar&lt;br /&gt;checking for a BSD-compatible install... /usr/bin/install -c&lt;br /&gt;checking whether make sets $(MAKE)... yes&lt;br /&gt;checking for main in -lm... yes&lt;br /&gt;checking for an ANSI C-conforming const... yes&lt;br /&gt;&lt;big&gt;&lt;b&gt;checking your blood pressure... a bit high, but we can proceed&lt;/b&gt;&lt;/big&gt;&lt;br /&gt;configure: checking whether apxs is available...&lt;br /&gt;checking for --with-apxs... /usr/sbin/apxs executable, good&lt;br /&gt;checking Apache version... 2.0.52&lt;br /&gt;checking for Apache libexec directory... /usr/lib/httpd/modules&lt;br /&gt;checking for Apache include directory... -I/usr/include/httpd&lt;br /&gt;checking for --with-python... /usr/local/bin/python2.6&lt;br /&gt;checking Python version... 2.6&lt;br /&gt;checking Python install prefix... /usr/local&lt;br /&gt;checking checking where python libraries are installed... /usr/local/lib/python2.6&lt;br /&gt;checking for Py_NewInterpreter in -lpython2.6... yes&lt;br /&gt;checking what libraries Python was linked with... -lpython2.6 -lpthread -ldl -lutil -lm&lt;br /&gt;checking linker flags used to link Python... &lt;br /&gt;checking where Python include files are... -I/usr/local/include/python2.6&lt;br /&gt;checking for --with-python-src... /home/bkmontgomery/Python-2.6.1/src&lt;br /&gt;checking for --with-mutex-dir... no&lt;br /&gt;Using MUTEX_DIR /tmp&lt;br /&gt;checking for --with-max-locks... 32&lt;br /&gt;Using 32 MAX_LOCKS.&lt;br /&gt;checking for --with-flex... /usr/bin/flex&lt;br /&gt;found /usr/bin/flex, we'll use this. Use --with-flex to specify another.&lt;br /&gt;checking flex version... configure: WARNING: Flex version 2.5.4 found.&lt;br /&gt;    Version 2.5.31 or greater is required.  You can generally ignore this&lt;br /&gt;    warning unless you need to regenerate psp_parser.c from psp_parse.l.&lt;br /&gt;    If you do need regenerate psp_parser.c, use --with-flex to specify the&lt;br /&gt;    location of the correct flex version. See the README for more information.&lt;br /&gt;configure: creating ./config.status&lt;br /&gt;config.status: creating Makefile&lt;br /&gt;config.status: creating src/Makefile&lt;br /&gt;config.status: creating Doc/Makefile&lt;br /&gt;config.status: creating src/include/mod_python.h&lt;br /&gt;config.status: creating test/Makefile&lt;br /&gt;config.status: creating test/testconf.py&lt;br /&gt;config.status: creating dist/setup.py&lt;br /&gt;config.status: creating dist/Makefile&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Thank you for caring for my health, mod_python.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-3395389466546588268?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/3395389466546588268'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/3395389466546588268'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2009/03/modpython-checks-your-blood-pressure.html' title='mod_python checks your blood pressure'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-7714347806734835776</id><published>2009-02-20T10:15:00.005-06:00</published><updated>2009-02-20T11:01:05.697-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='cron'/><title type='text'>Scheduled Tasks (or cron jobs) with Django</title><content type='html'>This is my take on setting up cron jobs for the apps in a Django project.  It is based on my own convention, and it solves my initial problems where I want to perform some action on all of my Django apps at a periodic interval (currently this is a once-a-day task).&lt;br /&gt;&lt;br /&gt;In order for this to work, I create a &lt;em&gt;cron.py&lt;/em&gt; module for all of my INSTALLED_APPS. This module &lt;strong&gt;must&lt;/strong&gt; contain a &lt;em&gt;run&lt;/em&gt; method.  Other than that, it can work just like any other python module (using django's internals as necessary).&lt;br /&gt;&lt;br /&gt;For example, if you had an app called utils (possibly located at mysite/utils/), and if you just wanted to delete all sessions with old expire_dates, your &lt;em&gt;cron.py&lt;/em&gt; (which you would put in mysite/utils/cron.py) might look something like this:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;datetime&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; datetime&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;django.contrib.sessions.models&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; Session&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;delete_old_sessions&lt;/span&gt;():&lt;br /&gt;    Session&lt;span style="color: #666666"&gt;.&lt;/span&gt;objects&lt;span style="color: #666666"&gt;.&lt;/span&gt;filter(expire_date__lt&lt;span style="color: #666666"&gt;=&lt;/span&gt;datetime&lt;span style="color: #666666"&gt;.&lt;/span&gt;now())&lt;span style="color: #666666"&gt;.&lt;/span&gt;delete()&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;run&lt;/span&gt;():&lt;br /&gt;    delete_old_sessions()&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Now, the meat of this solution checks for the cron.py module in all of the apps in mysite.settings.INSTALLED_APPS, and invokes its run() method.  I've also named this module &lt;em&gt;cron.py&lt;/em&gt;, but this gets stored in Django's project directory (e.g. mysite)... the same directory where your &lt;em&gt;settings.py&lt;/em&gt; is located.&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;#!/usr/bin/env python&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;Project-wide Cron Job... A Command-line Django Script.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;This script gets scheduled and run by cron (or whatever).&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;It then calls the `run` method of each app&amp;#39;s cron module, &lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;if it exists (should be `appname/cron.py`)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;This script should be invoked after setting the &lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;DJANGO_SETTINGS_MODULE environment variable.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;You chould do this in a BASH script as follows:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    export DJANGO_SETTINGS_MODULE=mysite.settings&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    python /path/to/mysite/cron.py&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;django.conf&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; settings&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;my_import&lt;/span&gt;(name):&lt;br /&gt;    &lt;span style="color: #4070a0; font-style: italic"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    __import__ helper function to import modules inside packages&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    e.g.:  where name is something like &amp;#39;package.module.mod_i_want&amp;#39;,&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;           would return mod_i_want&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    &lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    See: http://www.python.org/doc/2.5.2/lib/built-in-funcs.html&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;    &amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;br /&gt;    mod &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #007020"&gt;__import__&lt;/span&gt;(name)&lt;br /&gt;    components &lt;span style="color: #666666"&gt;=&lt;/span&gt; name&lt;span style="color: #666666"&gt;.&lt;/span&gt;split(&lt;span style="color: #4070a0"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;)&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;for&lt;/span&gt; comp &lt;span style="color: #007020; font-weight: bold"&gt;in&lt;/span&gt; components[&lt;span style="color: #40a070"&gt;1&lt;/span&gt;:]:&lt;br /&gt;        mod &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #007020"&gt;getattr&lt;/span&gt;(mod, comp)&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;return&lt;/span&gt; mod&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;run&lt;/span&gt;():&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;for&lt;/span&gt; app &lt;span style="color: #007020; font-weight: bold"&gt;in&lt;/span&gt; settings&lt;span style="color: #666666"&gt;.&lt;/span&gt;INSTALLED_APPS:&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold"&gt;if&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;not&lt;/span&gt; app&lt;span style="color: #666666"&gt;.&lt;/span&gt;startswith(&lt;span style="color: #4070a0"&gt;&amp;#39;django&amp;#39;&lt;/span&gt;):&lt;br /&gt;            output_info &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%s&lt;/span&gt;&lt;span style="color: #4070a0"&gt;.cron&amp;#39;&lt;/span&gt;&lt;span style="color: #666666"&gt;%&lt;/span&gt;app&lt;br /&gt;            &lt;span style="color: #60a0b0; font-style: italic"&gt;## Dynamically import a module called &amp;#39;cron&amp;#39;&lt;/span&gt;&lt;br /&gt;            &lt;span style="color: #60a0b0; font-style: italic"&gt;## from each INSTALLED_APP (if it exists)&lt;/span&gt;&lt;br /&gt;            &lt;span style="color: #007020; font-weight: bold"&gt;try&lt;/span&gt;:&lt;br /&gt;                cron_mod &lt;span style="color: #666666"&gt;=&lt;/span&gt; my_import(app&lt;span style="color: #666666"&gt;+&lt;/span&gt;&lt;span style="color: #4070a0"&gt;&amp;#39;.cron&amp;#39;&lt;/span&gt;)&lt;br /&gt;                output_info &lt;span style="color: #666666"&gt;+=&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;#39;... FOUND&amp;#39;&lt;/span&gt;&lt;br /&gt;                &lt;span style="color: #007020; font-weight: bold"&gt;print&lt;/span&gt; output_info&lt;br /&gt;                &lt;span style="color: #60a0b0; font-style: italic"&gt;## 3. Execute the cron&amp;#39;s run method (if it exists)&lt;/span&gt;&lt;br /&gt;                &lt;span style="color: #007020; font-weight: bold"&gt;if&lt;/span&gt; &lt;span style="color: #007020"&gt;hasattr&lt;/span&gt;(cron_mod, &lt;span style="color: #4070a0"&gt;&amp;#39;run&amp;#39;&lt;/span&gt;):&lt;br /&gt;                    &lt;span style="color: #60a0b0; font-style: italic"&gt;#print &amp;#39;---&amp;gt; calling run()&amp;#39;&lt;/span&gt;&lt;br /&gt;                    cron_mod&lt;span style="color: #666666"&gt;.&lt;/span&gt;run()&lt;br /&gt;            &lt;span style="color: #007020; font-weight: bold"&gt;except&lt;/span&gt; &lt;span style="color: #007020"&gt;ImportError&lt;/span&gt;:&lt;br /&gt;                &lt;span style="color: #60a0b0; font-style: italic"&gt;# ignore packages that don&amp;#39;t have a cron module&lt;/span&gt;&lt;br /&gt;                output_info &lt;span style="color: #666666"&gt;+=&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;#39;... SKIPPED&amp;#39;&lt;/span&gt;&lt;br /&gt;                &lt;span style="color: #007020; font-weight: bold"&gt;print&lt;/span&gt; output_info&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;if&lt;/span&gt; __name__ &lt;span style="color: #666666"&gt;==&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;:&lt;br /&gt;    run()&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The final piece of this puzzle lies in the BASH script used to invoke the above python module. It makes sure the appropriate environment variables are set and then it invokes the above module.  I also store this in my django project directory (as cron.sh), and I use cron to schedule it to run.&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;#!/bin/bash&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;# This is a Django, Project-specific Cron script.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;# Separate Projects would need a copy of this script &lt;/span&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;# with appropriate Settings export statments.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #bb60d5"&gt;PYTHONPATH&lt;/span&gt;&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #4070a0"&gt;&amp;quot;${PYTHONPATH}:/path/to/django/project/directory&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #007020"&gt;export &lt;/span&gt;PYTHONPATH&lt;br /&gt;&lt;span style="color: #007020"&gt;export &lt;/span&gt;&lt;span style="color: #bb60d5"&gt;DJANGO_SETTINGS_MODULE&lt;/span&gt;&lt;span style="color: #666666"&gt;=&lt;/span&gt;mysite.settings&lt;br /&gt;&lt;br /&gt;python/path/to/django/project/directory/mysite/cron.py&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Using cron, you'd schudule this to run every morning at 6am by editing your crontab and adding the following:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;#m h  dom mon dow   command&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;&lt;/span&gt;  &lt;span style="color: #40a070"&gt;0&lt;/span&gt;   &lt;span style="color: #40a070"&gt;6&lt;/span&gt;    *        *      *     /path/to/django/project/directory/cron.sh&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;That's it.  This has been working for me, but there is at least one major pitfall:  All of your app's cron tasks get run at the same time. This works well if your apps need to do something once a day (which has been my requirement), but this probably won't work well if you have some apps that need to run tasks at differing times.&lt;br /&gt;&lt;br /&gt;There are also other solutions to this (none of which I've tried).  There's a snippet at &lt;a href="http://www.djangosnippets.org/snippets/1126/"&gt;http://www.djangosnippets.org/snippets/1126/&lt;/a&gt; which looks interesting.  Then there's the &lt;a href="http://code.google.com/p/django-cron/"&gt;django-cron&lt;/a&gt; app which seems to be fairly flexible in how it works, and it doesn't require cron (so this is a plus if you cant set a crontab or if you're on Windows!)&lt;br /&gt;&lt;br /&gt;Thanks in advance for any feedback... suggestions are always welcome!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-7714347806734835776?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7714347806734835776'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7714347806734835776'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2009/02/scheduled-tasks-or-cron-jobs-with.html' title='Scheduled Tasks (or cron jobs) with Django'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-6279726457602327300</id><published>2009-01-14T16:08:00.003-06:00</published><updated>2009-01-14T16:18:10.347-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>A Custom form for Django's Automatic Admin.</title><content type='html'>A huge selling-point for Django (at least for developers) is its &lt;a href="http://docs.djangoproject.com/en/dev/ref/contrib/admin/#ref-contrib-admin"&gt;Automatic Admin&lt;/a&gt;.  However, the ease at which the Admin can be set up, might make one second-guess an attempt to customize what is provided by default.  Of course, the default admin site may not be without its drawbacks...&lt;br /&gt;&lt;br /&gt;Many of the django Apps that I have built, tap into Django's &lt;a href="http://docs.djangoproject.com/en/dev/topics/auth/#topics-auth"&gt;User Authentication System&lt;/a&gt;. Simply put, when I build a model, it has a Foreign Key to django's User Class.&lt;br /&gt;&lt;br /&gt;Here's an example Model:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;django.contrib.auth.models&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; User&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;django.db&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; models&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;Book&lt;/span&gt;(models&lt;span style="color: #666666"&gt;.&lt;/span&gt;Model):&lt;br /&gt;    author &lt;span style="color: #666666"&gt;=&lt;/span&gt; models&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;ForeignKey&lt;/span&gt;(User)&lt;br /&gt;    title &lt;span style="color: #666666"&gt;=&lt;/span&gt; models&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;CharField&lt;/span&gt;()&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The problem here is that when I create or edit a Book object using the Automatic Admin, the author field is represented by a select element, whose options contain ALL User objects... listed by &lt;b&gt;username&lt;/b&gt;! Wouldn't it be nice if we could have that listed as "&lt;em&gt;firstname lastname&lt;/em&gt;" or even as "&lt;em&gt;lastname, firstname"&lt;/em&gt;?  You can! And here's how:&lt;br /&gt;&lt;br /&gt;First of all, Django's admin makes extensive use of &lt;a href="http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#topics-forms-modelforms"&gt;ModelForms&lt;/a&gt;, and fields with a Foreign Key relationship are represented by a &lt;a href="http://docs.djangoproject.com/en/dev/ref/forms/fields/#modelchoicefield"&gt;ModelChoiceField&lt;/a&gt;. So, all we need to do is extend the ModelChoiceField so that we have something that can be used on any Form that represents a Model with a Foreign Key to a User object.  The &lt;em&gt;label_from_instance&lt;/em&gt; method accepts an object (in this case, a User object), and returns a string that will be used between &amp;lt;option&amp;gt; elements.  In the example below, I've chosen to format that as "&lt;em&gt;firstname lastname&lt;/em&gt; (&lt;em&gt;username&lt;/em&gt;)".&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;django.forms&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; ModelChoiceField&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;UserModelChoiceField&lt;/span&gt;(ModelChoiceField):&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;label_from_instance&lt;/span&gt;(&lt;span style="color: #007020"&gt;self&lt;/span&gt;, obj):&lt;br /&gt;        &lt;span style="color: #60a0b0; font-style: italic"&gt;# Return a string of the format: &amp;quot;firstname lastname (username)&amp;quot;&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold"&gt;return&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%s&lt;/span&gt;&lt;span style="color: #4070a0"&gt; (&lt;/span&gt;&lt;span style="color: #70a0d0; font-style: italic"&gt;%s&lt;/span&gt;&lt;span style="color: #4070a0"&gt;)&amp;quot;&lt;/span&gt;&lt;span style="color: #666666"&gt;%&lt;/span&gt;(obj&lt;span style="color: #666666"&gt;.&lt;/span&gt;get_full_name(), obj&lt;span style="color: #666666"&gt;.&lt;/span&gt;username)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Now, create a ModelForm for your Model, which specifies the new Field to be used for the author.  Note that we need to pass it a queryset of Users.  Below, I've named this ModelForm, &lt;b&gt;BookAdminForm&lt;/b&gt; since I'm only going to use this form for the admin pages.  &lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;django.forms&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; ModelForm&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;django.contrib.auth.models&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; User&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;BookAdminForm&lt;/span&gt;(ModelForm):&lt;br /&gt;    author &lt;span style="color: #666666"&gt;=&lt;/span&gt; UserModelChoiceField(User&lt;span style="color: #666666"&gt;.&lt;/span&gt;objects&lt;span style="color: #666666"&gt;.&lt;/span&gt;all()&lt;span style="color: #666666"&gt;.&lt;/span&gt;order_by(&lt;span style="color: #4070a0"&gt;&amp;#39;first_name&amp;#39;&lt;/span&gt;))&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;Meta&lt;/span&gt;:&lt;br /&gt;        model &lt;span style="color: #666666"&gt;=&lt;/span&gt; Book&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Now we set up the ModelAmin for the Book Model.  In it, we can specify the form that is used by Django's automatic admin (Note that this &lt;a href="http://docs.djangoproject.com/en/dev/ref/contrib/admin/#form"&gt;MUST be a ModelForm&lt;/a&gt;!).  Your admin would look something similar to the following:&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;django.contrib&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; admin&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;forms&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; BookAdminForm&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;models&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; Book&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;BookAdmin&lt;/span&gt;(admin&lt;span style="color: #666666"&gt;.&lt;/span&gt;ModelAdmin):&lt;br /&gt;    form &lt;span style="color: #666666"&gt;=&lt;/span&gt; BookAdminForm&lt;br /&gt;&lt;br /&gt;admin&lt;span style="color: #666666"&gt;.&lt;/span&gt;site&lt;span style="color: #666666"&gt;.&lt;/span&gt;register(Book, BookAdmin)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Now, when you use the Automatic admin to add or edit existing Book entries, the drop-down list of Author names will be a bit more user-friendly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-6279726457602327300?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/6279726457602327300'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/6279726457602327300'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2009/01/custom-form-for-djangos-automatic-admin.html' title='A Custom form for Django&apos;s Automatic Admin.'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-4532705687996021665</id><published>2009-01-05T19:29:00.004-06:00</published><updated>2009-01-05T20:14:39.266-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Add a Context Processor for your Django app using Sites</title><content type='html'>I've recently refactored a significant number of my Django Apps so that they include the "&lt;a href="http://docs.djangoproject.com/en/dev/ref/contrib/sites/#ref-contrib-sites"&gt;sites&lt;/a&gt;" framework.  Essentially, this allows me to use the same code (and database) for multiple sites. &lt;br /&gt;&lt;br /&gt;For Example, if I was was building a &lt;abbr title="Content Management System"&gt;CMS&lt;/abbr&gt; (and I am!),  I might have a model that defines a "page":&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;django.db&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; models&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;django.contrib.auth.models&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; User&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;django.contrib.sites.models&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; Site&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;Page&lt;/span&gt;(models&lt;span style="color: #666666"&gt;.&lt;/span&gt;Model):&lt;br /&gt;    title &lt;span style="color: #666666"&gt;=&lt;/span&gt; models&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;CharField&lt;/span&gt;(&lt;span style="color: #4070a0"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;, max_length&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #40a070"&gt;255&lt;/span&gt;)&lt;br /&gt;    content &lt;span style="color: #666666"&gt;=&lt;/span&gt; models&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;TextField&lt;/span&gt;()&lt;br /&gt;    author &lt;span style="color: #666666"&gt;=&lt;/span&gt; models&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;ForeignKey&lt;/span&gt;(User)&lt;br /&gt;    sites &lt;span style="color: #666666"&gt;=&lt;/span&gt; models&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;ManyToManyField&lt;/span&gt;(Site, &lt;br /&gt;                  help_text&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #4070a0"&gt;&amp;quot;This page will be displayed on the selected Sites&amp;quot;&lt;/span&gt;)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Note that the Page class has a &lt;a href="http://docs.djangoproject.com/en/dev/ref/models/fields/#manytomanyfield"&gt;ManyToManyField&lt;/a&gt; relation ship to a Django Site, which allows a page to be associated with one or more Sites.  The neat thing about this, is that when I publish content on a Page, it can be published to one or more sites!&lt;br /&gt;&lt;br /&gt;While working with this, I discovered I often wanted to write template code that &lt;em&gt;knew&lt;/em&gt; which which Site it was associated.  For example, I might have a base template containing a block inside html head elements so templates that inherit it can include external javascript or CSS files.  For example, &lt;em&gt;base.html&lt;/em&gt; might include the following:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;block&lt;/span&gt; &lt;span style="color: #bb60d5"&gt;title&lt;/span&gt; &lt;span style="color: #007020"&gt;%}{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;endblock&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;block&lt;/span&gt; &lt;span style="color: #bb60d5"&gt;head&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt; &lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;endblock&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Then, in any template that inherits from base.html, I could do something like the following:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;extends&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;quot;base.html&amp;quot;&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;block&lt;/span&gt; &lt;span style="color: #bb60d5"&gt;head&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;ifequal&lt;/span&gt; &lt;span style="color: #bb60d5"&gt;current_site.domain&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;quot;www.whatever.com&amp;quot;&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;link&lt;/span&gt; &lt;span style="color: #4070a0"&gt;rel=&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;type=&amp;quot;text/css&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;href=&amp;quot;/media/whatever.css&amp;quot;&lt;/span&gt; &lt;span style="color: #062873; font-weight: bold"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;endifequal&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;endblock&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;There trick here, though, is "How is my template going to know what site is being requested?"&lt;br /&gt;&lt;br /&gt;I &lt;em&gt;could&lt;/em&gt; put something like the following in &lt;b&gt;every&lt;/b&gt; view I write...&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;current_site &lt;span style="color: #666666"&gt;=&lt;/span&gt; Site&lt;span style="color: #666666"&gt;.&lt;/span&gt;objects&lt;span style="color: #666666"&gt;.&lt;/span&gt;get_current()&lt;/pre&gt;&lt;/div&gt;BUT, that's a lot of extra stuff to type.  Especially if you have a lot of views.&lt;br /&gt;&lt;br /&gt;The clever thing to do, would be to write code so that a Site object containing the currently requested site is automatically added to the current &lt;a href="http://docs.djangoproject.com/en/dev/ref/templates/api/#basics"&gt;Context&lt;/a&gt;.  We can do that by writing our own &lt;b&gt;Context Processor&lt;/b&gt;!&lt;br /&gt;&lt;br /&gt;And that's just what I did!  The following code is fairly simple.  It just retrieves the current Site from given request object (using Site.objects.get_current()), then returns a dictionary mapping the current site to the variable name &lt;b&gt;current_site&lt;/b&gt;. &lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;django.conf&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; settings&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;django.contrib.sites.models&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; Site&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;current_site&lt;/span&gt;(request):&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;A context processor to add the &amp;quot;current site&amp;quot; to the current Context&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #4070a0; font-style: italic"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;try&lt;/span&gt;:&lt;br /&gt;        current_site &lt;span style="color: #666666"&gt;=&lt;/span&gt; Site&lt;span style="color: #666666"&gt;.&lt;/span&gt;objects&lt;span style="color: #666666"&gt;.&lt;/span&gt;get_current()&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold"&gt;return&lt;/span&gt; {&lt;br /&gt;            &lt;span style="color: #4070a0"&gt;&amp;#39;current_site&amp;#39;&lt;/span&gt;: current_site,&lt;br /&gt;        }&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;except&lt;/span&gt; Site&lt;span style="color: #666666"&gt;.&lt;/span&gt;DoesNotExist:&lt;br /&gt;        &lt;span style="color: #60a0b0; font-style: italic"&gt;# always return a dict, no matter what!&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold"&gt;return&lt;/span&gt; {&lt;span style="color: #4070a0"&gt;&amp;#39;current_site&amp;#39;&lt;/span&gt;:&lt;span style="color: #4070a0"&gt;&amp;#39;&amp;#39;&lt;/span&gt;} &lt;span style="color: #60a0b0; font-style: italic"&gt;# an empty string&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;For this to work, we've got to add the function above to the list of TEMPLATE_CONTEXT_PROCESSORS in our project settings file.  My project directory is called "mysite",  so I created a folder called "context_processors", and in it, I created a file called "current_site.py".  That file contains the function &lt;em&gt;current_site&lt;/em&gt; defined above.  &lt;br /&gt;&lt;br /&gt;To get my new context processor working, I've got to edit mysite/settings.py, which now looks something like the following:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;TEMPLATE_CONTEXT_PROCESSORS &lt;span style="color: #666666"&gt;=&lt;/span&gt; (&lt;br /&gt;    &lt;span style="color: #4070a0"&gt;&amp;quot;mysite.context_processors.current_site.current_site&amp;quot;&lt;/span&gt;,&lt;br /&gt;    &lt;span style="color: #4070a0"&gt;&amp;quot;django.core.context_processors.auth&amp;quot;&lt;/span&gt;, &lt;br /&gt;    &lt;span style="color: #4070a0"&gt;&amp;quot;django.core.context_processors.debug&amp;quot;&lt;/span&gt;,&lt;br /&gt;    &lt;span style="color: #4070a0"&gt;&amp;quot;django.core.context_processors.i18n&amp;quot;&lt;/span&gt;,&lt;br /&gt;    &lt;span style="color: #4070a0"&gt;&amp;quot;django.core.context_processors.media&amp;quot;&lt;/span&gt;, &lt;br /&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt; &lt;br /&gt;Voila!  Now, while writing template code, I can always access the &lt;em&gt;current_site&lt;/em&gt; variable!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-4532705687996021665?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/4532705687996021665'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/4532705687996021665'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2009/01/add-context-processor-for-your-django.html' title='Add a Context Processor for your Django app using Sites'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-2142776883690478188</id><published>2009-01-04T14:27:00.004-06:00</published><updated>2009-01-04T15:08:08.537-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Fun'/><category scheme='http://www.blogger.com/atom/ns#' term='Image'/><title type='text'>Morphology is Fun!</title><content type='html'>&lt;a href="http://en.wikipedia.org/wiki/Morphological_image_processing"&gt;Morphological operations&lt;/a&gt; are very common in image processing.  The two most basic of these are Erosion and Dilations, and from these, additional morphological operations can be performed. While there is an &lt;a href="http://www.ph.tn.tudelft.nl/Courses/FIP/noframes/fip-Morpholo.html"&gt;abundance&lt;/a&gt; of &lt;a href="http://www-viz.tamu.edu/faculty/parke/ends489f00/notes/sec1_9.html"&gt;literature&lt;/a&gt; on the topic of &lt;a href="http://www.dspguide.com/ch25/4.htm"&gt;morphology&lt;/a&gt;, I often like to view concrete examples of the operations.  &lt;br /&gt;&lt;br /&gt;So, I've &lt;a href="http://bradmontgomery.net/morphology/"&gt;published a few images&lt;/a&gt; that illustrate the output of 1-to-5 iterations of various morphological operations--erosion, dilation, opening, closing, gradient, top hat, and black hat--with various structuring elements--rectangle, cross, and ellipse.&lt;br /&gt;&lt;br /&gt;See it at:  &lt;a href="http://bradmontgomery.net/morphology/"&gt;bradmontgomery.net/morphology/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The code to reproduce these images was written using OpenCV, and is freely available for academic purposes (that means its not "great" code, nor is it production-ready!). &lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://bradmontgomery.net/morphology/erode.cpp"&gt;erode.cpp&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://bradmontgomery.net/morphology/dilate.cpp"&gt;dilate.cpp&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://bradmontgomery.net/morphology/morphology.cpp"&gt;morphology.cpp&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-2142776883690478188?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2142776883690478188'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2142776883690478188'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2009/01/morphology-is-fun.html' title='Morphology is Fun!'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-7973631231608601676</id><published>2008-12-17T19:37:00.007-06:00</published><updated>2010-09-06T08:35:54.239-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python PDF'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>How to convert HTML to PDF using Python.</title><content type='html'>I'm building web-based, data-driven apps using &lt;a href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt;.  Eventually (or unfortunately), I will need to generate some reports that are &lt;span style="font-style:italic;"&gt;printer-friendly&lt;/span&gt;.  Logically, &lt;a href="http://en.wikipedia.org/wiki/Portable_Document_Format"&gt;PDF&lt;/a&gt; is &lt;span style="font-weight:bold;"&gt;the&lt;/span&gt; format for such files... so how am I going to convert my xHTML and CSS to a nice-looking PDF document?&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://www.djangobook.com/"&gt;Django Book&lt;/a&gt; has a whole chapter dedicated to &lt;a href="http://www.djangobook.com/en/1.0/chapter11/"&gt;Generating Non-HTML Content&lt;/a&gt;.  They seem to to be fond of &lt;a href="http://www.reportlab.org/rl_toolkit.html"&gt;ReportLab ToolKit&lt;/a&gt;.  The caveat here, though, is that you need to know a bit about the internals of a PDF document.  If you're familiar with this, the ReportLab toolkit seems to be the way to go!   It has many features, and it seems to be a powerful PDF-generating tool.&lt;br /&gt;&lt;br /&gt;Unfortunately, I know nothing about PDF internals, but I do know quite a bit about HTML and CSS.  That's why &lt;a href="http://www.xhtml2pdf.com/"&gt;xhtml2pdf.com&lt;/a&gt; caught my attention.  If it delivers on it's promises, it parses HTML and CSS and generates PDFs  (imagine that)!  There's also a handy Activestate recipe using it: &lt;a href="http://code.activestate.com/recipes/572160/"&gt;Recipe 572160: HTML/CSS to PDF converter&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I'm definately going to check this (HTML2PDF.org) out... so expect an update on this!&lt;br /&gt;&lt;br /&gt;Any other suggestions?&lt;br /&gt;&lt;br /&gt;UPDATE: &lt;a href="http://www.xhtml2pdf.com/"&gt;xhtml2pdf&lt;/a&gt; works well.  There's also a great&lt;br /&gt;&lt;a href="http://www.20seven.org/journal/2008/11/pdf-generation-with-pisa-in-django.html"&gt;post by Greg Newman&lt;/a&gt; outlining how it's used in django.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-7973631231608601676?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7973631231608601676'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7973631231608601676'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/12/how-to-convert-html-to-pdf-using-python.html' title='How to convert HTML to PDF using Python.'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-3588681090551099694</id><published>2008-12-04T19:11:00.004-06:00</published><updated>2008-12-04T19:29:10.547-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Fun'/><category scheme='http://www.blogger.com/atom/ns#' term='awesome'/><category scheme='http://www.blogger.com/atom/ns#' term='music'/><title type='text'>radiohead - 15 steps video</title><content type='html'>Updated... BIZRADv2... is a bit better.&lt;br /&gt;&lt;br /&gt;&lt;script type="text/javascript" src="http://www.wowow.co.jp/music/radiohead/special/widget/widget.js" charset="utf-8"&gt;&lt;/script&gt;&lt;script type="text/javascript"&gt;rh.createWidget("BIZRADV2", "4G5D1F9F2F0G8I9H1F5F2E0D5E1F7C8F5GaK3E1G5D1C2E4C8C9C7C6F4D5DaD9E8D5D1K9D8D7D1D0G5D2E1D4DaD9D6D3E5D1C2E5D3D4DbD0D4E1D5DaD9D8C7H2D4E9E7D5C1E3D1D2D5D7C8EaD9D1H3D1D7DaE6D5C1D3E7F4D8CaD9DaD1C2C1C0F2C1C2D0D7D8D6E9FaH3C4F1G3H5F4E0E1G8DaC9D3E2G1D5E8C7E6E4C3E0CaFbD7J");&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-3588681090551099694?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/3588681090551099694'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/3588681090551099694'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/12/radiohead-15-steps-video.html' title='radiohead - 15 steps video'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-140923247933426192</id><published>2008-12-04T16:27:00.001-06:00</published><updated>2008-12-04T16:32:11.242-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='meme'/><title type='text'>That Looks about Right</title><content type='html'>&lt;pre&gt;&lt;br /&gt; history | awk '{ print $2 }' | sort | uniq -c | sort -rn | head&lt;br /&gt; 152 cd&lt;br /&gt; 145 ll&lt;br /&gt;  73 vi&lt;br /&gt;  20 rm&lt;br /&gt;  18 svn&lt;br /&gt;  13 exit&lt;br /&gt;   9 ls&lt;br /&gt;   8 grep&lt;br /&gt;   7 php&lt;br /&gt;   5 wget&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;via &lt;a href="http://www.b-list.org/weblog/2008/apr/10/meme/"&gt;b-list.org&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-140923247933426192?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/140923247933426192'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/140923247933426192'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/12/that-looks-about-right.html' title='That Looks about Right'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-641240897788708420</id><published>2008-11-24T15:11:00.008-06:00</published><updated>2009-09-23T20:29:12.928-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><title type='text'>A simple Django example with Ajax</title><content type='html'>I often employ Ajax in HTML forms in order to update the list of options in select elements.  For example, suppose a form consists of two select elements, and the options in the second depends on the values selected in the first. A simple example of this might be an Automobile Rental website that lets you choose the type of vehicle as well as the color.  Not all vehicles come in the same color, though, so you might have a form that looks similar to the following:&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;select&lt;/span&gt; &lt;span style="color: #4070a0"&gt;name=&amp;quot;auto&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;id=&amp;quot;auto&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;onchange=&amp;quot;get_vehicle_colors();&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;option&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;-- select a vehicle type --&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;option&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;car&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;Car&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;option&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;truck&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;Truck&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;option&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;motorcycle&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;Motorcycle&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;select&lt;/span&gt; &lt;span style="color: #4070a0"&gt;name=&amp;quot;color&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;id=&amp;quot;color&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;option&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;-- choose a vehicle first--&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;In this example, you would choose the type of automobile you wanted, then employ Ajax to set the appropriate color values for the &lt;em&gt;color&lt;/em&gt; element.&lt;br /&gt;&lt;br /&gt;A Django app that provides this sort of functionality, might have a Model resembling the following (omitting various methods and Meta classes):&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;Color&lt;/span&gt;(models&lt;span style="color: #666666"&gt;.&lt;/span&gt;Model):&lt;br /&gt;    color &lt;span style="color: #666666"&gt;=&lt;/span&gt; models&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;CharField&lt;/span&gt;(max_length&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #40a070"&gt;256&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;Auto&lt;/span&gt;(models&lt;span style="color: #666666"&gt;.&lt;/span&gt;Model):&lt;br /&gt;    &lt;span style="color: #007020"&gt;type&lt;/span&gt; &lt;span style="color: #666666"&gt;=&lt;/span&gt; models&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;CharField&lt;/span&gt;(&lt;span style="color: #4070a0"&gt;&amp;#39;auto type&amp;#39;&lt;/span&gt;, max_length&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #40a070"&gt;256&lt;/span&gt;)&lt;br /&gt;    colors &lt;span style="color: #666666"&gt;=&lt;/span&gt; models&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;ManyToManyField&lt;/span&gt;(Color) &lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Likewise, a form (similar to the one above) could be built with:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;django&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; forms&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;models&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; Color, Auto&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;AutoForm&lt;/span&gt;(forms&lt;span style="color: #666666"&gt;.&lt;/span&gt;Form):&lt;br /&gt;    TYPE_CHOICES &lt;span style="color: #666666"&gt;=&lt;/span&gt; [(&lt;span style="color: #4070a0"&gt;&amp;#39;&amp;#39;&lt;/span&gt;, &lt;span style="color: #4070a0"&gt;&amp;#39;-- choose a type --&amp;#39;&lt;/span&gt;), ] &lt;span style="color: #666666"&gt;+&lt;/span&gt; [(t&lt;span style="color: #666666"&gt;.&lt;/span&gt;type, t&lt;span style="color: #666666"&gt;.&lt;/span&gt;type) &lt;span style="color: #007020; font-weight: bold"&gt;for&lt;/span&gt; t &lt;span style="color: #007020; font-weight: bold"&gt;in&lt;/span&gt; Auto&lt;span style="color: #666666"&gt;.&lt;/span&gt;objects&lt;span style="color: #666666"&gt;.&lt;/span&gt;all()]&lt;br /&gt;    COLOR_CHOICES &lt;span style="color: #666666"&gt;=&lt;/span&gt; [(c&lt;span style="color: #666666"&gt;.&lt;/span&gt;color, c&lt;span style="color: #666666"&gt;.&lt;/span&gt;color) &lt;span style="color: #007020; font-weight: bold"&gt;for&lt;/span&gt; c &lt;span style="color: #007020; font-weight: bold"&gt;in&lt;/span&gt; Color&lt;span style="color: #666666"&gt;.&lt;/span&gt;objects&lt;span style="color: #666666"&gt;.&lt;/span&gt;all()]&lt;br /&gt;    COLOR_CHOICES&lt;span style="color: #666666"&gt;.&lt;/span&gt;insert(&lt;span style="color: #40a070"&gt;0&lt;/span&gt;, (&lt;span style="color: #4070a0"&gt;&amp;#39;&amp;#39;&lt;/span&gt;, &lt;span style="color: #4070a0"&gt;&amp;#39;-- choose a vehicle type first --&amp;#39;&lt;/span&gt;))&lt;br /&gt;    &lt;br /&gt;    &lt;span style="color: #007020"&gt;type&lt;/span&gt; &lt;span style="color: #666666"&gt;=&lt;/span&gt; forms&lt;span style="color: #666666"&gt;.&lt;/span&gt;ChoiceField(choices&lt;span style="color: #666666"&gt;=&lt;/span&gt;TYPE_CHOICES, widget&lt;span style="color: #666666"&gt;=&lt;/span&gt;forms&lt;span style="color: #666666"&gt;.&lt;/span&gt;Select(attrs&lt;span style="color: #666666"&gt;=&lt;/span&gt;{&lt;span style="color: #4070a0"&gt;&amp;#39;onchange&amp;#39;&lt;/span&gt;:&lt;span style="color: #4070a0"&gt;&amp;#39;get_vehicle_color();&amp;#39;&lt;/span&gt;}))&lt;br /&gt;    color &lt;span style="color: #666666"&gt;=&lt;/span&gt; forms&lt;span style="color: #666666"&gt;.&lt;/span&gt;ChoiceField(choices&lt;span style="color: #666666"&gt;=&lt;/span&gt;COLOR_CHOICES)&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Notice the use of the &lt;em&gt;widget&lt;/em&gt; paramter on the Form's &lt;em&gt;type&lt;/em&gt; field.  Django Forms only render the most basic HTML, so in order to set an &lt;em&gt;onchange&lt;/em&gt; attribute for a select element, we have to specify that attribute in &lt;em&gt;attrs&lt;/em&gt;, which is a dictionary of element attribute name/value pairs. More information about customizing Form widgets can be found in Django's &lt;a href="http://docs.djangoproject.com/en/dev/ref/forms/widgets/#ref-forms-widgets"&gt;widget reference&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Another thing to note is the COLOR_CHOICES attribute.  A ChoiceField will validate that any user-submitted content is conteint in its provided &lt;em&gt;choices&lt;/em&gt;.  So, the COLOR_CHOICES must contain all valid colors for an Auto.  However, we make sure the first choice is a default value that will later get updated by our AJAX request.&lt;br /&gt;&lt;br /&gt;For the most part, this form would be used as in any other Django app, but since we're adding in a little Ajax, I include my javascript libraries (in this example, &lt;a href="http://prototypejs.org/"&gt;Prototype&lt;/a&gt;) in the same template as the form.  So, my template code looks something like this:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;extends&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;quot;base.html&amp;quot;&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;block&lt;/span&gt; &lt;span style="color: #bb60d5"&gt;head&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;script &lt;/span&gt;&lt;span style="color: #4070a0"&gt;type=&amp;quot;text/javascript&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;src=&amp;quot;/site_media/prototype.js&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;script &lt;/span&gt;&lt;span style="color: #4070a0"&gt;type=&amp;quot;text/javascript&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;src=&amp;quot;/site_media/my_ajax_function.js&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;endblock&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;block&lt;/span&gt; &lt;span style="color: #bb60d5"&gt;content&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;if&lt;/span&gt; &lt;span style="color: #bb60d5"&gt;form_was_valid&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;        {# ... show whatever... #}&lt;br /&gt;    &lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;else&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;form&lt;/span&gt; &lt;span style="color: #4070a0"&gt;action=&amp;quot;/auto/reserve/&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;method=&amp;quot;POST&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #007020"&gt;{{&lt;/span&gt; &lt;span style="color: #bb60d5"&gt;form.as_ul&lt;/span&gt;&lt;span style="color: #007020"&gt;}}&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;li&amp;gt;&amp;lt;label&lt;/span&gt; &lt;span style="color: #4070a0"&gt;for=&amp;quot;submit&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #d55537; font-weight: bold"&gt;&amp;amp;nbsp;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;input&lt;/span&gt; &lt;span style="color: #4070a0"&gt;type=&amp;quot;submit&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;id=&amp;quot;submit&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;name=&amp;quot;submit&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;Submit&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;/&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;endif&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;endblock&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;There are two items of note, here.  The first is that this template builds on top of a &lt;em&gt;base.html&lt;/em&gt; template which contains my sites layout and definitions for blocks.  The second, is that one of those blocks--head--is inside my page's head element so that I can reference arbitrary javascript files (or CSS if I needed to) in only the templates that need them. It may be a minor note, but including your javascript libraries ONLY when you need them might save you some load-time and bandwidth.&lt;br /&gt;&lt;br /&gt;Now, what does the stuff in the &lt;em&gt;my_ajax_function.js&lt;/em&gt; look like?  When a Django form gets rendered, every form element automatically gets an &lt;em&gt;id&lt;/em&gt; attribute whose value is the name of the field, prefixed by "id_".  So, our type and color Select widgets would have attributes id="id_type" and id="id_color", respectively.&lt;br /&gt;&lt;br /&gt;So what is it that our Javascript needs to do?&lt;ol&gt;&lt;li&gt;When an auto type is selected (determined by the &lt;em&gt;onchange&lt;/em&gt; event, grab the value of that type (&lt;em&gt;$('id_type').getValue()&lt;/em&gt;)&lt;/li&gt;&lt;li&gt;Construct the XMLHttpRequest with the appropriate POST data (the type of Auto chosen--accomplished using the $H shortcut to create a &lt;a href="http://prototypejs.org/api/hash"&gt;Hash&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;Send that back to the webserver (at the appropriate URL, which we've set as &lt;em&gt;/auto/ajax_purpose_staff/&lt;/em&gt;)&lt;/li&gt;&lt;li&gt;Listen for a response from the server, &lt;/li&gt;&lt;li&gt;And if that response contains any text (hopefully a list of available colors), update the select element with that text&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;function&lt;/span&gt; get_vehicle_color(){&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;new&lt;/span&gt; &lt;span style="color: #666666"&gt;A&lt;/span&gt;jax.&lt;span style="color: #666666"&gt;R&lt;/span&gt;equest(&lt;span style="color: #4070a0"&gt;&amp;#39;/auto/ajax_purpose_staff/&amp;#39;&lt;/span&gt;&lt;span style="color: #666666"&gt;,&lt;/span&gt; { &lt;br /&gt;    method&lt;span style="color: #666666"&gt;:&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;#39;post&amp;#39;&lt;/span&gt;&lt;span style="color: #666666"&gt;,&lt;/span&gt;&lt;br /&gt;    parameters&lt;span style="color: #666666"&gt;:&lt;/span&gt; $H({&lt;span style="color: #4070a0"&gt;&amp;#39;type&amp;#39;&lt;/span&gt;&lt;span style="color: #666666"&gt;:&lt;/span&gt;$(&lt;span style="color: #4070a0"&gt;&amp;#39;id_type&amp;#39;&lt;/span&gt;).getValue()})&lt;span style="color: #666666"&gt;,&lt;/span&gt;&lt;br /&gt;    onSuccess&lt;span style="color: #666666"&gt;:&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;function&lt;/span&gt;(transport) {&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold"&gt;var&lt;/span&gt; e &lt;span style="color: #666666"&gt;=&lt;/span&gt; $(&lt;span style="color: #4070a0"&gt;&amp;#39;id_color&amp;#39;&lt;/span&gt;)&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold"&gt;if&lt;/span&gt;(transport.responseText)&lt;br /&gt;            e.update(transport.responseText)&lt;br /&gt;    }&lt;br /&gt;    }); &lt;span style="color: #60a0b0; font-style: italic"&gt;// end new Ajax.Request&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;&lt;/span&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;So now we've got a model and a form (outfitted with some nifty Ajax code, no less), how would we set up a view and a URLconf?  Well, the URLconf works the same as in any other app, so we just have to set an entry that maps to the view that handles the Ajax request.  If the name of this app is &lt;em&gt;auto&lt;/em&gt;, and it lives in a project called &lt;em&gt;mysite&lt;/em&gt;, our URLconf might look like the following:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;urlpatterns &lt;span style="color: #666666"&gt;=&lt;/span&gt; patterns(&lt;span style="color: #4070a0"&gt;&amp;#39;mysite.auto.views&amp;#39;&lt;/span&gt;,&lt;br /&gt;    (&lt;span style="color: #4070a0"&gt;r&amp;#39;^ajax_color_request/$&amp;#39;&lt;/span&gt;, &lt;span style="color: #4070a0"&gt;&amp;#39;ajax_color_request&amp;#39;&lt;/span&gt;),&lt;br /&gt;    &lt;span style="color: #60a0b0; font-style: italic"&gt;# ... everything else...&lt;/span&gt;&lt;br /&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;And it would map our URL (www.example.com/auto/ajax_color_request/) to a view named &lt;em&gt;ajax_color_request&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;Now for the view. Since our Ajax request is sending its data via post, we can pull it from request.POST (which is a dictionary-like object), and then retrieve all the colors associated with a particular type of Auto.&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;ajax_color_request&lt;/span&gt;(request):&lt;br /&gt;    &lt;span style="color: #60a0b0; font-style: italic"&gt;# Expect an auto &amp;#39;type&amp;#39; to be passed in via Ajax and POST&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;if&lt;/span&gt; request&lt;span style="color: #666666"&gt;.&lt;/span&gt;is_ajax() &lt;span style="color: #007020; font-weight: bold"&gt;and&lt;/span&gt; request&lt;span style="color: #666666"&gt;.&lt;/span&gt;method &lt;span style="color: #666666"&gt;==&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;#39;POST&lt;/span&gt;&lt;br /&gt;        auto_type &lt;span style="color: #666666"&gt;=&lt;/span&gt; Auto&lt;span style="color: #666666"&gt;.&lt;/span&gt;objects&lt;span style="color: #666666"&gt;.&lt;/span&gt;filter(&lt;span style="color: #007020"&gt;type&lt;/span&gt;&lt;span style="color: #666666"&gt;=&lt;/span&gt;request&lt;span style="color: #666666"&gt;.&lt;/span&gt;POST&lt;span style="color: #666666"&gt;.&lt;/span&gt;get(&lt;span style="color: #4070a0"&gt;&amp;#39;type&amp;#39;&lt;/span&gt;, &lt;span style="color: #4070a0"&gt;&amp;#39;&amp;#39;&lt;/span&gt;))&lt;br /&gt;        colors &lt;span style="color: #666666"&gt;=&lt;/span&gt; auto_type&lt;span style="color: #666666"&gt;.&lt;/span&gt;colors&lt;span style="color: #666666"&gt;.&lt;/span&gt;all() &lt;span style="color: #60a0b0; font-style: italic"&gt;# get all the colors for this type of auto.&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;return&lt;/span&gt; render_to_response(&lt;span style="color: #4070a0"&gt;&amp;#39;auto/ajax_color_request.html&amp;#39;&lt;/span&gt;, &lt;span style="color: #007020"&gt;locals&lt;/span&gt;())&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Now, all we have to do is send that data back to the client as a snippet of HTML which will get written to the appropriate select element. I've chosen to do this in a very simple template:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;for&lt;/span&gt; &lt;span style="color: #bb60d5"&gt;c&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;in&lt;/span&gt; &lt;span style="color: #bb60d5"&gt;colors&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;option&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;&lt;/span&gt;&lt;span style="color: #007020"&gt;{{&lt;/span&gt; &lt;span style="color: #bb60d5"&gt;c.color&lt;/span&gt; &lt;span style="color: #007020"&gt;}}&lt;/span&gt;&lt;span style="color: #4070a0"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #007020"&gt;{{&lt;/span&gt; &lt;span style="color: #bb60d5"&gt;c.color&lt;/span&gt;&lt;span style="color: #666666"&gt;|&lt;/span&gt;&lt;span style="color: #06287e"&gt;title&lt;/span&gt; &lt;span style="color: #007020"&gt;}}&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;endfor&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;And there you have it.  A simple bit of Ajax in a Django app.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-641240897788708420?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/641240897788708420'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/641240897788708420'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/11/simple-django-example-with-ajax.html' title='A simple Django example with Ajax'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-8329154980397267028</id><published>2008-11-19T12:49:00.005-06:00</published><updated>2008-11-19T13:11:24.763-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Extending Django's MultiWidget: SplitSelectDateTimeWidget</title><content type='html'>This entry is an update to &lt;a href="http://bradmontgomery.blogspot.com/2008/11/selecttimewidget-custom-django-widget.html"&gt;SelectTimeWidget: A custom Django Widget&lt;/a&gt;.  &lt;br /&gt;&lt;br /&gt;The Problem:  I want to use a Single widget object for a DateTimeField, but I want it to consist of select elements with appropriate options for month, day, year, hour, minute, and second.  Additionally, I want to be able to specify a 12-hour format, so I would then need options for "a.m." and "p.m."&lt;br /&gt;&lt;br /&gt;Fortunately, Django's SelectDateWidget (from django.forms.extras.widgets) takes care of the Date portion of this, and I've previously written a similar SelectTimeWidget.  Now, I just need to find some way to appropriately combine the two widgets.&lt;br /&gt;&lt;br /&gt;After a little digging in Django's source code, I found something called a MultiWidget (in django.forms.widgets).  From it's docstring:&lt;blockquote&gt;A widget that is composed of multiple widgets.&lt;/blockquote&gt;Wow! This sounds like JUST what I need!  Luckily, just beneath it is the definition of a SplitDateTimeWidget(MultiWidget), which combines two TextInput widgets for DateTimeFields.  So taking that as an example, I've written the &lt;a href="http://www.djangosnippets.org/snippets/1206/"&gt; SplitSelectDateTimeWidget&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;To Use the SplitSelectDateTimeWidget you might do something similar to this:&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;# Default usage of SplitSelectDateTimeWidget&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;TimeForm&lt;/span&gt;(Form):&lt;br /&gt;    dt &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #007020"&gt;DateTimeField&lt;/span&gt;(widget&lt;span style="color: #666666"&gt;=&lt;/span&gt;SplitSelectDateTimeWidget())&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;A slightly more complex example hooks into the flexibility of the underlying widgets (SelectDateWidget and SelecTimeWidget):&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;TimeForm&lt;/span&gt;(Form)&lt;br /&gt;    dt &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #007020"&gt;DateTimeField&lt;/span&gt;(widget&lt;span style="color: #666666"&gt;=&lt;/span&gt;SplitSelectDateTimeWidget(hour_step&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #40a070"&gt;2&lt;/span&gt;, \&lt;br /&gt;    minute_step&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #40a070"&gt;15&lt;/span&gt;, second_step&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #40a070"&gt;30&lt;/span&gt;, twelve_hr&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #007020"&gt;True&lt;/span&gt;, years&lt;span style="color: #666666"&gt;=&lt;/span&gt;[&lt;span style="color: #40a070"&gt;2008&lt;/span&gt;,&lt;span style="color: #40a070"&gt;2009&lt;/span&gt;,&lt;span style="color: #40a070"&gt;2010&lt;/span&gt;]))&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;The above example displays hours in increments of 2, minutes in increments of 15, and seconds in increments of 30. Likewise, only the years 2008, 2009,and 2010 are displayed in the years' options.&lt;br /&gt;&lt;br /&gt;The output of a form using the SplitSelectDateTimeWidget looks something similar to this:&lt;br/&gt;&lt;br /&gt;&lt;img src="http://bradmontgomery.net/images/datetimeselect.png" alt="SplitSelectDateTimeWidget"/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-8329154980397267028?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/8329154980397267028'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/8329154980397267028'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/11/extending-djangos-multiwidget.html' title='Extending Django&apos;s MultiWidget: SplitSelectDateTimeWidget'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-5947824555473713916</id><published>2008-11-18T19:40:00.008-06:00</published><updated>2010-04-06T06:49:17.387-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>SelectTimeWidget: A custom Django Widget</title><content type='html'>I've been meaning to write this blog post for quite some time...&lt;br /&gt;&lt;br /&gt;Django Models provide a way to create a definitive source of data for web applications.  Written as a python class, a Django Model consists of Fields that (among other things) define a &lt;em&gt;type&lt;/em&gt; for your data.  &lt;br /&gt;&lt;br /&gt;Django's Forms provide a mechanism for generating HTML form elements and validating user input.  A sublcass of Django's Form class is the ModelForm which essentially creates a Form based on the Fields in a Model.  Form Fields and Model Fields are not the same--though there is a &lt;a href="http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#field-types"&gt;mapping from Model fields to Form fields&lt;/a&gt;, however.  The Fields in a Form define the validation logic for the data entered into the html form elements generated by the Field.&lt;br /&gt;&lt;br /&gt;When Django renders a Form, it chooses an appropriate html form element (such as input, textarea, or select elements) based on the type of Fields in the Form.  For example a form to save a URL with an associated title might look something like the following:&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;django&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; forms&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;LinkForm&lt;/span&gt;(forms&lt;span style="color: #666666"&gt;.&lt;/span&gt;Form):&lt;br /&gt;    title &lt;span style="color: #666666"&gt;=&lt;/span&gt; forms&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;CharField&lt;/span&gt;() &lt;br /&gt;    url &lt;span style="color: #666666"&gt;=&lt;/span&gt; forms&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;URLField&lt;/span&gt;()&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;The HTML generated by calling this forms &lt;em&gt;as_table()&lt;/em&gt; method might look something like this:&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #c65d09; font-weight: bold"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;f &lt;span style="color: #666666"&gt;=&lt;/span&gt; LinkForm()&lt;br /&gt;&lt;span style="color: #c65d09; font-weight: bold"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;f&lt;span style="color: #666666"&gt;.&lt;/span&gt;as_table()&lt;br /&gt;&lt;span style="color: #808080"&gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Title:&amp;lt;/th&amp;gt;&amp;lt;td&amp;gt;&amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;title&amp;quot; /&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #808080"&gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Url:&amp;lt;/th&amp;gt;&amp;lt;td&amp;gt;&amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;url&amp;quot;/&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #808080"&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;We can change the behaviour of this output HTML by specifying a specific widget for the Form.  If for some &lt;em&gt;strange&lt;/em&gt; reason we wanted the Title to render in a textarea, we would do the following:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;title &lt;span style="color: #666666"&gt;=&lt;/span&gt; forms&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;CharField&lt;/span&gt;(widget&lt;span style="color: #666666"&gt;=&lt;/span&gt;forms&lt;span style="color: #666666"&gt;.&lt;/span&gt;Textarea)&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;All of this is well-documented in Django's &lt;a href="http://docs.djangoproject.com/en/dev/ref/forms/widgets/"&gt;Widget documentation&lt;/a&gt;, which has been working well for me 80% of the time.&lt;br /&gt;&lt;br /&gt;My big complaint here is that Date and Time information defaults to text input elements.  In the past, I've always liked to use select elements for situations when users would need to enter things like dates (perhaps a different select element for Months, Days, and Years) and time (different select elements for Hours, Minutes...&lt;br /&gt;&lt;br /&gt;Luckily, (as of version 1.0, I think), Django offers a &lt;b&gt;SelectDateWidget&lt;/b&gt; (located in &lt;code&gt;django.forms.extras.widgets&lt;/code&gt;) which accomplishes this very thing.  It produces three select elements, and it can be attached to a Form's DateField.  This is accomplished like so:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;some_date &lt;span style="color: #666666"&gt;=&lt;/span&gt; forms&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;DateField&lt;/span&gt;(widget&lt;span style="color: #666666"&gt;=&lt;/span&gt;SelectDateWidget, initial&lt;span style="color: #666666"&gt;=&lt;/span&gt;datetime&lt;span style="color: #666666"&gt;.&lt;/span&gt;date&lt;span style="color: #666666"&gt;.&lt;/span&gt;today())&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;That's Awesome!  I can even tell the widget to default to "today's" date by passing it an &lt;em&gt;initial&lt;/em&gt; parameter containing the value generated by &lt;em&gt;datetime.date.today()&lt;/em&gt;. Ok... what about times?  Would we ever want to enter Time information as three separate select elements.  I do, so I basically altered the &lt;b&gt;SelectDateWidget&lt;/b&gt;, creating a &lt;a href="http://www.djangosnippets.org/snippets/1202/"&gt;&lt;b&gt;SelectTimeWidget&lt;/b&gt;&lt;/a&gt;, which is available on &lt;a href="http://www.djangosnippets.org/snippets/1202/"&gt;django snippets&lt;/a&gt;. It isn't perfect, but for now, it has accomplished what I've wanted... and its fairly flexible.&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;# Specify a basic 24-hr time Widget (the default)&lt;/span&gt;&lt;br /&gt;t &lt;span style="color: #666666"&gt;=&lt;/span&gt; forms&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;TimeField&lt;/span&gt;(widget&lt;span style="color: #666666"&gt;=&lt;/span&gt;SelectTimeWidget())&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;# Force minutes and seconds to be displayed in increments of 10&lt;/span&gt;&lt;br /&gt;t &lt;span style="color: #666666"&gt;=&lt;/span&gt; forms&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;TimeField&lt;/span&gt;(widget&lt;span style="color: #666666"&gt;=&lt;/span&gt;SelectTimeWidget(minute_step&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #40a070"&gt;10&lt;/span&gt;, second_step&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #40a070"&gt;10&lt;/span&gt;))&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;# Use a 12-hr time format, which will display a 4th select &lt;/span&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;# element containing a.m. and p.m. options)&lt;/span&gt;&lt;br /&gt;t &lt;span style="color: #666666"&gt;=&lt;/span&gt; forms&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;TimeField&lt;/span&gt;(widget&lt;span style="color: #666666"&gt;=&lt;/span&gt;SelectTimeWidget(twelve_hr&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #007020"&gt;True&lt;/span&gt;))&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;However, I often use DateTimeFields in both Models and Forms (which consists of date and time info).  All the code I need to represent select elements for such a field exists in both &lt;b&gt;SelectDateWidget&lt;/b&gt; and &lt;b&gt;SelectTimeWidget&lt;/b&gt;s.  The problem?  I just have figured out a &lt;em&gt;good&lt;/em&gt; (read: proper) way to combine these two Widget classes.&lt;br /&gt;&lt;br /&gt;Do I use multiple inheritance here... mixins... ?  My experience doing this in python is a bit rusty... (erm, non-existent!)  Hopefully the helpful &lt;a href="http://groups.google.com/group/django-users/"&gt;django community&lt;/a&gt; will help me out.  :)&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;UPDATE: This discussion is continued in &lt;a href="http://bradmontgomery.blogspot.com/2008/11/extending-djangos-multiwidget.html"&gt;Extending Django's MultiWidget: SplitSelectDateTimeWidget&lt;/a&gt;.  I found a solution to this problem simply  by digging through Django's source code. Huzzah for Open Source!&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;br /&gt;So if you had a project called &lt;em&gt;mysite&lt;/em&gt; and an app called &lt;em&gt;myapp&lt;/em&gt;, putting it all together would look something like this:&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;# in urls.py&lt;/span&gt;&lt;br /&gt;urlpatterns &lt;span style="color: #666666"&gt;+=&lt;/span&gt; patterns(&lt;span style="color: #4070a0"&gt;&amp;#39;&amp;#39;&lt;/span&gt;,&lt;br /&gt;    (&lt;span style="color: #4070a0"&gt;r&amp;#39;^test/$&amp;#39;&lt;/span&gt;, &lt;span style="color: #4070a0"&gt;&amp;#39;mysite.myapp.views.test&amp;#39;&lt;/span&gt;),&lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;# in forms.py&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;class&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;TestForm&lt;/span&gt;(forms&lt;span style="color: #666666"&gt;.&lt;/span&gt;Form):&lt;br /&gt;    t &lt;span style="color: #666666"&gt;=&lt;/span&gt; forms&lt;span style="color: #666666"&gt;.&lt;/span&gt;&lt;span style="color: #007020"&gt;TimeField&lt;/span&gt;(widget&lt;span style="color: #666666"&gt;=&lt;/span&gt;SelectTimeWidget(minute_step&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #40a070"&gt;15&lt;/span&gt;, second_step&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #40a070"&gt;30&lt;/span&gt;, twelve_hr&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #007020"&gt;True&lt;/span&gt;))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic"&gt;# in views.py&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;test&lt;/span&gt;(request):&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;forms&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; TestForm&lt;br /&gt;    &lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;if&lt;/span&gt; request&lt;span style="color: #666666"&gt;.&lt;/span&gt;method &lt;span style="color: #666666"&gt;==&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;:&lt;br /&gt;        form &lt;span style="color: #666666"&gt;=&lt;/span&gt; TestForm(request&lt;span style="color: #666666"&gt;.&lt;/span&gt;POST)&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold"&gt;if&lt;/span&gt; form&lt;span style="color: #666666"&gt;.&lt;/span&gt;is_valid():&lt;br /&gt;            info &lt;span style="color: #666666"&gt;=&lt;/span&gt; form&lt;span style="color: #666666"&gt;.&lt;/span&gt;cleaned_data&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;else&lt;/span&gt;:&lt;br /&gt;        form &lt;span style="color: #666666"&gt;=&lt;/span&gt; TestForm()&lt;br /&gt;        &lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;return&lt;/span&gt; render_to_response(&lt;span style="color: #4070a0"&gt;&amp;#39;myapp/test.html&amp;#39;&lt;/span&gt;,&lt;br /&gt;                               &lt;span style="color: #007020"&gt;locals&lt;/span&gt;(),&lt;br /&gt;                               context_instance&lt;span style="color: #666666"&gt;=&lt;/span&gt;RequestContext(request))&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;And the test.html Template:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;extends&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;quot;base.html&amp;quot;&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;block&lt;/span&gt; &lt;span style="color: #bb60d5"&gt;content&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;if&lt;/span&gt; &lt;span style="color: #bb60d5"&gt;info&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;pre&amp;gt;&lt;/span&gt;&lt;span style="color: #007020"&gt;{{&lt;/span&gt; &lt;span style="color: #bb60d5"&gt;info&lt;/span&gt; &lt;span style="color: #007020"&gt;}}&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;else&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;form&lt;/span&gt; &lt;span style="color: #4070a0"&gt;action=&amp;quot;&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;method=&amp;quot;post&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #007020"&gt;{{&lt;/span&gt; &lt;span style="color: #bb60d5"&gt;form.as_p&lt;/span&gt; &lt;span style="color: #007020"&gt;}}&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;p&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span style="color: #4070a0"&gt;type=&amp;quot;submit&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;go&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;/&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;endif&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #007020"&gt;{%&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;endblock&lt;/span&gt; &lt;span style="color: #007020"&gt;%}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-5947824555473713916?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/5947824555473713916'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/5947824555473713916'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/11/selecttimewidget-custom-django-widget.html' title='SelectTimeWidget: A custom Django Widget'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-4651340280840269055</id><published>2008-11-17T12:41:00.004-06:00</published><updated>2008-11-24T15:27:54.257-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Soup's On! And it IS Beautiful!</title><content type='html'>Here's the problem: There's a BAJILLIION static html pages sitting out on a server, and I need to migrate all that content to a new Database-driven &lt;abbr title="Content Management System"&gt;CMS&lt;/abbr&gt;.  Additionally, I need to get rid of a lot of non-essential hard-coded presentational markup  (like align="center" or font="whatever") and any inline styles that may exist... (you know, because external CSS is the way to go).&lt;br /&gt;&lt;br /&gt;I &lt;em&gt;could&lt;/em&gt; spend hours and hours just copy-/pasting stuff... but meh.  Enter &lt;a href="http://www.crummy.com/software/BeautifulSoup/"&gt;BeautifulSoup&lt;/a&gt;, &lt;a href="http://www.python.org/doc/2.5.2/lib/module-urllib.html"&gt;urllib&lt;/a&gt;, and &lt;a href="http://www.python.org/doc/2.5.2/lib/module-sqlite3.html"&gt;sqlite&lt;/a&gt;... all glued together with &lt;a href="http://python.org"&gt;python&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Now, for the most part, the content I want to scrape from these static html pages aIso contains links to the other pages I want to keep.  So, here's what we can do, use urlllib to grab the content from the web server, use BeautifulSoup to extract all the values from href attributes in our "a" elements, and keep a list of these (checking to make sure they point to sites we want to keep).  &lt;br /&gt;&lt;br /&gt;Then, I use BeautifulSoup to parse the page, keeping the bits that I want to keep.  Lucky for me, the pages I'm particularly interested in have handy "InstanceBeginEditable" and "InstanceEndEditable" in comments (thanks Dreamweaver), so I just strip out everything before and after that, and parse what's left over.  Of course, this sort of thing is going to be different for everybody, but luckily, parsing &lt;em&gt;X&lt;/em&gt;HTML isn't that difficulty thanks to &lt;a href="http://www.crummy.com/software/BeautifulSoup/documentation.html"&gt;BeautifulSoup's mostly-comprehensive documentation&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So, the part you've been waiting for... Download the Code, try it out, and leave me some feedback!&lt;br /&gt;&lt;big&gt;&lt;a href="http://bradmontgomery.net/files/migrate_web.zip"&gt;http://bradmontgomery.net/files/migrate_web.zip&lt;/a&gt;&lt;/big&gt;&lt;br /&gt;&lt;br /&gt;&lt;a rel="license" href="http://creativecommons.org/licenses/by/3.0/us/"&gt;&lt;img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by/3.0/us/80x15.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span xmlns:dc="http://purl.org/dc/elements/1.1/" href="http://purl.org/dc/dcmitype/InteractiveResource" property="dc:title" rel="dc:type"&gt;migrate_web.py&lt;/span&gt; by &lt;a xmlns:cc="http://creativecommons.org/ns#" href="http://bradmontgomery.net/" property="cc:attributionName" rel="cc:attributionURL"&gt;Brad Montgomery&lt;/a&gt; is licensed under a &lt;a rel="license" href="http://creativecommons.org/licenses/by/3.0/us/"&gt;Creative Commons Attribution 3.0 United States License&lt;/a&gt;.&lt;br /&gt;Based on a work at &lt;a xmlns:dc="http://purl.org/dc/elements/1.1/" href="http://bradmontgomery.net/files/migrate_web.py" rel="dc:source"&gt;bradmontgomery.net&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-4651340280840269055?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/4651340280840269055'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/4651340280840269055'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/11/soups-on-and-it-is-beautiful.html' title='Soup&apos;s On! And it IS Beautiful!'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-3677292020264588556</id><published>2008-11-12T10:17:00.005-06:00</published><updated>2008-11-24T15:28:23.236-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Fun'/><title type='text'>Wordle - Cool Word Clouds</title><content type='html'>I've just run across &lt;a href="http://www.wordle.net"&gt;Wordle&lt;/a&gt;, a nifty app that generates artistic word clouds based on text you give it. It also consumes rss or atom feeds, so you can do something like the image below:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.wordle.net/gallery/wrdl/307886/brad%27s_blog"  title="Wordle: brad&amp;#39;s blog"&gt;&lt;img src="http://www.wordle.net/thumb/wrdl/307886/brad%27s_blog" style="padding:4px;border:1px solid #ddd"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It's quite interesting what sort of things stand out in this... I just happened to notice that &lt;span style="font-size: x-large"&gt;everyone want great web&lt;/span&gt;!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-3677292020264588556?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/3677292020264588556'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/3677292020264588556'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/11/wordle-cool-word-clouds.html' title='Wordle - Cool Word Clouds'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-1728884132620608387</id><published>2008-11-09T18:53:00.007-06:00</published><updated>2008-11-09T19:15:42.643-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BarCampMemphis'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='#bcmem'/><title type='text'>BarCampMemphis, the Day After</title><content type='html'>I think everyone is in agreement:  &lt;a href="http://barcampmemphis.com/"&gt;BarCampMemphis&lt;/a&gt; was a tremendous success!  &lt;br /&gt;&lt;br /&gt;I want to personally thank everyone who came to my &lt;a href="http://bradmontgomery.net/barcampmemphis/"&gt;Zero-to-Wiki in 30 minutes with Django&lt;/a&gt; session, and I also want to remind everyone that the Presentation and the code is freely available!&lt;br /&gt;&lt;br /&gt;There were many great sessions available throughout the day, but I want to list the ones I got to attend... all of which were just excellent:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.linkedin.com/in/davebarger"&gt;Dave Barger&lt;/a&gt;'s &lt;a href="http://www.linkedin.com/"&gt;LinkedIn&lt;/a&gt; tips&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Will Murphy's insights on what's changing on the web&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Scott Brown's Ruby on Rails demo&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The great thoughts on Game Design offered by Mike Rowe, Scott Brown, and Kyle Dempsey&lt;/li&gt;&lt;br /&gt;&lt;li&gt;New insights on Etiquette in Social media moderated by &lt;a href="http://www.spencerdillard.com/"&gt;Spencer Dillard&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;And thanks to &lt;a href="http://tollie.org/"&gt;Tollie Williams&lt;/a&gt; for a great list of iPhone Apps...&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Relive the action as it happened, thanks to Zannel:  &lt;a href="http://www.zannel.com/bkmontgomery"&gt;http://www.zannel.com/bkmontgomery&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-1728884132620608387?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/1728884132620608387'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/1728884132620608387'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/11/barcampmemphis-day-after.html' title='BarCampMemphis, the Day After'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-7042306767709574258</id><published>2008-10-22T16:41:00.005-05:00</published><updated>2008-10-31T10:14:21.308-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BarCampMemphis'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='#bcmem'/><title type='text'>A topic for BarCampMemphis</title><content type='html'>I've recently watched the &lt;a href="http://memphisbarcamp.pbwiki.com/BarCamp+Memphis+-+Amy"&gt;What is BarCamp&lt;/a&gt; video, and I've been thinking about a &lt;a href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt;-related topic.  I'm sure there are plenty of web developers out there looking for &lt;big&gt;a better way&lt;/big&gt;...&lt;br /&gt;&lt;br /&gt;Now, I've only been using Django for about 5 months, but I'm pretty sure I could give any interested kindred souls a good jump-start.  And so that's what I'd like to do... &lt;big&gt;show by example&lt;/big&gt;.&lt;br /&gt;&lt;br /&gt;The Example?  Why not a wiki... It's simple.  (blog examples have been overdone)&lt;br /&gt;&lt;br /&gt;So there it is... Anyone interested in &lt;big&gt;&lt;b&gt;0-to-wiki in 30 minutes&lt;/b&gt;&lt;/big&gt;?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-7042306767709574258?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7042306767709574258'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7042306767709574258'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/10/topic-for-barcampmemphis.html' title='A topic for BarCampMemphis'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-7285899308137797208</id><published>2008-10-05T13:22:00.004-05:00</published><updated>2009-03-25T15:45:49.299-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='beard'/><category scheme='http://www.blogger.com/atom/ns#' term='Fun'/><title type='text'>on beards</title><content type='html'>I just ran across an interesting &lt;a href="http://www.vimeo.com/1819723"&gt;beard video on vimeo&lt;/a&gt;.  Lots of cool links including &lt;a href="http://www.beards.org/"&gt;beards.org&lt;/a&gt;, the &lt;a href="http://www.worldbeardchampionships.com/"&gt;World Beard and Moustache Championships&lt;/a&gt;, and the &lt;a href="http://www.beardliberationfront.com/"&gt;BLF&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;Fun stuff.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-7285899308137797208?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7285899308137797208'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7285899308137797208'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/10/on-beards.html' title='on beards'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-2694001442361173820</id><published>2008-09-23T19:31:00.003-05:00</published><updated>2008-09-23T19:33:51.476-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hack'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='Hardware'/><category scheme='http://www.blogger.com/atom/ns#' term='Fun'/><title type='text'>hack your iphone earbuds...</title><content type='html'>&lt;p&gt;I've been complaining (inwardly) about my iPhone's earbuds since I first tried them on.  Who knew it was so easy to just hack them!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.youtube.com/watch?v=yLzs5aHv3jI"&gt;iPhone / iPod Touch Headphone / Earbud Replacement&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-2694001442361173820?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2694001442361173820'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2694001442361173820'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/09/hack-your-iphone-earbuds.html' title='hack your iphone earbuds...'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-9033636319984044555</id><published>2008-09-16T13:48:00.004-05:00</published><updated>2008-09-16T13:59:31.925-05:00</updated><title type='text'>iTunes Genius Lies!</title><content type='html'>&lt;div style="float: left; padding: 1em; margin: 1em;"&gt;&lt;img src="http://bradmontgomery.net/images/topsongsyourmissing" alt="Top Songs Im missing?"/&gt;&lt;/div&gt; &lt;br /&gt;&lt;br /&gt;I begrudgingly turned on the &lt;a href="http://www.apple.com/itunes/whatsnew/"&gt;Genius Sidebar in iTunes 8&lt;/a&gt; and now it's showing me the "Top Songs You're Missing"... except, I'm not missing them!  Surely Apple doesn't think I should purchase music that's already in my library!  &lt;br /&gt;&lt;br /&gt;Maybe this is just a bug... or maybe iTunes is confuzed because my album is "Weezer (Blue Album)"... who knows.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://bradmontgomery.net/images/wezzer_albums.png"&gt;&lt;img src="http://bradmontgomery.net/images/wezzer_albums.png" alt="I like Weezer"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-9033636319984044555?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/9033636319984044555'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/9033636319984044555'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/09/itunes-genius-lies.html' title='iTunes Genius Lies!'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-6470669501196876188</id><published>2008-09-13T10:23:00.004-05:00</published><updated>2008-09-13T11:08:26.617-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hardware'/><category scheme='http://www.blogger.com/atom/ns#' term='Fun'/><category scheme='http://www.blogger.com/atom/ns#' term='robots'/><title type='text'>Pondering a Robotic Lawn Mower</title><content type='html'>Last weekend my gasoline powered lawn mower died. This prompted a search for a new mower, during which I stumbled upon some of &lt;a href="http://www.friendlyrobotics.com/"&gt;Friendly Robotics' Robomow&lt;/a&gt;; a line of battery-powered robotic lawn mowers.  Now, I have to admit that for the past 15 years or so, every time I've mowed a lawn, I've thought about the possibilities of a robotic lawn mower, and I'm not at all surprised that there's a company producing these.  At first the Robomow seems like a really nice product, but it does a have a couple of major drawbacks that would prevent me from purchasing one: &lt;ol&gt;&lt;li&gt;Cost: it's at least $2500.  I just can't afford that&lt;/li&gt;&lt;li&gt;It requires a low-voltage perimeter wire.&lt;/li&gt;&lt;li&gt;It takes a huge, heavy-looking battery.&lt;/li&gt;&lt;/ol&gt; The first problem is a big one for me (i'm broke), though it's a completely uninteresting problem.&lt;br /&gt;&lt;br /&gt;The second problem is the one that really irks me though.  The Robomow requires that you install a low-voltage perimeter wire around your yard.  Now... if you have a rectangular yard with nothing it it, this may not be that big a deal, but if you have trees or any other obstacles (flowers, gazebos, tool sheds, playground equipment, etc), laying that wire is going to be a lot of work. It just seems like there's enough technology out there that would allow a robotic lawn mower to navigate a yard in a much smarter fashion. If I were to build a robotic mower it just seems that there are a number of options that would allow it to navigate a lawn without the perimeter wire.  Now I'm just thinking out loud here, and putting this all into one machine may be cost-prohibitive, but if I were to build my own robotic lawn mower, these are the things I would consider: &lt;ul&gt;&lt;li&gt;GPS: your lawn mower is probably outside. Couldn't a robot "know" its position and stay inside some predefined boundary?&lt;/li&gt;&lt;li&gt;Touch Sensors: why not just let the robot (gently) bump into stuff. Using touch sensors it might be possible to just back up and mow around stuff. This may be especially useful for fenced-in yards.&lt;/li&gt;&lt;li&gt;Light or IR sensors: if for some reason boundary problems could not be solved using GPS, couldn't lasers or IR LEDs be deployed (I'm thinking stakes with battery-powered LEDS on two sides at each corners of the yard) yard)&lt;/li&gt;&lt;/ul&gt;There's got to be a compination of these that would allow a robot to navigate obstacles without using a perimeter wire.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I don't have a solution for the third problem.  I like the idea of a quiet electric motor, but batteries are heavy, and (in my opinion) just aren't ideal for a piece of lawn equipment.  For now, a small, efficient gasoline engine (small like those in a hand-held weed-eater... I'm not sure how efficient those are, though) might be the best way to go.  Solar power may also be a possbility.&lt;br /&gt;&lt;br /&gt;I'm also thinking a &lt;a href="http://en.wikipedia.org/wiki/Lawn_mower#Reel_.28cylinder.29_mowers"&gt;reel mower&lt;/a&gt; might be a better way to actually cut the grass.  Could a small motor attached to each wheel provide enough control/power/propulsion to make this work?&lt;br /&gt;&lt;br /&gt;(I just bought a &lt;a href="http://www.sears.com/shc/s/p_10153_12605_07137619000P?mv=rr"&gt;Craftsman Reel mower&lt;/a&gt;, so I'm curious to see how well it works...)&lt;br /&gt;&lt;br /&gt;I guess there's also a number of other significant questions that may need to be addressed when considering robotic lawn care.  How do we take care of sticks and leaves?  I have lots of trees, so these a big problem for me.&lt;br /&gt;&lt;br /&gt;I'm beginning to think we're a number of years away from completely robotic lawn care...&lt;br /&gt;&lt;br /&gt;This is a completely unorganized, incompletely thought-out post, but I just wanted to get some ideas out in the open...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-6470669501196876188?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/6470669501196876188'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/6470669501196876188'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/09/pondering-robotic-lawn-mower.html' title='Pondering a Robotic Lawn Mower'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-6785752564543884991</id><published>2008-09-09T06:20:00.002-05:00</published><updated>2008-09-09T06:26:44.127-05:00</updated><title type='text'>Recursively deleting files (based on regex)</title><content type='html'>While we're on the topic... (the topic being recursively doing stuff to files), I often want to delete all the files in a hierachy of directories that match a regular expression (or regex).&lt;br /&gt;&lt;br /&gt;The typical scenario for this is, "I want to delete all of my compiled python files."&lt;br /&gt;&lt;br /&gt;Here's how I do it:&lt;pre&gt;find ./ -type f -name "*.pyc" -exec rm {} \;&lt;/pre&gt;&lt;em&gt;find ./ -type f -name "*.pyc"&lt;/em&gt; should find all files ending in .pyc under the &lt;em&gt;current directory&lt;/em&gt;.  Once they've been found, the &lt;em&gt;-exec rm {} \;&lt;/em&gt; will remove them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-6785752564543884991?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/6785752564543884991'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/6785752564543884991'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/09/recursively-deleting-files-based-on.html' title='Recursively deleting files (based on regex)'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-4179556125303523868</id><published>2008-09-05T19:10:00.002-05:00</published><updated>2008-09-05T19:15:03.503-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Mac'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Recursively Renaming files</title><content type='html'>My hosting provider offers PHP4 and PHP5.  Unfortunately, all files ending in .php get interpreted by PHP4, while all files ending in .php5 get interpreted by PHP5.  So, how do I quickly change all of my files that end in .php to .php5?&lt;br /&gt;&lt;br /&gt;Do a google search for "recursively rename files", and you might run across this:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://seal-7.blogspot.com/2006/12/recursively-rename-files-with-regex-one.html"&gt;http://seal-7.blogspot.com/2006/12/recursively-rename-files-with-regex-one.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;So, to accomplish my task, I use the following:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;find . -type f -print0 | xargs -0 rename 's/.php$/.php5/g'&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Awesomeness.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-4179556125303523868?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/4179556125303523868'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/4179556125303523868'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/09/recursively-renaming-files.html' title='Recursively Renaming files'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-3648293666927933971</id><published>2008-09-05T09:05:00.003-05:00</published><updated>2008-10-31T10:14:49.177-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BarCampMemphis'/><category scheme='http://www.blogger.com/atom/ns#' term='#bcmem'/><title type='text'>Looking Forward to BarCampMemphis</title><content type='html'>&lt;a href="http://barcamp.org/"&gt;BarCamp&lt;/a&gt; is coming to &lt;a href="http://memphisbarcamp.pbwiki.com/FrontPage"&gt;Memphis&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;A project of &lt;a href="http://www.launchmemphis.com/"&gt;LauchMemphis&lt;/a&gt;, this is yet another event focused on growing the local tech industry.  It follows the successful &lt;a href="http://memphis.startupweekend.com/"&gt;Memphis Startup Weekend&lt;/a&gt; (which resulted in &lt;a href="http://spynnr.com/"&gt;Spynnr&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;The theme of BarCampMemphis is &lt;b&gt;FLUX: Your Paradigm&lt;/b&gt;, and from the &lt;a href="http://memphisbarcamp.pbwiki.com/What+is+Barcamp+Memphis "&gt;About BarCampMemphis page&lt;/a&gt;: &lt;br /&gt;&lt;blockquote&gt;In a constantly shifting environment, what do you do?  Entreprenuers and businesses are constantly trying to walk the line between chaos and control, between the cutting edge and solid ground.  Things are in FLUX. Now enter the world of technology; tried and true constants shift quickly and the market gives businesses and entreprenuers a choice: change or fail.  BarCamp Memphis is designed to explore the awesome questions that affect you.  Maybe you will find solid ground amidst shifting paradigms!&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;If you're unfamiliar with the concept of a BarCamp, check out &lt;a href="http://barcamp.org/"&gt;BarCamp.org&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Barcamp"&gt;Wikipedia's BarCamp page&lt;/a&gt;, which starts with:&lt;br /&gt;&lt;blockquote&gt;BarCamp is an international network of user generated conferences — open, participatory workshop-events, whose content is provided by participants — often focusing on early-stage web applications, and related open source technologies, social protocols, and open data formats.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;BarCampMemphis looks to be exciting and informative!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-3648293666927933971?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/3648293666927933971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/3648293666927933971'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/09/looking-forward-to-barcampmemphis.html' title='Looking Forward to BarCampMemphis'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-7935637856182059427</id><published>2008-09-04T15:12:00.007-05:00</published><updated>2008-11-24T15:28:42.720-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Apps'/><category scheme='http://www.blogger.com/atom/ns#' term='OS X'/><category scheme='http://www.blogger.com/atom/ns#' term='Safari'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Safari is Cooler than you Think!</title><content type='html'>When you really start digging into Mac OS X, it's fairly mind-boggling how much &lt;em&gt;extra stuff&lt;/em&gt; it has that your average user never sees.  I recently encountered a problem on my MacBook Pro, where the &lt;a href="http://discussions.apple.com/thread.jspa?messageID=7760823&amp;#7760823"&gt;Optical Audio was overriding my internal speakers&lt;/a&gt;, which prevented me from hearing any audio (without using headphones).  Unfortunately, there's no easy-to-access preference pane to enable or disable various audio devices.  This led me on a search for command-line utilities to manage system preferences.&lt;br /&gt;&lt;br /&gt;(hang on, I'll get to Safari in a second...)&lt;br /&gt;&lt;br /&gt;Eventually I ran into Amit Singh's list of &lt;a href="http://www.kernelthread.com/mac/osx/tools.html"&gt;OS X Hacking tools&lt;/a&gt;, where I stumbled upon the &lt;b&gt;&lt;a href="http://developer.apple.com/documentation/Darwin/Reference/ManPages/man1/defaults.1.html"&gt;defaults&lt;/a&gt;&lt;/b&gt; command.  &lt;b&gt;defaults&lt;/b&gt; is the magic "change any setting for Mac OS X on the command line" tool that you (by you, I mean me) never knew about!&lt;br /&gt;&lt;br /&gt;So, what does this have to do with Safari?  Well, after looking for more information on how to use &lt;b&gt;defaults&lt;/b&gt;, I stumbled upon Mac OS X Tips' &lt;a href="http://www.macosxtips.co.uk/index_files/terminal-commands-for-hidden-mac-os-x-settings.html"&gt;Top 15 Terminal Commands for Hidden Mac OS X Settings&lt;/a&gt;, one of which really caught my attention:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;defaults write com.apple.safari IncludeDebugMenu 1&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This enables a "Debug" Menu ("Develop" menu on Safari 3).  The Developer menu allows you to:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Change Safari's user-agent, so you can masquerade as IE, Firefox, Opera, earlier versions of Safari, or even the iPhone version of Safari&lt;/li&gt;&lt;li&gt;View a Web Inspector - allows you to view scripts, css files, and other information associated with a Web page...&lt;/li&gt;&lt;li&gt;View a network timeline - showing the time it took to download each component linked in a web-page...&lt;/li&gt;&lt;li&gt;Disable stuff - like javascript, css, java, caches...&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;It's not quite the &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/60"&gt;Web Developer plugin&lt;/a&gt; for Firefox, but it does give you additional control over Safari.&lt;br /&gt;&lt;br /&gt;And the Network timeline looks pretty darn cool!&lt;br /&gt;&lt;br /&gt;ps: i never did find a way to disable the optical audio...guess i got side-tracked.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;[Update: 10/05/2008]&lt;/b&gt; I just realized there's an easier way to enable this in the Advanced Preferences Tab... just enable the "Show Develop menu in menu bar" option! (as in the screenshot below)&lt;br /&gt;&lt;br /&gt;&lt;img src="http://bradmontgomery.net/images/Safari_Advanced.png" alt="Show Develop menu in menu bar"/&gt;&lt;br /&gt;&lt;br /&gt;I guess it pays to pay attention to your app's options!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-7935637856182059427?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7935637856182059427'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7935637856182059427'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/09/safari-is-cooler-than-you-think.html' title='Safari is Cooler than you Think!'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-1674615880198012537</id><published>2008-08-13T08:30:00.002-05:00</published><updated>2008-11-24T15:28:57.080-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='networking'/><title type='text'>Net Neutrality</title><content type='html'>&lt;a target="_blank" href="http://foureyedmonsters.com/neutrality/"&gt;&lt;img src="http://foureyedmonsters.com/video_podcast/images/neutrality.jpg"&gt;&lt;/a&gt;&lt;br/&gt;&lt;a href="http://www.savetheinternet.com/"&gt;Save the Internet&lt;/a&gt; | &lt;a href="http://rockthevote.com/"&gt;Rock the Vote&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-1674615880198012537?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/1674615880198012537'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/1674615880198012537'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/08/net-neutrality.html' title='Net Neutrality'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-1905114992701722468</id><published>2008-08-12T10:32:00.004-05:00</published><updated>2008-11-24T15:29:18.225-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>How to update an input value with the value from a selected option using Prototype</title><content type='html'>Today, I needed to set the value of an HTML input element based on the value of a option in a select element.  This is fairly easy to do with &lt;a href="http://www.prototypejs.org/api/element/writeAttribute"&gt;Prototype's writeAttribute&lt;/a&gt;. Here's an example:&lt;br /&gt;&lt;br /&gt;A simple javascript function to do the work:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;function&lt;/span&gt; populate_input(){&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;var&lt;/span&gt; field &lt;span style="color: #666666"&gt;=&lt;/span&gt; $(&lt;span style="color: #4070a0"&gt;&amp;#39;tf_select&amp;#39;&lt;/span&gt;).getValue(); &lt;br /&gt;    $(&lt;span style="color: #4070a0"&gt;&amp;#39;tf&amp;#39;&lt;/span&gt;).writeAttribute(&lt;span style="color: #4070a0"&gt;&amp;#39;value&amp;#39;&lt;/span&gt;&lt;span style="color: #666666"&gt;,&lt;/span&gt; field);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;A simple HTML snippet to see it in action:&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;p&amp;gt;&amp;lt;select&lt;/span&gt; &lt;span style="color: #4070a0"&gt;id=&amp;quot;tf_select&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;name=&amp;quot;tf_select&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;onchange=&amp;quot;populate_input();&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;option&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;- choose one -&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;option&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;v1&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;value 1&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;option&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;v2&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;gt;&lt;/span&gt;value 2&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/select&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span style="color: #4070a0"&gt;type=&amp;quot;test&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;id=&amp;quot;tf&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;name=&amp;quot;tf&amp;quot;&lt;/span&gt; &lt;span style="color: #4070a0"&gt;value=&amp;quot;&amp;quot;&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold"&gt;/&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;And yes... I know the title of this post is almost longer than the post itself!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-1905114992701722468?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/1905114992701722468'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/1905114992701722468'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/08/today-i-needed-to-set-value-of-html.html' title='How to update an input value with the value from a selected option using Prototype'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-8162317062922827536</id><published>2008-08-11T09:05:00.007-05:00</published><updated>2008-08-18T10:14:12.482-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Apps'/><category scheme='http://www.blogger.com/atom/ns#' term='Video'/><title type='text'>Extracting and Encoding Still images to/from Video files</title><content type='html'>&lt;a href="http://ffmpeg.mplayerhq.hu"&gt;ffmpeg&lt;/a&gt; is a cool tool.&lt;br /&gt;&lt;br /&gt;While it's got many many features and uses, I mostly use it to extract frames (or still images) from a video and create a video out of frames.  While &lt;a href="http://ffmpeg.mplayerhq.hu/faq.html#SEC14"&gt;instructions on how to do this&lt;/a&gt; are posted on their list of FAQs, it's probably worth posting again.&lt;br /&gt;&lt;br /&gt;To extract all frames from a video file named "video.mpg": &lt;pre&gt;ffmpeg -i video.mpg frame%d.jpg&lt;/pre&gt;This will produce a series of JPEG image files named file1.jpg, file2.jpg... file10.jpg, file11.jpg... etc.&lt;br /&gt;&lt;br /&gt;I often like to have a zero prepended to the number associated with a frame.  For exaple, if I knew there were 500 frames in the entire video, I would like to have the first frame named "file001.jpg" instead of "file1.jpg". This can be achieved by specifying the number of digits to use in the output file name (such as 3 in the example below):&lt;pre&gt;ffmpeg -i video.mpg frame%3d.jpg&lt;/pre&gt;Now, to create a video named "movie.mpg" from a series of images named something like "frameX.jpg" (where X is the frame number), you could do something like this:&lt;pre&gt;ffmpeg -f image2 -i frame%d.jpg movie.mpg&lt;/pre&gt;The caveat here is that the input frames MUST be numbered as if they were extracted from the video.  So, if you had zero-padded filenames (as in the case where we used "frame%3d.jpg" to extract images), you'd have to account for that when encoding those images back into a movie.&lt;br /&gt;&lt;br /&gt;For example: &lt;pre&gt;ffmpeg -f image2 -i frame%3d.jpg movie.mpg&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;A Note on image formats: In the example above where we created a video file from a set of still images, the "-f image2" option to ffmpeg specifies a video format.  Available formats can be displayed using the command &lt;pre&gt;ffmpeg -formats&lt;/pre&gt; Note that this is not the same as a &lt;a href="http://en.wikipedia.org/wiki/Container_format_(digital)"&gt;container format&lt;/a&gt; (such as .mpg, .avi, .mov... etc). If this sounds a little confusing, you may want to read up on &lt;a href="http://en.wikipedia.org/wiki/Digital_video#Encoding"&gt;digital video encoding&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-8162317062922827536?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/8162317062922827536'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/8162317062922827536'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/08/extracting-and-encoding-still-images.html' title='Extracting and Encoding Still images to/from Video files'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-2356803099596722527</id><published>2008-08-08T21:39:00.003-05:00</published><updated>2008-08-08T21:50:41.031-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mac'/><category scheme='http://www.blogger.com/atom/ns#' term='Fun'/><title type='text'>The Apple Dock is a Mirror!</title><content type='html'>I guess I'm still a &lt;abbr title=" (my first Mac was the 1st gen Mac mini 1.5Ghz PPC)"&gt;relatively new&lt;/abbr&gt; Mac User, but I consider myself fairly technical and probably more savvy that your typical Mac user (though there are a lot of mac users who are far more savvy than I!)&lt;br /&gt;&lt;br /&gt;&lt;div style="float: right; width: 250px; padding: 5px;"&gt;&lt;img src="http://bradmontgomery.net/images/dock_mirror.png" alt="I can see my cursor in the Dock!"/&gt;&lt;/div&gt;Imagine my surprise when, while working tonight,  I discovered that the Dock in Leopard is a Mirror! Yes, I'd noticed the reflection of the Icons, but tonight, I actually saw my cursor (in Terminal) moving as I typed... that caught my attention! At first I though, "well that's just amazing!"  I had to wonder just how much work went into making that happen.  Then, I discovered that &lt;a href="http://forums.macnn.com/90/mac-os-x/352053/annoying-dock-mirror-removal-in-leopard/"&gt;some people don't like this&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;Oh well.  To each &lt;abbr title="uhm... her?"&gt;his&lt;/abbr&gt; own...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-2356803099596722527?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2356803099596722527'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2356803099596722527'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/08/apple-dock-is-mirror.html' title='The Apple Dock is a Mirror!'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-6208247461343887893</id><published>2008-07-30T15:29:00.003-05:00</published><updated>2008-07-30T15:44:50.847-05:00</updated><title type='text'>PHP is chopping off my Access Memo Fields</title><content type='html'>I've got a few simple web forms that use PHP to read and write to an Access database (running on IIS), and I just spent the last few hours frantically trying to figure out why some of my Memo fields were being truncated around 4000 characters.  &lt;br /&gt;&lt;br /&gt;I know Memo fields "should" be able to contain up to 65536 characters, and I could verify this by inserting data directly into the database.  However, when querying the database through PHP (using odbc), I could only retrieve 4000 characters.&lt;br /&gt;&lt;br /&gt;The culprit?  A tiny little setting called &lt;span style="font-weight: bold; font-size: xx-large;"&gt;odbc.defaultlrl&lt;/span&gt;! (lrl = long read length).  This can be changed in php.ini, or you can use ini_set to modify this setting directly in your php script:&lt;br /&gt;&lt;pre&gt;&amp;lt;?php&lt;br /&gt;ini_set(&amp;#39;odbc.defaultlrl&amp;#39;, 65536);&lt;br /&gt;?&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I found this info on the discussion for the &lt;a href="http://us3.php.net/manual/en/function.odbc-longreadlen.php"&gt;odbc_longreadlen&lt;/a&gt; function.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-6208247461343887893?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/6208247461343887893'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/6208247461343887893'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/07/php-is-chopping-off-my-access-memo.html' title='PHP is chopping off my Access Memo Fields'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-9092702185978828760</id><published>2008-07-16T10:37:00.005-05:00</published><updated>2008-07-16T10:53:29.229-05:00</updated><title type='text'>Django: Generating an Image with PIL</title><content type='html'>I've been reading through the &lt;a href="http://www.djangobook.com/"&gt;Django Book&lt;/a&gt;, and in &lt;a href="http://www.djangobook.com/en/1.0/chapter11/"&gt;chapter 11&lt;/a&gt; they talk about generating non-HTML content (such as PDF files, Images, RSS/Atom Feeds).  They mention using &lt;a href="http://www.pythonware.com/products/pil/"&gt;PIL&lt;/a&gt; to generate images, but they don't give an example.  So, I thought I'd post a simple example View that generates an image.&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight" &gt;&lt;pre&gt;&lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;pil_image&lt;/span&gt;(request):&lt;br /&gt;    &lt;span style="color: #4070a0; font-style: italic"&gt;&amp;#39;&amp;#39;&amp;#39; A View that Returns a PNG Image generated using PIL&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;Image&lt;/span&gt;&lt;span style="color: #666666"&gt;,&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;ImageDraw&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;    size &lt;span style="color: #666666"&gt;=&lt;/span&gt; (&lt;span style="color: #40a070"&gt;100&lt;/span&gt;,&lt;span style="color: #40a070"&gt;50&lt;/span&gt;)             &lt;span style="color: #60a0b0; font-style: italic"&gt;# size of the image to create&lt;/span&gt;&lt;br /&gt;    im &lt;span style="color: #666666"&gt;=&lt;/span&gt; Image&lt;span style="color: #666666"&gt;.&lt;/span&gt;new(&lt;span style="color: #4070a0"&gt;&amp;#39;RGB&amp;#39;&lt;/span&gt;, size) &lt;span style="color: #60a0b0; font-style: italic"&gt;# create the image&lt;/span&gt;&lt;br /&gt;    draw &lt;span style="color: #666666"&gt;=&lt;/span&gt; ImageDraw&lt;span style="color: #666666"&gt;.&lt;/span&gt;Draw(im)   &lt;span style="color: #60a0b0; font-style: italic"&gt;# create a drawing object that is&lt;/span&gt;&lt;br /&gt;                                &lt;span style="color: #60a0b0; font-style: italic"&gt;# used to draw on the new image&lt;/span&gt;&lt;br /&gt;    red &lt;span style="color: #666666"&gt;=&lt;/span&gt; (&lt;span style="color: #40a070"&gt;255&lt;/span&gt;,&lt;span style="color: #40a070"&gt;0&lt;/span&gt;,&lt;span style="color: #40a070"&gt;0&lt;/span&gt;)    &lt;span style="color: #60a0b0; font-style: italic"&gt;# color of our text&lt;/span&gt;&lt;br /&gt;    text_pos &lt;span style="color: #666666"&gt;=&lt;/span&gt; (&lt;span style="color: #40a070"&gt;10&lt;/span&gt;,&lt;span style="color: #40a070"&gt;10&lt;/span&gt;) &lt;span style="color: #60a0b0; font-style: italic"&gt;# top-left position of our text&lt;/span&gt;&lt;br /&gt;    text &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #4070a0"&gt;&amp;quot;Hello World!&amp;quot;&lt;/span&gt; &lt;span style="color: #60a0b0; font-style: italic"&gt;# text to draw&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #60a0b0; font-style: italic"&gt;# Now, we&amp;#39;ll do the drawing: &lt;/span&gt;&lt;br /&gt;    draw&lt;span style="color: #666666"&gt;.&lt;/span&gt;text(text_pos, text, fill&lt;span style="color: #666666"&gt;=&lt;/span&gt;red)&lt;br /&gt;    &lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;del&lt;/span&gt; draw &lt;span style="color: #60a0b0; font-style: italic"&gt;# I&amp;#39;m done drawing so I don&amp;#39;t need this anymore&lt;/span&gt;&lt;br /&gt;    &lt;br /&gt;    &lt;span style="color: #60a0b0; font-style: italic"&gt;# We need an HttpResponse object with the correct mimetype&lt;/span&gt;&lt;br /&gt;    response &lt;span style="color: #666666"&gt;=&lt;/span&gt; HttpResponse(mimetype&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #4070a0"&gt;&amp;quot;image/png&amp;quot;&lt;/span&gt;)&lt;br /&gt;    &lt;span style="color: #60a0b0; font-style: italic"&gt;# now, we tell the image to save as a PNG to the &lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #60a0b0; font-style: italic"&gt;# provided file-like object&lt;/span&gt;&lt;br /&gt;    im&lt;span style="color: #666666"&gt;.&lt;/span&gt;save(response, &lt;span style="color: #4070a0"&gt;&amp;#39;PNG&amp;#39;&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #007020; font-weight: bold"&gt;return&lt;/span&gt; response &lt;span style="color: #60a0b0; font-style: italic"&gt;# and we&amp;#39;re done!&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;This example just draws simple text, but the drawing code could be replaced by something more elaborate such as code that opens and scales existing images.&lt;br /&gt;&lt;br /&gt;A note of concern:  The Image object (im) &lt;b&gt;MUST&lt;/b&gt; be saved as a PNG for this to work.  Luckily, the Image.save method expects a file-like object as its first parameter, so we can use django's HttpResponse object here.  Also, the example above doesn't make use of any particular Font for the drawing, so if you do want to draw text, you'll want to take a look at PIL's &lt;a href="http://www.pythonware.com/library/pil/handbook/imagedraw.htm"&gt;ImageDraw Documentation&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;(on a side note: the html for the code above was generated using &lt;a href="http://dpaste.com"&gt;dpaste&lt;/a&gt;, which rocks!)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-9092702185978828760?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/9092702185978828760'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/9092702185978828760'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/07/django-generating-image-with-pil.html' title='Django: Generating an Image with PIL'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-4197258512698216949</id><published>2008-07-15T10:57:00.006-05:00</published><updated>2008-11-24T15:29:34.852-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>The Structure of a Django App</title><content type='html'>&lt;a href="http://bradmontgomery.blogspot.com/2008/06/lions-tigers-and-web-development.html"&gt;Previously&lt;/a&gt;, I'd lamented the difficultly present in choosing an web development framework.  I'd worked through several &lt;a href="http://www.symfony-project.org/"&gt;symfony&lt;/a&gt; tutorials, and though I could see the benefits down the road, it just didn't feel right to me (yes... "feel" is a technical drawback).&lt;br /&gt;&lt;br /&gt;So, I checked out a copy of &lt;a href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt;, and I haven't looked back.  If you're the least bit proficient with python, and you need to build a database-driven web site, USE DJANGO!  They have superb &lt;a href="http://www.djangoproject.com/documentation/"&gt;documentation&lt;/a&gt;, a free book (the &lt;a href="http://djangobook.com/"&gt;The Django Book&lt;/a&gt;), and there's a built-in development server included so you don't &lt;b&gt;have&lt;/b&gt; to get Apache and mod-python (or some other webserver) running somewhere before you can start writing code. &lt;br /&gt;&lt;br /&gt;The fist thing that really made me like Django was the structure of it.  First of all, a site is organized as a project.  Multiple projects may be set up for different websites.  Inside a project, you build an App. (Note that a project may have one or more Apps).&lt;br /&gt;&lt;br /&gt;The App is where you do most of your work, it really consists of four parts:&lt;ol&gt;&lt;li&gt;Models&lt;/li&gt;&lt;li&gt;Views&lt;/li&gt;&lt;li&gt;The URLconf&lt;/li&gt;&lt;li&gt;Template(s)&lt;/li&gt;&lt;/ol&gt;  &lt;br /&gt;&lt;br /&gt;&lt;b&gt;Models&lt;/b&gt; (models.py) define your data.  Instead of building tables in your database, you build python classes in your model.  Django will give you the SQL to create your database tables.  Now, what's really cool, is Django's Automatically generated Admin interface.  All you really need is the model, and the Admin interface provides a password-protected, web-based, interface to your data.  &lt;br /&gt;&lt;br /&gt;&lt;b&gt;Views&lt;/b&gt; (views.py) are essentially functions that extract the correct data based on some (or no) input.  Some may think this is an over-simplification, but in Django, the View is the "view of your data".  The functions in your View will pass data back to the Templates where they are displayed.&lt;br /&gt;&lt;br /&gt;The &lt;b&gt;URLconf&lt;/b&gt; (urls.py) is magic. Well, it sort of seems that way, but it just uses regular expressions to match urls and pass data to the View.  So, "out of the box", Django supports pretty URLs so there's no need to do any url rewriting to get them.&lt;br /&gt;&lt;br /&gt;Last (but not least) are the &lt;b&gt;Templates&lt;/b&gt;.  Django sports it's own template language, and apparently it also supports a host of pre-existing template engines.  I personally have never used existing template engines, so this is all a bit new to me.  However, the Django template language appears both concise and powerful.&lt;br /&gt;&lt;br /&gt;Now, there's a bit more to it that what I've described here, but if you're wondering about Django (like I was), go ahead and check out &lt;a href="http://www.djangoproject.com/documentation/overview/"&gt;their overview&lt;/a&gt;, skim &lt;a href="http://www.djangoproject.com/documentation/install/"&gt;the installaltion guide&lt;/a&gt;, and then start working &lt;a href="http://www.djangoproject.com/documentation/tutorial01/"&gt;the tutorial&lt;/a&gt;.  I did, and I haven't looked back.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-4197258512698216949?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/4197258512698216949'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/4197258512698216949'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/07/structure-of-django-app.html' title='The Structure of a Django App'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-2125746792493164819</id><published>2008-07-12T10:12:00.004-05:00</published><updated>2008-07-12T10:17:03.314-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hardware'/><category scheme='http://www.blogger.com/atom/ns#' term='Fun'/><title type='text'>Why Bluetooth headsets didn't catch on in the '90s</title><content type='html'>&lt;span style="text-align: center; margin-left: auto; margin-right: auto;"&gt;&lt;img src="http://bradmontgomery.net/images/1990s_bluetooth.png" alt="Why bluetooth headsets did not catch on in the 90s"/&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-2125746792493164819?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2125746792493164819'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2125746792493164819'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/07/why-bluetooth-headsets-didnt-catch-on.html' title='Why Bluetooth headsets didn&apos;t catch on in the &apos;90s'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-2733020526160702993</id><published>2008-07-11T11:09:00.003-05:00</published><updated>2008-07-11T11:23:08.774-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Apps'/><category scheme='http://www.blogger.com/atom/ns#' term='Games'/><category scheme='http://www.blogger.com/atom/ns#' term='pyglet'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Games for Kids: dodger</title><content type='html'>I've always thought it would be fun to build video games, and when my daughters were born, I knew I would eventually want to build some simple games that they could enjoy while they're young.  My original attempt at this lead me to &lt;a href="http://pygame.org"&gt;pygame&lt;/a&gt;, and the result was a simple alphabet/number game that plopped letters and numbers on the screen (very similar to &lt;a href="http://alphababy.sourceforge.net/"&gt;AlphaBaby&lt;/a&gt;, but not nearly as good!). Unfortunately, I never continued to develop that one.&lt;br /&gt;&lt;br /&gt;Since my oldest daughter (who is now 5) has recently discovered &lt;a href="http://gcompris.net/"&gt;GCompris&lt;/a&gt;, I thought I'd give the game-writing another chance.  This has resulted in &lt;a href="http://bradmontgomery.net/show.php?page=project_kidgames"&gt;dodger&lt;/a&gt;, which is also my first attempt at using &lt;a href="http://pyglet.org/"&gt;pyglet&lt;/a&gt;. I know it can't compete with GCompris, but hopefully my daughter will like it. &lt;br /&gt;&lt;br /&gt;I've released it under a &lt;a href="http://creativecommons.org/"&gt;CC&lt;/a&gt; license, so feel free to grab a copy, play it, use it, learn from it or whatever... Enjoy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-2733020526160702993?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2733020526160702993'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2733020526160702993'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/07/games-for-kids-dodger.html' title='Games for Kids: dodger'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-1289332286105916934</id><published>2008-06-25T08:29:00.005-05:00</published><updated>2008-06-25T10:00:09.380-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Fink'/><category scheme='http://www.blogger.com/atom/ns#' term='OS X'/><category scheme='http://www.blogger.com/atom/ns#' term='Mac'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Mac OS X, Python, and Fink: Playing Nicely Together</title><content type='html'>Since upgrading to Leopard, I've been using Mac's default install of Python (which is 2.5.1).  For the most part it's worked well for me, namely because I've installed additionaly packages either using Mac installers or through easy_install.  I recently needed to install python-ldap which didn't work using easy_install.  So, I turned to &lt;a href="http://finkproject.org/"&gt;Fink&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Unfortunately, Fink wanted to install it's own version of python (2.5.2) as well.  (I say unfortunately only because I now how two separate python installations, which I think is a bit redundant.)  So, the problem is this:  How do I make the Mac's System python play nicely with any python modules I install through Fink?&lt;br /&gt;&lt;br /&gt;My solution to this problem is to add the Fink python's site-packages directory to my PYTHONPATH.  I do this by setting an environment variable in my &lt;em&gt;.profile&lt;/em&gt; file located in my home directory.  I added the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PYTHONPATH="${PYTHONPATH}:/sw/lib/python2.5/site-packages"&lt;br /&gt;export PYTHONPATH&lt;/pre&gt;&lt;br /&gt;Now, when I run the python interpreter, I can import packages that were installed the Mac way, or through Fink.  Hopefully, this won't give me any problems.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-1289332286105916934?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/1289332286105916934'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/1289332286105916934'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/06/mac-os-x-python-and-fink-playing-nicely.html' title='Mac OS X, Python, and Fink: Playing Nicely Together'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-8785207291593859662</id><published>2008-06-17T20:05:00.004-05:00</published><updated>2008-11-24T15:29:48.885-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Lions, Tigers, and Web Development Frameworks, oh my!</title><content type='html'>Apparently I've stumbled upon a problem that has recently faced many web developers.  That is, I would like to adopt an &lt;a href="http://www.google.com/search?q=+open-source+web+development+framework&amp;ie=utf-8&amp;oe=utf-8&amp;aq=t&amp;rls=com.ubuntu:en-US:unofficial&amp;client=firefox-a"&gt;open-source web development framework&lt;/a&gt; for mid-sized project. Well... searching that phrase only yields 200,000+ results.  So how does one choose?&lt;br /&gt;&lt;br /&gt;I guess Ruby on Rails sparked the whole "Web Development Framework" movement (among other things). I've typically used PHP for my web-based projects in the past, but over the last 2 years, I've also become a huge fan of Python.  I'm not opposed to learning a whole new language (in fact, I'm quite for it!), but for this particular project, I'd like to stick with something I already know quite well.  So, that limits my languages to PHP and Python.&lt;br /&gt;&lt;br /&gt;Additionally, I would like that the framework support multiple databases on the back-end (especially SQLite!), and that there not be huge additional requirements to get things up and running.  I would also like it to integrate nicely with &lt;a href="http://prototypejs.org"&gt;Prototype&lt;/a&gt;, and, if it works on Mac OS X, that a huge PLUS!&lt;br /&gt;&lt;br /&gt;So, for the PHP side of things, my top three candidates were:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="http://www.symfony-project.org/"&gt;symfony&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="http://www.xisc.com/"&gt;prado&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.cakephp.org/"&gt;CakePHP&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;I've already started working through symfony's excellent tutorials, and there are two main reasons that I chose symfony to start with:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;An &lt;a href="http://www.symfony-project.org/book/1_0/"&gt;online book&lt;/a&gt; (and excellent Documentation)&lt;/li&gt;&lt;li&gt;An easy-to-use &lt;a href="http://www.symfony-project.org/installation/1_0"&gt;sandbox&lt;/a&gt;.  This lets me get started quickly with it.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;I've still got a soft spot in my heart for Python, and eventually, I'd like to let it into my Web Development world.  I've read good things about both &lt;a href="http://turbogears.org/"&gt;TurboGears&lt;/a&gt; and &lt;a href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt;.  Interestingly enough, Django comes bundled with its own (lightweight) web server (for testing purposes), so it's a great candidate for anyone wanting a framework with little overhead...  It will probably be my overall number 2 choice.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-8785207291593859662?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/8785207291593859662'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/8785207291593859662'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/06/lions-tigers-and-web-development.html' title='Lions, Tigers, and Web Development Frameworks, oh my!'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-8540889042235705913</id><published>2008-05-24T07:59:00.004-05:00</published><updated>2008-11-19T04:31:59.418-06:00</updated><title type='text'>Folding@Home</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_FnxSpwZnwaA/SDgSGwWGsTI/AAAAAAAAAA0/h4wEFSKHASs/s1600-h/FAHlogoButton.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_FnxSpwZnwaA/SDgSGwWGsTI/AAAAAAAAAA0/h4wEFSKHASs/s200/FAHlogoButton.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5203929276692738354" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I've recently started running &lt;a href="http://folding.stanford.edu/"&gt;Folding@Home&lt;/a&gt;.  This project (out of Stanford) has been around for a number of years, and apparently they're still going strong!  They simulate the way that proteins fold (or assemble themselves t) in order to investigate diseases that occur when the proteins don't fold correctly.  Check out their link for more info.&lt;br /&gt;&lt;br /&gt;Anyways, I've got these computers, so why not do something good with them while I'm not using them!  Here are my &lt;a href="http://fah-web.stanford.edu/cgi-bin/main.py?qtype=userpage&amp;username=bradmontgomery"&gt;Stats&lt;/a&gt;!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-8540889042235705913?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/8540889042235705913'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/8540889042235705913'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/05/foldinghome.html' title='Folding@Home'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_FnxSpwZnwaA/SDgSGwWGsTI/AAAAAAAAAA0/h4wEFSKHASs/s72-c/FAHlogoButton.jpg' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-6453986640460085337</id><published>2008-05-20T10:01:00.003-05:00</published><updated>2008-06-17T21:04:03.779-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Fun'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='Image'/><title type='text'>pyCropper</title><content type='html'>Occasionally, I need to crop some images.  I suspect this happens to a lot of people, and they simply use their favorite photo editor to perform the task.  For a few images, this works just fine.  Plenty of people have &lt;a href="www.adobe.com/products/photoshop"&gt;Photoshop&lt;/a&gt;, the &lt;a href="http://www.gimp.org"&gt;Gimp&lt;/a&gt;, or even &lt;a href="www.apple.com/iphoto/"&gt;iPhoto&lt;/a&gt;.  &lt;br /&gt;&lt;br /&gt;This past weekend, however, I found myself needing to crop about 1000 images, and I need to do it quickly.  This was more than my machine could load into the Gimp at once.  Even loading 100 at a time was a bit slow for me.  So I set out to find some simple tool that would let me JUST crop images.  I found a few nifty web-based tools (&lt;a href="http://www.picresize.com/"&gt;picresize.com&lt;/a&gt; and &lt;a href="http://www.resize.it/"&gt;resize.it&lt;/a&gt;) that would allow me to easily crop ONE image... but that's not really what I needed.&lt;br /&gt;&lt;br /&gt;So, when I couldn't find what I needed, I wrote my own, and it's now available under the &lt;a href="http://www.gnu.org/licenses/gpl.html"&gt;GPLv3&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;If you find yourself needing to crop a large number of images, head on over to the &lt;a href="http://bradmontgomery.net/pycropper/"&gt;pyCropper page&lt;/a&gt;.  Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-6453986640460085337?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/6453986640460085337'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/6453986640460085337'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/05/pycropper.html' title='pyCropper'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-1071652847642456889</id><published>2008-05-02T19:02:00.006-05:00</published><updated>2008-05-05T18:41:29.716-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Fun'/><category scheme='http://www.blogger.com/atom/ns#' term='Food'/><title type='text'>A Go at California Rolls</title><content type='html'>Well, I've tried to make some California rolls.  I think they turned out OK.  Not the prettiest rolls, but they tasted good... maybe I shouldn't have eaten them all, though!&lt;br /&gt;&lt;br /&gt;&lt;img style="margin: 0.5em; float:left;" src="http://bradmontgomery.net/files/rolled_california_rolls.jpg" alt="rolled up california rolls"/&gt;&lt;br /&gt;&lt;br /&gt;&lt;img style="margin: 0.5em; float:left;" src="http://bradmontgomery.net/files/finished_california_rolls.jpg" alt="finished california rolls"/&gt;&lt;br /&gt;&lt;br /&gt;These were made with the following ingredients: &lt;ul&gt;&lt;li&gt;1 cup of rice. (most recipes call for short grain rice, but I used long grain rice)&lt;/li&gt;&lt;li&gt;1Tbsp of Rice vinegar&lt;/li&gt;&lt;li&gt;dash of salt&lt;/li&gt;&lt;li&gt;Nori&lt;/li&gt;&lt;li&gt;Carrots&lt;/li&gt;&lt;li&gt;Cucumber&lt;/li&gt;&lt;li&gt;Imitation crab meat (or real crab meat if you have it)&lt;/li&gt;&lt;li&gt;1 Avacado&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Boil 1 and 3/4 cups of water and salt.  Once the water has come to a boil, add the rice, cover, and reduce to a simmer for 15 minutes. While simmering, julienne (or slice very thinly) carrots and cucumber.  Chop crabmeat.&lt;br /&gt;&lt;br /&gt;Once rice has finished, mix in the rice vinegar, and let cool for 10 minutes.  Spread a thin layer of rice on the Nori, and top the rice with a small amount of carrots, cucumber, and crab, then roll tightly.  Slice into thin pieces!&lt;br /&gt;&lt;br /&gt;Enjoy with wasabi, soy sauce and pickled ginger!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-1071652847642456889?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/1071652847642456889'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/1071652847642456889'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/05/go-at-california-rolls.html' title='A Go at California Rolls'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-6224135340274907494</id><published>2008-04-13T19:40:00.004-05:00</published><updated>2008-04-13T19:48:59.750-05:00</updated><title type='text'>Review: Beginning Game Development with Python and Pygame</title><content type='html'>It's been out for a while now, and I'd always meant to write a review for this book... I pre-ordered it, and I've had it for a&lt;br /&gt;while, but I haven't worked through the whole thing (though I've hit most of the latter chapters).  Actually, this is not really a review, per se... just my succinct list of pros and cons.&lt;br /&gt;&lt;br /&gt;So, without further ado:&lt;br /&gt;&lt;br /&gt;Pros:&lt;ul&gt;&lt;li&gt;Very easy to read.&lt;/li&gt;&lt;li&gt;Great examples that actually work&lt;/li&gt;&lt;li&gt;Chapters 1 &amp; 2 give a great intro to python, so this would actually be a good book if you've never touched python before (but did have some programming experience)&lt;/li&gt;&lt;li&gt;Lots of info using pygame+opengl&lt;/li&gt;&lt;li&gt;A lot of examples use his &lt;a href="http://www.willmcgugan.com/game-objects/"&gt;gameobjects&lt;/a&gt; library, so a lot of the grunt-work coding is available to use already.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Cons:&lt;ul&gt;&lt;li&gt;My biggest complaint is the lack of discussion on Sprites. Pygame is really a 2D library, and I think he left out a lot of very important information by not discussing how the Sprite class can be used.&lt;/li&gt;&lt;li&gt;Only cursory discussion Sound.  If you're writing a game and just want the basic sound effects and/or background music, this is ok. However, if you want to do something really interesting with sound, you'll need to dig way beyond what this book offers.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Overall, I think the book is worth getting unless you've already done&lt;br /&gt;a few significant projects using pygame.  It's definately an Intro&lt;br /&gt;book, and it does a really good job giving the user an idea how to put&lt;br /&gt;a project together.&lt;br /&gt;&lt;br /&gt;Disclaimer: I'm a relative noob to pygame (~10 months), and I've been&lt;br /&gt;using pygame for research projects in Augmented Reality (not&lt;br /&gt;necessarily games) for both visual and non-visual (sound-only)&lt;br /&gt;applications.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-6224135340274907494?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/6224135340274907494'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/6224135340274907494'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/04/review-beginning-game-development-with.html' title='Review: Beginning Game Development with Python and Pygame'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-1740503720891335819</id><published>2008-03-04T10:14:00.005-06:00</published><updated>2008-03-04T10:28:56.427-06:00</updated><title type='text'>Google blocking automated searches?</title><content type='html'>This morning I was writing a little python, and I used Firefox's Google toolbar to search for &lt;a href="http://sorry.google.com/sorry/?continue=http://www.google.com/search%3Fq%3Disalnum%26ie%3Dutf-8%26oe%3Dutf-8%26aq%3Dt%26rls%3Dcom.ubuntu:en-US:official%26client%3Dfirefox-a"&gt;isalnum&lt;/a&gt;.  The strange thing about this, though, is that Google didn't do the search!  Instead they gave me a message saying:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;We're sorry...but your query looks similar to automated requests from a computer virus or spyware application. To protect our users, we can't process your request right now.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Luckily I could type in a &lt;a href="http://en.wikipedia.org/wiki/Captcha"&gt;captcha&lt;/a&gt; and continue the search. This really caught me offguard, though, so I decided to experiment a little.  I had been logged in to my &lt;a href="http://gmail.com"&gt;gmail&lt;/a&gt; account t the time, so I tried clearing the cookies and cache from my browser.  I searched for "python isapha", and I got the same "We're sorry..." message.  But this time, &lt;span style="font-weight:bold;"&gt;there was no captcha!&lt;/span&gt; I also tried searching for other terms (including "dogs"), all with same result.  &lt;br /&gt;&lt;br /&gt;So what happens if I don't have a Google account?  I suddenly can't use their search engine?&lt;br /&gt;&lt;br /&gt;So this got me thinking... Is the state of malware so bad that we'll eventually have to validate our identity before using a search engine?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-1740503720891335819?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/1740503720891335819'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/1740503720891335819'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/03/google-blocking-automated-searches.html' title='Google blocking automated searches?'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-222154634554447484</id><published>2008-02-15T18:26:00.005-06:00</published><updated>2008-02-16T09:52:38.051-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Shrinking Images with Python</title><content type='html'>My wife had a very large image, and she needed some smaller versions of it.  Well, I thought I'd just fire up &lt;a href="http://gimp.org"&gt;The GIMP&lt;/a&gt; and create a few shrunken versions, but by the time The GIMP loaded on my G4 Mac mini, I'd almost finished writing the python coded needed to do the task for me!&lt;br /&gt;&lt;br /&gt;Writing code is good!&lt;br /&gt;&lt;pre&gt;import Image&lt;br /&gt;from sys import argv&lt;br /&gt;&lt;br /&gt;def shrink(filename):&lt;br /&gt;    im = Image.open(filename)&lt;br /&gt;    w,h = im.size&lt;br /&gt;    &lt;br /&gt;    # Create images that are 10%, 20%....90% of the original&lt;br /&gt;    sizes = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9]&lt;br /&gt;    &lt;br /&gt;    for s in sizes:&lt;br /&gt;        nw = int(w*s)&lt;br /&gt;        nh = int(h*s)&lt;br /&gt;        n = im.resize((nw, nh))&lt;br /&gt;        f = "%s_x_%s_%s"%(nw,nh,filename)&lt;br /&gt;        print "Saving %s"%(f)&lt;br /&gt;        n.save(f)&lt;br /&gt;        &lt;br /&gt;if __name__ == "__main__":&lt;br /&gt;    &lt;br /&gt;    if len(argv) == 2:&lt;br /&gt;        shrink(argv[1])&lt;br /&gt;    else:&lt;br /&gt;        print "USAGE: python shrink.py &lt;imagefile&gt;"&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Download this code: &lt;a href="http://bradmontgomery.net/files/shrink.zip"&gt;http://bradmontgomery.net/files/shrink.zip&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-222154634554447484?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/222154634554447484'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/222154634554447484'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/02/shrinking-images-with-python.html' title='Shrinking Images with Python'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-2822110457460130484</id><published>2008-01-24T13:13:00.000-06:00</published><updated>2008-01-24T13:52:50.186-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='OpenCV'/><title type='text'>Tracking a Laser pointer with Python and OpenCV</title><content type='html'>A recent thread on the OpenCV mailing list (entitled: "Tracking laser dots") discussed techniques that could be used to track the dot from a laser pointer.  This sounded like something fun, so I finally got around to trying it out. Essentially this could be done acheived by the following algorithm:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Grab the video frame.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Convert the video frame to the HSV color space.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Split the frame into individual components:&lt;br /&gt; (separate images for H, S, and V).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Apply a threshold to each compenent (hopefully keeping just the dot from the laser). It was originally suggested that just the Hue component be used to search for the laser's dot, but I actually got several false positives doing this.  Therefore, using Value in addition to Hue gave me a more reliable result.  I can see where finding good threshold values for all 3 components would be a good approach in some situations. &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Finally, perform an AND operation on the 3 images (which "should" cut down on false positives)&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;I should note that my testing was performed using a red laser pointer and a large white sheet of paper in an well-lit office. Since I was only tracking the dot on the paper, this turned out to be a fairly easy task to accomplish.  Finding good threshold values in other situations would be much more difficult.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://bradmontgomery.net/files/laser_tracker.zip"&gt;Download this Python code: (laser_tracker.zip)&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-2822110457460130484?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2822110457460130484'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2822110457460130484'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/01/tracking-laser-pointer-with-python-and.html' title='Tracking a Laser pointer with Python and OpenCV'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-8252996560888481758</id><published>2008-01-14T13:51:00.000-06:00</published><updated>2008-01-14T14:05:01.919-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Fun'/><category scheme='http://www.blogger.com/atom/ns#' term='Food'/><title type='text'>Super Spicy Creamy Veggie Chili</title><content type='html'>I like food.  I like to eat it... I like to make it.  &lt;br /&gt;&lt;br /&gt;A few days ago I got a craving for some chili, and I almost had all of the ingredients that I needed.  What I didn't have, was meat.  So, I decided to improvise, and what resulted was the recipe below.  It's sure to give all the gastric goodness of any other spicy bowl of chili!&lt;br /&gt;&lt;br /&gt;Ingredients:&lt;br /&gt;1 can black beans.&lt;br /&gt;1 can dark red kidney beans.&lt;br /&gt;2 cans &lt;a href="http://www.ro-tel.com/pages/products/extra_hot/index.jsp"&gt;RO*TEL extra hot&lt;/a&gt; (good stuff!)&lt;br /&gt;1 medium eggplant cut in small cubes.&lt;br /&gt;about 1 cup of cubed cheddar&lt;br /&gt;about 1 cup of sour cream&lt;br /&gt;a hearty dash of chili powder.&lt;br /&gt;&lt;br /&gt;Rinse the beans, and put them in a pan with the RO*TEL and the eggplant. Place on Medium heat until the eggplant starts to soften, then add the cheese and the sour cream.  Once everything is all melted, it's ready to eat!  &lt;br /&gt;&lt;br /&gt;It's good with corn chips or crackers, and another dollop of sour cream doesn't hurt either.  Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-8252996560888481758?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/8252996560888481758'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/8252996560888481758'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2008/01/super-spicy-creamy-veggie-chili.html' title='Super Spicy Creamy Veggie Chili'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-8290323884016481914</id><published>2007-12-05T08:59:00.001-06:00</published><updated>2008-06-17T21:04:39.248-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='Image'/><title type='text'>Computing Correlation Coefficients in Python</title><content type='html'>A useful technique for matching objects in images is to compute the images' Correlation Coefficients.  Essentially, you take any image and compute the correlation between it and another, smaller image containing ONLY the object that you want to identify.  The resulting correlation image should contain bright spots where there is a high correlation (or match) between the two images.&lt;br /&gt;&lt;br /&gt;Here's a simple python script to compute the correlation between two images:&lt;br /&gt;&lt;a href="http://bradmontgomery.net/files/correlation.zip"&gt;http://bradmontgomery.net/files/correlation.zip&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It requires PIL and numpy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-8290323884016481914?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/8290323884016481914'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/8290323884016481914'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/12/computing-correlation-coefficients-in.html' title='Computing Correlation Coefficients in Python'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-7249636208289922033</id><published>2007-10-31T10:13:00.000-05:00</published><updated>2008-02-06T11:32:36.112-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Apps'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Announcing pgSlideShow</title><content type='html'>I've just released a simple little application called &lt;a href="http://bradmontgomery.net/pgSlideShow/"&gt;pgSlideShow&lt;/a&gt;.  It's a free image slideshow application written in &lt;a href="http://python.org"&gt;Python&lt;/a&gt; using &lt;a href="http://pygame.org"&gt;pygame&lt;/a&gt;. It recursively searches a given directory for image files, and then displays them on a computer screen in Fullscreen mode. &lt;br /&gt;&lt;br /&gt;Get it here:&lt;br /&gt;&lt;a href="http://bradmontgomery.net/pgSlideShow/"&gt;http://bradmontgomery.net/pgSlideShow/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-7249636208289922033?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7249636208289922033'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7249636208289922033'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/10/announcing-pgslideshow.html' title='Announcing pgSlideShow'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-7263461531173609314</id><published>2007-10-16T21:04:00.000-05:00</published><updated>2007-10-16T21:35:47.721-05:00</updated><title type='text'>On the Move...</title><content type='html'>Well, I've just created my account here at &lt;a href="http://www.blogger.com/"&gt;blogger.com&lt;/a&gt;, and I'll have to try it out for a while before I decide whether or not I'm happy.  I've been using my own system for a while, and normally, I'm happy with a simple, stream-lined app, but I've begun wanting new features that I just don't have time to build for myself...&lt;br /&gt;&lt;br /&gt;So, I'm here.  We'll see how it works.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-7263461531173609314?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7263461531173609314'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7263461531173609314'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/10/on-move.html' title='On the Move...'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-4096316648311915512</id><published>2007-10-16T20:17:00.001-05:00</published><updated>2010-07-07T10:36:50.337-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OS X'/><category scheme='http://www.blogger.com/atom/ns#' term='OpenGL'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>PyOpenGL on OS X</title><content type='html'>&lt;strong&gt;Update: July 7, 2010:&lt;/strong&gt; For the past year or so I've been using &lt;a href="http://pypi.python.org/pypi/virtualenv"&gt;virtualenv&lt;/a&gt;, &lt;a href="http://www.doughellmann.com/projects/virtualenvwrapper/"&gt;virtualenvwrapper&lt;/a&gt;, and &lt;a href="http://pip.openplans.org/"&gt;pip&lt;/a&gt; to manage and install python packages, and I highly encourage everyone to use these tools!  It may take a bit to learn how to use these tools, but once you do, the rewards are great!  (especially when using 3rd party python libraries on Mac OS X).&lt;br /&gt;&lt;br /&gt;To install PyOpenGL using the aforementioned tools, you'd simply do the following.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Create a virtual environment for your OpenGL installation.  I'm naming it "gl":&lt;pre&gt;mkvirtualenv gl&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Then use pip to install PyOpenGL: &lt;pre&gt;pip install pyopengl&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;That's all there is to it!  I grabbed the demo apps from &lt;a href="http://pypi.python.org/pypi/PyOpenGL-Demo"&gt;http://pypi.python.org/pypi/PyOpenGL-Demo&lt;/a&gt;, and verified that the gears.py script works, so this should get you started.&lt;br /&gt;&lt;br /&gt;I'm leaving the old post below, but again, check out virtualenv and pip.  You'll be happy that you did!&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;I actually got this working quite a while ago, but for some reason I've gotten busy and never documented it.  In fact, installing PyOpenGL was almost so easy it's not even worth mentioning... Except... it was  so easy, so it's worth mentioning!&lt;br /&gt;&lt;br /&gt;Here are the steps to install PyOpenGL on Mac OS X (10.4.10):&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt; You'll need to get setuptools, and the easiest way to do that is with &lt;a href="http://peak.telecommunity.com/DevCenter/EasyInstall"&gt;EasyInstall&lt;/a&gt; (see &lt;a href="http://peak.telecommunity.com/DevCenter/EasyInstall"&gt;http://peak.telecommunity.com/DevCenter/EasyInstall&lt;/a&gt;).  You'll download the &lt;b&gt;ez_setup.py&lt;/b&gt; file, and just run: &lt;b&gt;python ez_setup.py&lt;/b&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Download PyOpenGL from their &lt;a href="http://sourceforge.net/project/showfiles.php?group_id=5988"&gt;download page&lt;/a&gt;. (Side Note: I also grabbed the OpenGLContext). Now, once you untar/unzip it, you should have a PyOpenGL/ directory.  Just open a Terminal, cd into that directory, and run &lt;b&gt;python setup.py build&lt;/b&gt;.  Once that is finished, run &lt;b&gt;python setup.py install&lt;/b&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Now, do the same for OpenGLContext...&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-4096316648311915512?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/4096316648311915512'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/4096316648311915512'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/10/pyopengl-on-os-x.html' title='PyOpenGL on OS X'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-543717478932761428</id><published>2007-09-23T20:38:00.000-05:00</published><updated>2007-12-02T15:52:34.938-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OS X'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Pygame on OS X with python 2.5</title><content type='html'>I've used Pygame on Mac OS X in the past,  but my installation recently stopped working for some reason, so I decided to grab the binaries and re-install.  After checking out the &lt;a href="http://pythonmac.org/packages/"&gt;Pythonmac&lt;/a&gt; list, I was a little disappointed to see that there were only Pygame binaries for &lt;a href="http://pythonmac.org/packages/py24-fat/index.html"&gt;Python 2.4&lt;/a&gt;.  Below is a list of software that I installed (in the necessary order) to get pygame working with Python 2.5 on OS X.  All of these (except Python 2.5.1) I got from &lt;a href="http://pythonmac.org/packages/py25-fat/index.html"&gt;pythonmac.org/packages&lt;/a&gt;.&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.python.org/ftp/python/2.5.1/python-2.5.1-macosx.dmg"&gt;Python 2.5.1&lt;/a&gt; - from the &lt;a href="http://www.python.org/download/"&gt;Python download page&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://pythonmac.org/packages/py25-fat/dmg/PIL-1.1.6-py2.5-macosx10.4-2007-05-18.dmg"&gt;PIL 1.16 - Python Image Library&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://pythonmac.org/packages/py25-fat/dmg/numpy-1.0.3.1-py2.5-macosx10.4-2007-08-27.dmg"&gt;NumPy 1.0.3.1&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://pythonmac.org/packages/py25-fat/dmg/Numeric-24.2-py2.5-macosx10.4.dmg"&gt;Numeric 24.2&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://pythonmac.org/packages/py25-fat/dmg/numarray-1.5.2-py2.5-macosx10.4-2007-01-30.dmg"&gt;numarray 1.5.2&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://pythonmac.org/packages/py25-fat/mpkg/pyobjc-1.4-py2.5-macosx10.4.mpkg.zip"&gt;PyObjC 1.4&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://pythonmac.org/packages/py25-fat/dmg/wxPython2.8-osx-unicode-2.8.3.0-universal10.4-py2.5.dmg"&gt; wxPython 2.9.3.0&lt;/a&gt; (just in case)&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;Finally, I grabbed the Pygame 1.7.1 source from &lt;a href="http://pygame.org/download.shtml"&gt;pygame.org/download.shtml&lt;/a&gt;.&lt;br /&gt; &lt;br /&gt;Installation for most of the prerequisite packages went smoothly, since they all include OS X Installers. While pygame requires Numeric, I went ahead and installed NumPy and numarray, just in case.&lt;br /&gt;&lt;br /&gt;The only real problem came when installing PyObjC.  After installing, I opened a python interpreter to test it out by typing "import Foundation" (as it says to do in the &lt;a href="http://pyobjc.sourceforge.net/doc/tutorial.php"&gt;PyObjC Tutorial&lt;/a&gt;.  This let to the following error:&lt;pre&gt;&lt;br /&gt;dyld: Symbol not found: __cg_jpeg_resync_to_restart&lt;br /&gt;Referenced from: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ImageIO.framework/Versions/A/ImageIO&lt;br /&gt;Expected in: /sw/lib/libJPEG.dylib&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Apparently, if you've installed anything using Fink, the PyObjC installer gets confused when trying to choose to which libraries to link.  For me, libjpeg was the culprit... hence the error above.  To make sure PyObjC works with Apple's ImageIO, I edited my ~/.profile and commented out any lines that set a DYLD_LIBRARY_PATH environment variable (just place a # in front of the line).  For me, this looked like this:&lt;br /&gt;&lt;pre&gt;#DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/usr/local/lib:/sw/lib:/usr/lib&lt;br /&gt;#export DYLD_LIBRARY_PATH&lt;/pre&gt;&lt;br /&gt;This prevents any conflicts when linking against libraries installed by fink (which are stored in /sw) and possibly duplicate system libraries.  After doing this, I re-ran the PyObjC installer, and everything worked perfectly.  Now, I built the Pygame source, and my simple Pygame applications will run. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;So what's next?&lt;/b&gt; Notice there's no mention of PyOpenGL above, so any Pygame applications that use PyOpenGL won't work Until you &lt;a href="http://bradmontgomery.blogspot.com/2007/10/pyopengl-on-os-x.html"&gt;Install PyOpenGL&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Download this Pygame Package:&lt;/b&gt; &lt;a href="http://bradmontgomery.net/files/pygame-1.7.1release-py2.5.1-macosx10.4.dmg"&gt;http://bradmontgomery.net/files/pygame-1.7.1release-py2.5.1-macosx10.4.dmg&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-543717478932761428?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/543717478932761428'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/543717478932761428'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/09/pygame-on-os-x-with-python-25.html' title='Pygame on OS X with python 2.5'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-7952795941748667225</id><published>2007-09-15T20:42:00.000-05:00</published><updated>2007-10-26T20:43:00.323-05:00</updated><title type='text'>$100 iPhone Early adopter Credit</title><content type='html'>There's been a lot of hoop-la about the steep price-drop of the iPhone, and a lot of people have widely ranging opinions about the $100 credit.&lt;br /&gt;&lt;br /&gt;Well, I (actually, my wife) willingly paid the initial price, and I've been absolutely happy with my iPhone!  I also grabbed my $100 credit.  All I had to do was hit &lt;a href="http://apple.com"&gt;Apple's website&lt;/a&gt; and type in  my phone number, and my iPhone's serial number.&lt;br /&gt;&lt;br /&gt;$100 is $100! Woot!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-7952795941748667225?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7952795941748667225'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7952795941748667225'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/09/100-iphone-early-adopter-credit.html' title='$100 iPhone Early adopter Credit'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-326716395189621856</id><published>2007-09-10T20:43:00.000-05:00</published><updated>2007-10-26T20:44:51.301-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>The Never-ending "To-Learn" List</title><content type='html'>&lt;b&gt;I'm officially a student!&lt;/b&gt; August marked the beginning of a new semester for me, and this time I'm a full-time student.  After being on the faculty side of things for a while, I'm actually having a great time being a student again.  One of the major benefits of being a student is that I'm actually getting to dive into that never-ending list of technology tools that I've been wanting to learn how to use!&lt;br /&gt;&lt;br /&gt;Just to name a few:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://python.org"&gt;Python&lt;/a&gt; - I'm writing as much code as I possibly can in Python.  I've used python minimally in the past, but it's actually nice to start working on larger projects using it.  I don't really like to get into religious debates about languages, but Python does feel nice!&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://pygame.org"&gt;Pygame&lt;/a&gt; - I'm not building a game (yet), but it does make a nice foundation for putting  pictures on a screen!&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.pythonware.com/products/pil/index.htm"&gt;PIL&lt;/a&gt; - the Python Image Library offers an Excellent way to to Image manipulation!&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://twistedmatrix.com"&gt;Twisted&lt;/a&gt; - if you EVER need to build an event-driven network application from the ground up... use this!&lt;/li&gt; &lt;br /&gt;&lt;li&gt;&lt;a href="http://www.opengl.org/"&gt;OpenGL&lt;/a&gt; - GAhh!  Why haven't I learned this before... there's a lot to learn!&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Luckily, there's &lt;a href="http://pyopengl.sourceforge.net/"&gt;PyOpenGL&lt;/a&gt;!&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;That should get me started!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-326716395189621856?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/326716395189621856'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/326716395189621856'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/09/never-ending-to-learn-list.html' title='The Never-ending &quot;To-Learn&quot; List'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-324645953644061889</id><published>2007-07-13T20:44:00.001-05:00</published><updated>2008-11-24T15:30:05.164-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Building Apps for the iPhone</title><content type='html'>So there was this little get-together called &lt;a href="http://barcamp.org/iPhoneDevCamp"&gt;iPhoneDevCamp&lt;/a&gt; last weekend (July 6-8, 2007), and since I'm just over 2000 miles from San Francisco, I missed out.  However, in just under a week, there seems to have been a flood of great iPhone applications.  To find out what I mean, just check out a &lt;a href="http://www.google.com/search?hl=en&amp;q=iphone+applications&amp;btnG=Search"&gt;google search&lt;/a&gt;, OR, head right on over to the &lt;a href="http://iphoneapplicationlist.com/"&gt;iPhone Application List&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;So, in such a short time since the iPhone's release, how have so many applications been built?  I blame &lt;a href="http://www.joehewitt.com/"&gt;Joe Hewitt&lt;/a&gt;.  His User Interface library, &lt;b&gt;&lt;a href="http://code.google.com/p/iui/"&gt;iUI&lt;/a&gt;&lt;/b&gt;, makes building basic apps for the iPhone a cinch. So what is iUI? Well, to quote from his &lt;a href="http://www.joehewitt.com/blog/introducing_iui.php"&gt;blog&lt;/a&gt;:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;Its goal is simply to turn ordinary standards-based HTML into a polished, usable interface that meets the high standards set by Apple's own native iPhone apps.&lt;/blockquote&gt;&lt;br /&gt;So, there you have it.  &lt;a href="http://code.google.com/p/iui/source"&gt;Try it out!&lt;/a&gt;  I certainly will!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-324645953644061889?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/324645953644061889'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/324645953644061889'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/07/building-apps-for-iphone.html' title='Building Apps for the iPhone'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-7561224320778422737</id><published>2007-07-08T20:46:00.000-05:00</published><updated>2007-10-26T20:47:37.332-05:00</updated><title type='text'>iPhone Disappointments</title><content type='html'>&lt;small&gt;Updated on 2007-07-10&lt;br/&gt;(I forgot a couple of things, so I'm adding them below as numbers 1 &amp; 2)&lt;/small&gt; Part 3 of my iPhone saga (&lt;a href="http://bradmontgomery.net/show.php?page=blog&amp;id=45"&gt;Part 1&lt;/a&gt; and &lt;a href="http://bradmontgomery.net/show.php?page=blog&amp;id=46"&gt;Part 2&lt;/a&gt;)&lt;br /&gt;After using the iPhone for a little less than a week, I do have a few disappointments.  Don't get me wrong, I still think it's an incredibly cool device.  Like my wife said, "It's the greatest thing since sliced bread."   &lt;br /&gt;&lt;br /&gt;I share some of my disappointments with &lt;a href="http://terrywhite.com/techblog/"&gt;Terry White&lt;/a&gt;, who gives &lt;a href="http://terrywhite.com/techblog/?p=196"&gt;a fairly objective review&lt;/a&gt; of the iPhone in general.&lt;br /&gt;&lt;br /&gt;So to cut to the chase, here they are:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Limited Tilt...&lt;/b&gt;  I've found that it's easiest to type on the iPhone when I tilt it sideways (in landscape)  However, the app that I type most in (Mail) doesn't let you do this!!  In fact, only Safari, Photos, and iPod take advantage of the iPhones accelerometer.  Mail, SMS, Google Maps, and Notes (all apps that may require extensive "typing") don't!  Hopefully a software update will fix this!&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Where are the games!?&lt;/b&gt; I have to admit that I've already had a few neat ideas for games on the iPhone.  Surprisingly, it doesn't come with ANY bundled games.  The folks who put on the &lt;a href="http://barcamp.org/iPhoneDevCamp"&gt;iPhoneDevCamp&lt;/a&gt; may have had some interesting browser-based games, but there's still a lot more you could do with a local app.  Only time will tell what will happen for those interested in &lt;a href="http://www.google.com/search?q=iphone%203rd%20party%20developer&amp;sourceid=mozilla2&amp;ie=utf-8&amp;oe=utf-8"&gt;3rd party development for the iPhone&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Fingerprints!&lt;/b&gt;:  I'll admit, I'm OC when it comes to fingerprints on my monitor/TV/display, so I'm constantly wiping the iPhone on my shirt.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;EDGE&lt;/b&gt;: OK, nothing wrong with the iPhone itself... the EDGE network (AT&amp;T's data network) is just too slow.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Ringtones&lt;/b&gt;: All that music, and you can't use your tunes as a ringtone!&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;No Video!&lt;/b&gt; - While most cell phones give you poor quality video, at least they give you the option to capture video!  Not so with the iPhone (yet, I hope)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Safari/Google Maps Crash!&lt;/b&gt;:  This one was a big surprise to me!  Safari seems to crash a LOT for me.  In fact, I tried to post a blog yesterday, and halfway through writing it on the iPhone, Safari just disappeared.  When I started Safari again, I was greeted with the site I'd previously viewed (&lt;a href="http://slashdot.org/palm"&gt;http://slashdot.org/palm&lt;/a&gt;). &lt;br /&gt;Apparently several others have experienced this, and the solution (which I found on &lt;a href="http://discussions.apple.com/thread.jspa?messageID=4880845î©žï¿½"&gt;Apple's Discussion Boards&lt;/a&gt;, is to simply reboot the phone.  I'm confident that this issue will be fixed with an update in the future.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Bluetooth headset support&lt;/b&gt;:  I've got a Jabra BT 350 headset, which the iPhone regonised and paired with perfectly.  However, sometimes when I talk on the iPhone, it doesn't give me the option to switch the Audio to the headset.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;It Locked up!&lt;/b&gt;: Ok, so yesterday, my iPhone completely locked up. Well, the touch screen wouldn't work, with is fairly devastating for the iPhone.  I hadn't reset it since I'd activated it, and even when I tried to power it off... I couldn't move the slider to verify that I wanted it powered off!  After a &lt;a href="http://docs.info.apple.com/article.html?artnum=305743"&gt;Reset&lt;/a&gt;, though, it seems to be working perfectly.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;Overall, I'm still happy. Personally, I think the large screen is what makes the iPhone so cool.  I really like my music, but I've been more than happy with my iPod nano for that... it's the iPhones integration with iPhoto that takes the cake for me.  I've probably found myself looking a pictures of my daughters and my wife more than anything, and having the 2 megapixel camera rocks!  The email/web/phone just happens to seal the deal for me.So, &lt;a href="http://rosemarie.name"&gt;baby&lt;/a&gt;... Again, THANK YOU!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-7561224320778422737?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7561224320778422737'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7561224320778422737'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/07/iphone-disappointments.html' title='iPhone Disappointments'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-4930395376239823836</id><published>2007-07-04T20:51:00.000-05:00</published><updated>2007-10-26T20:53:13.575-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><title type='text'>My wife bought 2 iPhones!!!</title><content type='html'>I think I'm still in shock.  Yesterday, I got a call from my wife.  She simply said, "We both have iPhones! I got you the 8Gb model, and I got the 4Gb model..."  &lt;br /&gt;&lt;br /&gt;Yeah... I'm still in shock.&lt;br /&gt;&lt;br /&gt;So, over the next few days, I've decided to blog about my iPhone fun.  First, I'll start with some pictures!&lt;br /&gt;&lt;br /&gt;&lt;img src="http://bradmontgomery.net/files/iPhone/the_iPhone_box.png" style="float: left; margin: 5px;" alt="The iPhone Box."/&gt;&lt;br /&gt;&lt;img src="http://bradmontgomery.net/files/iPhone/the_iPhone_on_box.png" style="float: left; margin: 5px;" alt="The iPhone on top of the box."/&gt;&lt;br /&gt;&lt;img src="http://bradmontgomery.net/files/iPhone/iPhone_stuff.png" style="float: left; margin: 5px;" alt="Items included with the iPhone"/&gt;&lt;br style="clear: both"/&gt;&lt;br /&gt;&lt;br /&gt;So, here we have the iPhone with it's box, and all of the stuff that was include with it.  There's the dock, a USB connector, an AC adapter to recharge the iPhone, the new stereo headphones with a mic, the manuals, and the complementary apple stickers.&lt;br /&gt;&lt;br /&gt;One thing that I didn't know about was the new headphones.    In addition to just being the basic earbud (like those bundled with an iPod), these headphones also include a mic, so they can be used to talk on the phone.  The mic includes a "button" that allows you to answer phone calls. (it's not really an extruding button per se, but a "press-able"  area surrounding the mic.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://bradmontgomery.net/files/iPhone/iPhone_size_1.png" style="float: left; margin: 5px;" alt="The iPhone with a US Quarter"/&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://bradmontgomery.net/files/iPhone/iPhone_size_2.png" style="float: left; margin: 5px;" alt="The iPhone with a US Quarter"/&gt;&lt;br /&gt;&lt;img src="http://bradmontgomery.net/files/iPhone/iPhone_size_3.png" style="float: left; margin: 5px;" alt="The iPhone next to a Nokia 6102"/&gt;&lt;br /&gt;&lt;br style="clear: both"/&gt;&lt;br /&gt;Now, it's really difficult to get an idea just how sleek the iPhone is, without holding one yourself. I've photographed my new iPhone next to a US Quarter and next to my old Nokia 6102.  I have to admit, for a phone (or any portable device) the screen feels large, but the phone feels very slim.  I don't really like to carry much stuff in my pocket, so I'm not sure how it will feel when I'm not using it.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://bradmontgomery.net/files/iPhone/activate_iPhone.png" style="float: left; margin: 5px;" alt="You have to Activate the iPhone before using it!"/&gt;&lt;br /&gt;&lt;img src="http://bradmontgomery.net/files/iPhone/iPhone_keypad.png" style="float: left; margin: 5px;" alt="The iPhone keypad"/&gt;&lt;br /&gt;&lt;br style="clear: both"/&gt;&lt;br /&gt;As soon as I unpackaged my iPhone, I turned it on to start playing with it... Unfortunately, there's not much you can do until you activate it.  You can, however, unlock the display, and see the keypad, so I assume it could be used to dial 911 without activating it (like most/all cell phones).  Before I activated mine, I did punch some numbers on the keypad, just to see how it felt.  I didn't really have any problem touching the correct numbers, so I'm happy with how the keypad feels.  One other thing I noticed, is that the numbers on the screen decrease in size as you type more of them... this way, you always see the whole number you're dialing (unlike some other cell phones where you may only see 4-5 digits at a time, or where the digits will stack on top of each other)&lt;br /&gt;&lt;br /&gt;&lt;img src="http://bradmontgomery.net/files/iPhone/docked_on_mini.png" style="float: left; margin: 5px;" alt="The iPhone docked on top of my Mac mini."/&gt;&lt;img src="http://bradmontgomery.net/files/iPhone/docked_on_mini2.png" style="float: left; margin: 5px;" alt="The iPhone docked on top of my Mac mini."/&gt;&lt;br style="clear: both"/&gt;&lt;br /&gt;So, I finally got around to plugging up the dock and connecting my iPhone.  At first, I plugged the dock directly into my powered USB hub, but iTunes didn't detect the iPhone! So, I plugged the dock directly into the back of my Mac mini, and iTunes immediately detected it.  &lt;b&gt;Time to activate this baby!&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://bradmontgomery.net/files/iPhone/iPhone_welcome.png" style="float: left; margin: 5px;" alt="iTunes welcomes you to the iPhone."/&gt;&lt;br style="clear: both"/&gt;&lt;br /&gt;As soon as iTunes detects the iPhone, it greets you with a welcome screen, and walks you through the activation process.  My wife and I were already Cingular/AT&amp;T customers, and luckily the activation process included options for this possibility.  Unfortunately, activating the iPhone through iTunes only allowed us to add a data plan to our existing plan.  The lowest-cost data plan is $20/per phone, so our monthly bill has been increased by $40.  I read the &lt;a href="http://www.wireless.att.com/"&gt;att wireless website&lt;/a&gt;, and it seems that we should've been able to switch to a new plan.  I suspect that you'd either have to call AT&amp;T or stop by a store to do this.  The other thing that I'm a little disappointed about is the FamilyTalk pricing for the iPhone.  For regular phones, FamilyTalk is $9.99/per line.  For the iPhone, it's $29.99/per line.  So, as of now, our phone bill has increased an extra $20 on top of the $40 for the data access...  I'm not exactly happy about that.&lt;br /&gt;&lt;br /&gt;All in all, the iTunes registration process was really easy, and for both phones, it only took about 5-10 minutes.&lt;br /&gt;&lt;br /&gt;Once my iPhone was registered, it seamlessly sync'd my iCal calendars, my photos from iPhoto, and my music from iTunes.  Of course, since I'm a mac user, I expected this seamless integration.  My wife, who's using Windows XP was able to pull music from iTunes and photos from her My Pictures folder.  The iPhone also has the capability to sync mail and calendar info from Outlook or Outlook Express, but we haven't had a chance to try that out yet.&lt;br /&gt;&lt;br /&gt;So, my initial reaction to being an iPhone owner:  "COOL!"&lt;br /&gt;And it is... very cool, though a little pricey.&lt;br /&gt;&lt;br /&gt;As I explore my iPhone's new features and learn all of its quirks, I'll post my thoughts on this cool new gadget.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-4930395376239823836?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/4930395376239823836'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/4930395376239823836'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/07/my-wife-bought-2-iphones.html' title='My wife bought 2 iPhones!!!'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-5470121664639771675</id><published>2007-07-04T20:47:00.000-05:00</published><updated>2007-10-26T20:51:40.320-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><title type='text'>Using (and Abusing) the iPhone</title><content type='html'>OK... so for part 2 of my iPhone saga:&lt;br /&gt;We went to visit some family today, and of course I took my iPhone.  For the most part, I've really tested out the camera today.  I'm no guru, but I'm very happy with how quickly the iPhone takes pictures.  Since my girls are now 4 and 2, this is a very important feature!!  I've also got an HP Photosmart M527, and my non-scientific opinion is that the iPhone takes photos about as twice as fast as my HP.  (Of course, it's a 2-megapixel camera vs. a 6-megapixel camera)&lt;br /&gt;&lt;br /&gt;Just to give you an idea, here's a picture of my youngest daughter:  &lt;a href="http://bradmontgomery.net/files/iPhone/iphone_photo.jpg"&gt;&lt;img src="http://bradmontgomery.net/files/iPhone/iphone_photo_thumb.png" alt=""/&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I convinced my wife (OK, so she offered :)) to drive home from out festivities today, so I could play with the iPhone in the car.  While in town, I had no problems browsing the web or checking my email using the EDGE connection.  However, once we got out of town, there were several locations where I had no network connection (this is rural NE Arkansas...)&lt;br /&gt;&lt;br /&gt;Another thing that I've noticed is that there are a &lt;b&gt;LOT&lt;/b&gt; of unsecured wireless networks... but that's for another blog! &lt;br /&gt;&lt;br /&gt;&lt;b&gt;As for typing on the iPhone&lt;/b&gt;, I'm fairly impressed with it's ability to suggest and correct misspelled words.  Now, I don't really think that I have large fingers, but I very frequently misspelled words using the iPhone.  However, the iPhone "guessed" what I wanted about 80% of the time!  Things like "sxgppl" were correctly identified as "school"  (As as side note, I'd guesstimate that I touch type somewhere around 60wpm with 90% accuracy, if anyone cares)So, when typing on the iPhone, I found myself just ignoring what I typed in hopes that the phone could figure it out.  Overall, I'm impressed.&lt;br /&gt;&lt;br /&gt;Now that I'm home, the geek in me is dieing to portscan this thing!  So here goes!  First a basic portmap with nmap:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;nmap 192.168.0.11&lt;br /&gt;&lt;br /&gt;Starting Nmap 4.20 ( http://insecure.org ) at 2007-07-04 19:41 CDT&lt;br /&gt;All 1697 scanned ports on 192.168.0.11 are closed&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Interesting enough... let's see what OS nmap thinks is running on this thing:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sudo nmap -O 192.168.0.11&lt;br /&gt;&lt;br /&gt;Starting Nmap 4.20 ( http://insecure.org ) at 2007-07-04 19:43 CDT&lt;br /&gt;Warning:  OS detection for 192.168.0.11 will be MUCH less reliable because we did not find at least 1 open and 1 closed TCP port&lt;br /&gt;All 1697 scanned ports on 192.168.0.11 are closed&lt;br /&gt;MAC Address: 00:1B:63:E1:ED:D8 (Unknown)&lt;br /&gt;Device type: general purpose&lt;br /&gt;Running: Apple Mac OS X 10.3.X|10.4.X|10.5.X, FreeBSD 4.x&lt;br /&gt;OS details: Applie Mac OS X 10.3.9 - 10.4.7, Apple Mac OS X 10.4.8 (Tiger), OS X Server 10.5 (Leopard) pre-release build 9A284, FreeBSD 4.10-RELEASE (x86)&lt;br /&gt;Network Distance: 1 hop&lt;br /&gt;&lt;br /&gt;OS detection performed. Please report any incorrect results at http://insecure.org/nmap/submit/ .&lt;br /&gt;Nmap finished: 1 IP address (1 host up) scanned in 16.991 seconds&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So, while I'm not really a security professional, and I know these are very basic scans, it seems like there's no blatantly obvious security gaps in the iPhone (through its wifi connection anyway).  If anybody has any more advanced nmap scans they'd like to see performed, just let me know.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-5470121664639771675?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/5470121664639771675'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/5470121664639771675'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/07/using-and-abusing-iphone.html' title='Using (and Abusing) the iPhone'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-2431218985315684152</id><published>2007-06-16T20:53:00.000-05:00</published><updated>2007-10-26T20:55:17.714-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OS X'/><category scheme='http://www.blogger.com/atom/ns#' term='Games'/><title type='text'>Compiling Warzone 2100 on Mac OS X</title><content type='html'>I recently ran across &lt;a href="http://wz2100.net/"&gt;The Warzone 2100 Resurrection Project&lt;/a&gt;, and I thought to myself,"cool, I wonder if there's an OS X version..."  So, I hit their &lt;a href="http://wz2100.net/downloads.html"&gt;downloads&lt;/a&gt; page only to find the message: "(The MacOSX diskimage will follow soon)"&lt;br /&gt;&lt;br /&gt;So I thought, "oh well, I'll just grab the source code and compile it!"  After about two hours of digging, I finally got it compiled, and here's how!  (NOTE: I've got quite a bit of Unix stuff installed via &lt;a href="http://finkproject.org/"&gt;fink&lt;/a&gt;, so your mileage may vary)&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Here's the list of stuff I had to get/install/compile before I could even start on Warzone 2100:  &lt;b&gt;cmake (to compile physfs)&lt;/b&gt;, &lt;b&gt;physfs&lt;/b&gt;, &lt;b&gt;a newer bison&lt;/b&gt;, and &lt;b&gt;SDL_net&lt;/b&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;First I grab &lt;a href="http://cmake.org"&gt;cmake 2.4.6&lt;/a&gt;.  Luckily, they have a &lt;a href="http://cmake.org/HTML/Download.html"&gt;universal .dmg for Tiger&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Next, I grab the source code for &lt;a href="http://icculus.org/physfs/"&gt;physfs-1.1.1&lt;/a&gt; from their &lt;a href="http://icculus.org/physfs/downloads/"&gt;download page&lt;/a&gt;.  At first I tried compiling the "Unix Way", but that didn't work, so I used cmake to generate an Xcode project (pretty cool!). From a terminal window, I cd into the physfs directory and type:&lt;br /&gt;&lt;pre&gt;cmake -G Xcode .&lt;/pre&gt;&lt;br /&gt;Then, after compiling physfs with Xcode, I copied the libraries to my /usr/local directories. Again from a Terminal:&lt;br /&gt;&lt;pre&gt;sudo cp libphysfs.* /usr/local/lib&lt;br /&gt;sudo cp physfs.h /usr/local/include&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Next, I open up &lt;a href="http://finkcommander.sourceforge.net/"&gt;fink commander&lt;/a&gt; and grab the newest version of bison from fink&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Now I need &lt;a href="http://www.libsdl.org/projects/SDL_net/"&gt;SDL_net&lt;/a&gt;.  While the project offers a &lt;a href="http://www.libsdl.org/projects/SDL_net/release/SDL_net-1.2.6.dmg"&gt;Mac OS X Framework&lt;/a&gt; and SDL_net is available through fink... I grabbed the source anyway.  It compiled just fine the Unix way:&lt;pre&gt;&lt;br /&gt;./configure&lt;br /&gt;make&lt;br /&gt;sudo make install&lt;/pre&gt;&lt;br /&gt;This put all the libraries in /usr/local/lib and /usr/local/include&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Finally, time to compile Warzone2100, so I grabbed the &lt;a href="http://wz2100.net/downloads.html"&gt;source code&lt;/a&gt;, which contains a handy-dandy file called COMPILE.  I suggest you read it.  To start of, I opened a terminal and cd'd into the warzone2100-2.0.6 directory and typed the following command:&lt;pre&gt;./autogen.sh&lt;/pre&gt;&lt;br /&gt;Then, I typed the following to configure it using my fink libraries and headers:&lt;br /&gt;&lt;pre&gt;./configure --prefix=/Applications/Warzone2100-2.0.6 LDFLAGS=-L/sw/lib CPPFLAGS=-I/sw/include&lt;/pre&gt; &lt;br /&gt;This will also put the final binary in my /Applications Directory. So, the last to steps are to compile, and install:&lt;pre&gt;make&lt;br /&gt;sudo make install&lt;/pre&gt;This did the trick for me.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Now that I got Warzone2100 compiled, I can open a terminal and type the following:&lt;br /&gt;&lt;pre&gt;/Applications/Warzone2100/bin/warzone2100 --fullscreen&lt;/pre&gt;&lt;br /&gt;And this "should" lauch the application!&lt;br /&gt;&lt;/ol&gt;Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-2431218985315684152?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2431218985315684152'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2431218985315684152'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/06/compiling-warzone-2100-on-mac-os-x.html' title='Compiling Warzone 2100 on Mac OS X'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-8723962191521825126</id><published>2007-05-26T20:55:00.001-05:00</published><updated>2008-07-12T10:23:27.685-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Fun'/><title type='text'>Thunder Demons live here!</title><content type='html'>&lt;span style="float: right; padding: .5em;"&gt;&lt;img src="http://bradmontgomery.net/files/thunderdemons.jpg" alt="Thunder Demons live here!"/&gt;&lt;/span&gt;While walking across &lt;a href="http://memphis.edu"&gt;campus&lt;/a&gt;, I noticed this particular image attached to a large green box that I knew to contain electrical "stuff".  Now, I know the dangers of mixing humans with direct electrical current, but I'm not so sure this image evokes the correct message.  I'm not sure that a monstrous blob of electricity has ever actually jumped out of a box in order to heinously inflict harm to humans as they unknowingly walk by... I could be wrong, though.  Perhaps I should avoid that particular sidewalk next time I'm on campus.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-8723962191521825126?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/8723962191521825126'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/8723962191521825126'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/05/thunder-demons-live-here.html' title='Thunder Demons live here!'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-1417110640401924205</id><published>2007-05-22T20:56:00.000-05:00</published><updated>2007-10-26T20:57:05.209-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Apps'/><title type='text'>Easy Video Conferencing?</title><content type='html'>I just skimmed through the list of &lt;a href="http://www.pcworld.com/printable/article/id,131935/printable.html"&gt;PC World's 100 Best Products of 2007&lt;/a&gt;, and one of the things that caught my eye was &lt;br /&gt;&lt;a href="http://www.sightspeed.com/"&gt;SightSpeed&lt;/a&gt;.  Apparently it's supposed to be the world's best video conferencing system. What interests me, though is the price (FREE!) and that it works on Mac OS X (and Windows). I'm going to give it a shot and see how it works.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-1417110640401924205?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/1417110640401924205'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/1417110640401924205'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/05/easy-video-conferencing.html' title='Easy Video Conferencing?'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-6044072354529285854</id><published>2007-05-16T20:57:00.000-05:00</published><updated>2007-10-26T20:58:49.202-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Games'/><title type='text'>Hello Torque, again.</title><content type='html'>Well, I've just begun to dig into the &lt;a href="http://www.garagegames.com/products/torque/tge/"&gt;Torque Game Engine&lt;/a&gt; again, but this time, I'm building it on a Linux box. Once I downloaded the Linux source, it took me a while to figure out how to get it compiled.  TGE's configuration is a little different than the typical "./configure and make" process.&lt;br /&gt;&lt;br /&gt;First of all, I tried to make sure I had all of the development tools necessary to build TGE, including &lt;b&gt;nasm&lt;/b&gt;, &lt;b&gt;libogg&lt;/b&gt;, &lt;b&gt;libvorbis&lt;/b&gt;, and &lt;b&gt;libtheora&lt;/b&gt; along with any &lt;package&gt;-dev versions of many packages.   Then, once you unzip the source you need to issue the following command to set up your configuration:&lt;pre&gt;make -f mk/configure.mk&lt;/pre&gt;This will display the current build configuration and it will tell you if it's valid or not.  To get a valid configuration on my system, I used the following configuration:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;make -f m/configure.mk OS=LINUX COMPILER=GCC4 BUILD=DEBUG \&lt;br /&gt;DIR.OBJ=/home/brad/TorqueGameEngineSDK-Linux-1-5-0/build&lt;/pre&gt;&lt;br /&gt;Notice that I had to use an absolute path for the (optional) object Directory.  At first, I just used "DIR.OBJ=build", which resulted in some compiler errors. Anyway, this placed all the object files in a directory called "build.GCC4.DEBUG"&lt;br /&gt;&lt;br /&gt;The next change I had to make was in an actual source file. Apparently, GCC didn't like some things... I'm not going to post any details, but I did eventually get it working, so &lt;a href="http://bradmontgomery.net/show.php?page=contact"&gt;contact me&lt;/a&gt; if you're interested.&lt;br /&gt;&lt;br /&gt;Good Luck!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-6044072354529285854?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/6044072354529285854'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/6044072354529285854'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/05/hello-torque-again.html' title='Hello Torque, again.'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-3476416437013374650</id><published>2007-04-23T20:58:00.002-05:00</published><updated>2008-09-07T20:53:31.252-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Apps'/><category scheme='http://www.blogger.com/atom/ns#' term='OS X'/><category scheme='http://www.blogger.com/atom/ns#' term='Math'/><title type='text'>MacMathematics</title><content type='html'>One of my Favorite Mac Apps is &lt;a href="http://en.wikipedia.org/wiki/Grapher"&gt;Grapher&lt;/a&gt;.  It lets you graph everything from simple 2D equations to complex 3D equations.  For example, want to see all of the &lt;a href="http://bradmontgomery.net/files/CubeAxesOfSymmetry.jpg"&gt;Axes of symmetry for a cube&lt;/a&gt;?  Just enter the equations x=0, y=0, z=0, x=y, x=-y, x=z, x=-z, y=z, and y=-z, and voila!  Pictures make math fun! You can also save these in .gcx files.  Download my cube: &lt;a href="http://bradmontgomery.net/files/CubeAxesOfSymmetry.gcx"&gt;CubeAxesOfSymmetry.gcx&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-3476416437013374650?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/3476416437013374650'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/3476416437013374650'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/04/macmathematics_23.html' title='MacMathematics'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-8849074476208001463</id><published>2007-04-23T20:58:00.001-05:00</published><updated>2007-10-26T21:01:12.484-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Apps'/><category scheme='http://www.blogger.com/atom/ns#' term='OS X'/><category scheme='http://www.blogger.com/atom/ns#' term='Math'/><title type='text'>MacMathematics</title><content type='html'>One of my Favorite Mac Apps is &lt;a href="http://en.wikipedia.org/wiki/Grapher"&gt;Grapher&lt;/a&gt;.  It lets you graph everything from simple 2D equations to complex 3D equations.  For example, want to see all of the &lt;a href="http://bradmontgomery.net/files/CubeAxesOfSymmetry.jpg"&gt;Axes of symmetry for a cube&lt;/a&gt;?  Just enter the equations x=0, y=0, z=0, x=y, x=-y, x=z, x=-z, y=z, and y=-z, and voila!Pictures make math fun! You can also save these in .gcx files.  Download my cube: &lt;a href="http://bradmontgomery.net/files/CubeAxesOfSymmetry.gcx"&gt;CubeAxesOfSymmetry.gcx&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-8849074476208001463?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/8849074476208001463'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/8849074476208001463'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/04/macmathematics.html' title='MacMathematics'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-7754127153775579701</id><published>2007-03-09T21:01:00.000-06:00</published><updated>2007-10-26T21:03:18.435-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Games'/><title type='text'>Casual Games</title><content type='html'>It seems like I keep running into this term, "&lt;a href="http://en.wikipedia.org/wiki/Casual_game"&gt;casual games&lt;/a&gt;."  Apparently this is a burgeoning industry with the advent of more advanced handheld devices (like cellphones).  There were even two keynotes (Trip Hawkins of&lt;br /&gt;&lt;a href="http://www.digitalchocolate.com/"&gt;Digital Chocolate&lt;/a&gt;, and Robert Tercek of &lt;a href="http://peoplejam.com/"&gt;PeopleJam&lt;/a&gt; and GDC Mobile) regarding causal games at the recent &lt;a href="http://www.gdconf.com/"&gt;Game Developers Conference&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Basically, any simple (hopefully addictive) game that's quick and easy to learn falls in the category of a casual game.  Two of my favorite examples are &lt;a href="http://get.games.yahoo.com/proddesc?gamekey=atlantis2"&gt;Atlantis Sky Patrol&lt;/a&gt; (from &lt;a href="http://www.bigfishgames.com"&gt;Big Fish Games&lt;/a&gt;) and &lt;a href="http://www.popcap.com/launchpage.php?theGame=insaniquarium"&gt;Insaniquarium&lt;/a&gt;  (from &lt;a href="http://www.popcap.com"&gt;PopCam&lt;/a&gt;).  [Side note: It seems like I remember playing insaniquarium before it belonged to a company... did I just dream this?]&lt;br /&gt;&lt;br /&gt;I don't where I read it, but I vaguley remember reading a quote somewhere that had something to do with casual games, and it said something similar to this, "If you don't think you're a gamer, it's just because you haven't found the right game!"[Another Sidenote: Basicially, these are the the types of games I would like write during my free time (if I had any!)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-7754127153775579701?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7754127153775579701'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7754127153775579701'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/03/casual-games.html' title='Casual Games'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-7591638465624679365</id><published>2007-03-05T21:04:00.000-06:00</published><updated>2007-10-26T21:06:16.044-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hardware'/><category scheme='http://www.blogger.com/atom/ns#' term='Mac'/><title type='text'>Mac mini disassembly</title><content type='html'>&lt;img style="float:right" src="http://bradmontgomery.net/files/mini-disassembly.png" alt="Mac mini disassembly"/&gt;&lt;br /&gt;I've been a mac user since my wife bought me a Min in 2005.  During that time I've enjoyed being a Mac user, but until now I've not performed a lobotomy on my little mac. I finally decided to upgrad to 1Gb of Ram, and thanks to &lt;a href="http://www.newegg.com/product/product.asp?item=N82E16820161626"&gt;NewEgg&lt;/a&gt;, this was fairly inexpensive.  Also, thanks to &lt;a href="http://www.russellbeattie.com/notebook/1008276.html"&gt;Russel Beattie&lt;/a&gt; and &lt;a href="http://www.smashsworld.com/2005/01/taking-apart-mac-mini-how-to.php"&gt;smashsworld.com&lt;/a&gt;, I found it fairly easy to get into the mini.&lt;br /&gt;&lt;br /&gt;Though I only upgraded the RAM, I was interested in discovering how difficult it would be to upgrade the Hard drive as well.  To do this, you have to first remove the combo  drive (which is attached to a daughter board along with the hard drive). &lt;br /&gt; Once this has been done, you need to remove the air vents and the processor's cooling fan in order to get to the screws that hold the drive in place. While doing this, I noticed that the power switch's wire was taped to the vent, so I just cut this (The TAPE, not the WIRE!).  Once all of the screws that held the air vents to the motherboard were removed, I found it fairly easy to just lift the hard drive and it's daughterboard out of the motherboard.&lt;br /&gt;In doing this, I was fairly amazed at how small the Mac mini really is... I guess you have to see it to believe it!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-7591638465624679365?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7591638465624679365'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7591638465624679365'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/03/mac-mini-disassembly.html' title='Mac mini disassembly'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-7998488112426396151</id><published>2007-02-27T21:06:00.001-06:00</published><updated>2008-11-24T15:30:20.728-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Javascript: What is the standard?</title><content type='html'>I've recently been writing a little javascript, and I needed to chage the value of some text inside an html/xhtml element.  It seems there are several ways to do this, but evey browser may or may not support the same method for doing it (big surprise, here) I'm not sure what is considered the "standard" way.  Here's a little script that I use to help me decide which browsers support which methods for altering text within an html/xhtml element:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;html&amp;gt;&lt;br /&gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;blah&amp;lt;/title&amp;gt;&lt;br /&gt;&amp;lt;script language="JavaScript"&amp;gt;&lt;br /&gt;&amp;lt;!-- Hide code from older browsers&lt;br /&gt;function loader() {&lt;br /&gt;  pp = document.getElementsByTagName("p")&lt;br /&gt;  pp[0].innerText = ".innerText DOES work!"&lt;br /&gt;  pp[1].style.content = ".style.content DOES work!"&lt;br /&gt;  pp[2].innerhtml    = ".innerhtml DOES work!"&lt;br /&gt;  pp[3].childNodes[0].nodeValue = ".childNodes[0].nodeValue DOES work!"&lt;br /&gt;}&lt;br /&gt;// End hiding--&amp;gt;&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;/head&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;body onload="loader()"&amp;gt;&lt;br /&gt;&amp;lt;h1&amp;gt;Changing text with javascript&amp;lt;/h1&amp;gt;&lt;br /&gt;&amp;lt;p&amp;gt; .innerText DOES NOT work.&amp;lt;/p&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;p&amp;gt; .style.content DOES NOT work.&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;lt;p&amp;gt; .innerhtml DOES NOT work.&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;lt;p&amp;gt; .childNodes[0].nodeValue DOES NOT work.&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;/pre&gt;&lt;br /&gt;(Thanks to the users of &lt;a href="http://www.codingforums.com/showthread.php?t=34717&lt;br /&gt;"&gt;codingforums.com&lt;/a&gt;, especially Roy Gardiner, whose code from which this script was adapted.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-7998488112426396151?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7998488112426396151'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7998488112426396151'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/02/javascript-what-is-standard.html' title='Javascript: What is the standard?'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-2326695535330074466</id><published>2007-02-09T21:07:00.000-06:00</published><updated>2007-10-26T21:08:56.511-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>magic SysRq</title><content type='html'>Occasionally I goof up.  Yes, as much as I hate to admit it, it's true.  However, thanks to &lt;a href="http://liquidweather.net/howto/index.php?id=97"&gt;this handy tutorial on liquidweather.net&lt;/a&gt;, I've learned about some nifty ways to kill things in linux.&lt;br /&gt;&lt;br /&gt;In addition to the traditional ways to kill a process, this tutorial lists some magic SysRq key combinations that--if enabled in your kernel--can provide a nice option to just pulling the plug...&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Alt+SysRq+K - Kills all processes (SIGKILL / kill -9)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Alt+SysRq+E - Terminates all processes (SIGTERM / kill -15)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Alt+SysRq+I - Interrupts all processes (SIGINT / kill -2)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Alt+SysRq+U - Force unmount and remount of all filesystems readonly&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Alt+SysRq+S - Syncs all disks&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Alt+SysRq+B - Reboots&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;(Note to self, and any other developers:  When using a language without built-in garbage collection, don't forget to free up memory when you're done!)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-2326695535330074466?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2326695535330074466'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2326695535330074466'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/02/magic-sysrq.html' title='magic SysRq'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-2958546588737408191</id><published>2007-02-08T21:09:00.000-06:00</published><updated>2007-10-26T21:10:43.848-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='wifi'/><category scheme='http://www.blogger.com/atom/ns#' term='networking'/><title type='text'>wifit - an iwconfig tool</title><content type='html'>I've recently installed &lt;a href="http://www.mepis.org/"&gt;Mepis Linux&lt;/a&gt; on a couple of laptops (a Compaq Presario 2195US and a Dell Latitude D610, both of which use &lt;a href="http://ndiswrapper.sourceforge.net/"&gt;NdisWrapper&lt;/a&gt; for wifi drivers).I'm fairly mobile, so I needed a way to quickly change my wireless settings.  I wrote this simple bash script to let me do that, and I thought I'd just share it.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#!/bin/bash&lt;br /&gt;&lt;br /&gt;## wifit - the wifi tool&lt;br /&gt;## This is a script that accepts a wifi-enabled interface, &lt;br /&gt;## essid, and an optional ascii key for a wifi network connection&lt;br /&gt;&lt;br /&gt;if [ $# -lt 2 ] || [ $# -gt 3 ] ; then &lt;br /&gt; echo "wifit - the wifi tool"&lt;br /&gt; echo "-------------------------------------------------"&lt;br /&gt; echo "Usage: wifit &lt;interface&gt; &lt;essid&gt; [Optional: &lt;key&gt;]"&lt;br /&gt; echo " Example: wifit wlan0 somenet "&lt;br /&gt; echo " Example: wifit wlan0 somenet mypassword "&lt;br /&gt; echo "-------------------------------------------------"&lt;br /&gt;else&lt;br /&gt; echo "Setting up wifi connection for "&lt;br /&gt; echo "Interface $1, ESSID: $2..."&lt;br /&gt;&lt;br /&gt; iwconfig $1 essid $2&lt;br /&gt; iwlist $1 scan&lt;br /&gt; iwconfig $1 mode Managed&lt;br /&gt; if [ -n $3 ] ; then&lt;br /&gt;  iwconfig $1 key restricted s:$3&lt;br /&gt; else&lt;br /&gt;  iwconfig $1 key open&lt;br /&gt;  iwconfig ap any&lt;br /&gt; fi&lt;br /&gt; iwconfig commit&lt;br /&gt; dhclient&lt;br /&gt;fi&lt;/pre&gt;  &lt;br /&gt;To use this, just copy and paste the code above into a file named wifit (or wifit.sh, or whatever you want).  You'll then need to make that file executable using chmod:&lt;pre&gt;chmod a+x wifit.sh&lt;/pre&gt;Then, simply call the script (possibly using sudo) passing  it any arguments you need:&lt;pre&gt;sudo ./wifit wlan0 somenetwork&lt;/pre&gt;&lt;br /&gt;Alternatively, you could place this file in your /usr/local/bin or /usr/bin directories so that it would be included in your PATH.  If you find this useful, or if you decide to add to is, feel free to &lt;a href="http://bradmontgomery.net/show.php?page=contact"&gt;Contact Me&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;Enjoy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-2958546588737408191?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2958546588737408191'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2958546588737408191'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/02/wifit-iwconfig-tool.html' title='wifit - an iwconfig tool'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-5549773385995764114</id><published>2007-01-10T21:10:00.000-06:00</published><updated>2007-10-26T21:12:57.539-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OS X'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='Backup'/><title type='text'>Automatic Backups with cron, tar, and SSH</title><content type='html'>Everyone knows that backups are important, but how many of us regularly back up our own websites, blogs, or whatever? Well, I've put together a relatively simple way for my Mac to log into my Linux-based webserver, archive some content, and download it for me.  All of this is done Automatically, too!&lt;br /&gt;&lt;br /&gt;The first thing I had to do, was set up my home machine (a Mac, but any Unix-based system should work, too) so that it could log into a remote host without requiring me to type a password.  This is accomplished by generating a public key, which I can store on my webserver.  To do this, I opened a Terminal, and typed the following:&lt;pre&gt;ssh-keygen -t rsa&lt;/pre&gt; This generated the following output, and I accepted all default values... even the empty passphrase.  This is important, because I don't want to type anything to log into my server!&lt;pre&gt;&lt;br /&gt;Generating public/private rsa key pair.&lt;br /&gt;Enter file in which to save the key (/home/brad/.ssh/id_rsa):&lt;br /&gt;Enter passphrase (empty for no passphrase): &lt;br /&gt;Enter same passphrase again: &lt;br /&gt;Your identification has been saved in /home/brad/.ssh/id_rsa.&lt;br /&gt;Your public key has been saved in /home/brad/.ssh/id_rsa.pub.&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, I need to store the public key (id_rsa.pub) on my webserver. Again, from my Terminal (in my home directory), I issue the following command to log into my webserver (Note that you'd need to change the following command to fit your needs).&lt;br /&gt;&lt;pre&gt;cat .ssh/id_rsa.pub | ssh myusername@mywebserver.com 'cat &gt;&gt; .ssh/authorized_keys'&lt;/pre&gt;&lt;br /&gt;I'll need to type my password here, but after executing this command, I should be able to ssh into my remote server without ever typing my password again!&lt;br /&gt;&lt;br /&gt;Now that I've accomplished that, I need to write some simple bash scripts that will archive my web content. &lt;b&gt;On my Webserver:&lt;/b&gt; For simplicity sake, let's save this in our home directory in a file named www.sh.  Inside that file we put the following shell script, which uses tar to archive my web directory and then calls bzip2 to compress the tar file.  Upon execution, I should have a file with a name similar to 2007-01-10.tar.bz2. Notice that you could also modify this script to invoke mysql_dump or pg_dump if you needed to backup a database as well! &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#!/bin/bash&lt;br /&gt;DATE=`date +%F`  # Grab the date&lt;br /&gt;TARFILE=$DATE-www.tar # Use it to create a filename&lt;br /&gt;tar -cf $TARFILE public_html&lt;br /&gt;bzip2 $TARFILE # compress the tarfile&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now that I have a script that will archive my content, I need to schedule a cron job to run it on a regular basis. I can use the following command to view my cron jobs&lt;pre&gt;crontab -l&lt;/pre&gt;And, to edit my cron jobs, I just use:&lt;pre&gt;crontab -e&lt;/pre&gt;This will open your system's default editor (vi for me), where we need to add the information that tells cron when to run our www.sh script. I added the following: &lt;pre&gt;30 1 * * * ~/www.sh&lt;/pre&gt;This tells cron to run my backup script every morning at 1:30am (system time).  See &lt;a href="http://en.wikipedia.org/wiki/Cron"&gt;wikipedia&lt;/a&gt; for more info on cron.&lt;br /&gt;&lt;br /&gt;Almost done!  I've now got my webserver generating automatic nightly backups, but how can I transfer them to my local machine? Well, we'll make use of our newly generated public key to do this!  First I need to write another simple bash script that will use scp to copy the backup that I generated on the server.&lt;b&gt;On my local machine&lt;/b&gt;, I create a file called transfer.sh, which contains the following:&lt;br /&gt;&lt;pre&gt;#!/bin/bash&lt;br /&gt;DATE=`date +%F` # Grab the date&lt;br /&gt;# I'm downloading file with this name&lt;br /&gt;FILE=$DATE-www.tar.bz2 &lt;br /&gt;&lt;br /&gt;# put the file on my local Desktop&lt;br /&gt;scp myusername@mywebserver.com:~/$FILE ~/Desktop&lt;br /&gt;&lt;br /&gt;# delete the file from the server&lt;br /&gt;ssh myusername@mywebserver.com rm -f ~/$FILE&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now all I have to do is schedule a cron job on my local machine, and I've got an automatic website backup! Since I'm using Mac OS X, this works just like it does on a Linux box.  In my terminal I type the following command:&lt;br /&gt;&lt;pre&gt;crontab -e&lt;/pre&gt;&lt;br /&gt;This opens vi for me, so I type the following, which will exectute my transfer.sh script every morning at 7:30am.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;30 7 * * * $HOME/transfer.sh&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's It! One side note, however... the bash scripts that I've written must be executable by the user under which cron runs.  Usually, the following command will  make a file executable for its owner:&lt;br /&gt;&lt;pre&gt;chmod u+x filename&lt;/pre&gt;&lt;br /&gt;Resources:&lt;br /&gt;&lt;a href="http://www.debian-administration.org/articles/152"&gt;debian-administration.org&lt;/a&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/Cron"&gt;Wikipedia&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.linuxproblem.org/art_9.html"&gt;linuxproblem.org&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-5549773385995764114?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/5549773385995764114'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/5549773385995764114'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/01/automatic-backups-with-cron-tar-and-ssh.html' title='Automatic Backups with cron, tar, and SSH'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-1564602490120715649</id><published>2007-01-05T21:13:00.000-06:00</published><updated>2008-01-09T21:32:23.031-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='postgresql'/><title type='text'>Migrating PHP scripts to MySQL from PostgreSQL</title><content type='html'>I've recently had to work on a project where I needed to convert some very basic &lt;a href="http://php.net"&gt;PHP&lt;/a&gt; code that accessed a &lt;a href="http://www.postgresql.org/"&gt;postgresql&lt;/a&gt; database so that it would work with &lt;a href="http://mysql.com/"&gt;mysql&lt;/a&gt;. For the most part, this has been fairly simple thanks to &lt;a href="http://freshmeat.net/projects/rpl/"&gt;rpl&lt;/a&gt;. Many of PHP's database functions have very similar names, so I simply use rpl to convert the existing code.&lt;br /&gt;Here&amp;rsquo;s a simple bash script that I put together to convert some of my postgresql functions to mysql:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#!/bin/bash&lt;br /&gt;if [ ! -n "$1" ]&lt;br /&gt;then&lt;br /&gt;  echo "Usage: `basename $0` "&lt;br /&gt;  exit 1;&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;X=$1&lt;br /&gt;echo "---- Relacing pg_ with mysql_ in $X"&lt;br /&gt;&lt;br /&gt;rpl -R -x'.php' 'pg_pconnect' 'mysql_pconnect' $X&lt;br /&gt;rpl -R -x'.php' 'pg_connect' 'mysql_connect' $X&lt;br /&gt;rpl -R -x'.php' 'pg_query' 'mysql_query' $X&lt;br /&gt;rpl -R -x'.php' 'pg_fetch_row' 'mysq_fetch_row' $X&lt;br /&gt;rpl -R -x'.php' 'pg_fetch_assoc' 'mysq_fetch_assoc' $X&lt;br /&gt;rpl -R -x'.php' 'pg_close' 'mysq_close' $X&lt;br /&gt;rpl -R -x'.php' 'pg_num_rows' 'mysq_num_rows' $X&lt;br /&gt;rpl -R -x'.php' 'pg_numrows' 'mysq_num_rows' $X&lt;br /&gt;rpl -R -x'.php' 'pg_last_error' 'mysql_error' $X&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Just in case your curious about converting from postgresql or mysql to sqlite, I&amp;rsquo;ve compiled the following table of analogous functions from &lt;a href="http://php.net"&gt;php.net&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;table&gt;&lt;tr&gt;&lt;th style="border:1px solid #aaaaaa; padding: 3px;"&gt;Postgres&lt;/th&gt;&lt;th style="border:1px solid #aaaaaa; padding: 3px;"&gt;MySQL&lt;/th&gt;&lt;th style="border:1px solid #aaaaaa; padding: 3px;"&gt;SQLite&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="border:1px solid #aaaaaa; padding: 3px;"&gt;pg_connect&lt;/td&gt;&lt;td style="border:1px solid #aaaaaa; padding: 3px;"&gt;mysql_connect&lt;/td&gt;&lt;td style="border:1px solid #aaaaaa; padding: 3px;"&gt;sqlite_open // open or create database&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="border:1px solid #aaaaaa; padding: 3px;"&gt;pg_query&lt;/td&gt;&lt;td style="border:1px solid #aaaaaa; padding: 3px;"&gt;mysql_query&lt;/td&gt;&lt;td style="border:1px solid #aaaaaa; padding: 3px;"&gt;sqlite_query&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="border:1px solid #aaaaaa; padding: 3px;"&gt;pg_fetch_row&lt;/td&gt;&lt;td style="border:1px solid #aaaaaa; padding: 3px;"&gt;mysql_fetch_row&lt;/td&gt;&lt;td style="border:1px solid #aaaaaa; padding: 3px;" rowspan="2"&gt;sqlite_fetch_array &lt;small&gt;(ordinal and associative)&lt;/small&gt;&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td style="border:1px solid #aaaaaa; padding: 3px;"&gt;pg_fetch_assoc&lt;/td&gt;&lt;td style="border:1px solid #aaaaaa; padding: 3px;"&gt;mysql_fetch_assoc&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="border:1px solid #aaaaaa; padding: 3px;"&gt;pg_close&lt;/td&gt;&lt;td style="border:1px solid #aaaaaa; padding: 3px;"&gt;mysql_close&lt;/td&gt;&lt;td style="border:1px solid #aaaaaa; padding: 3px;"&gt;sqlite_close&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td style="border:1px solid #aaaaaa; padding: 3px;"&gt;pg_num_rows or pg_numrows&lt;/td&gt;&lt;td style="border:1px solid #aaaaaa; padding: 3px;"&gt;mysql_num_rows&lt;/td&gt;&lt;td style="border:1px solid #aaaaaa; padding: 3px;"&gt;sqlite_num_rows&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-1564602490120715649?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/1564602490120715649'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/1564602490120715649'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/01/going-from-postgresql-to-mysql-in-php.html' title='Migrating PHP scripts to MySQL from PostgreSQL'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-7802155410853194174</id><published>2007-01-03T21:15:00.000-06:00</published><updated>2007-10-26T21:17:06.453-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='networking'/><title type='text'>Blocking outgoing UDP traffic using iptables</title><content type='html'>Since rebuilding my server (after having it used to propogate a UDP flood DoS attack), I&amp;rsquo;ve been advised that I should set up iptables to block any unnecessary outbound UDP traffic.  So, how do I do this?&lt;br /&gt;Well, the first thing I&amp;rsquo;ll do is update my apt repository, and install iptables using the following two commands:&lt;br /&gt;&lt;pre&gt;apt-get update&lt;br /&gt;apt-get install iptables&lt;/pre&gt;&lt;br /&gt;Now, the quick and dirty solution is to just add append a rule that blocks all outgoing UDP packets from my server. You can do this based on the systems IP addess.  Assuming my server&amp;rsquo;s IP addess is 192.168.0.1, I would use the following rule:&lt;pre&gt;iptables -A OUTPUT -s 192.168.0.1 -p udp -j DROP&lt;/pre&gt;&lt;br /&gt;Essentially, this rule says, match any outbound UDP packets whose source address (-s) is 192.168.0.1, and jump (-j) to the DROP chain.  That will drop the packet.  Now, just to be safe, I&amp;rsquo;ll add the same rool using my loopback address, as follows:&lt;pre&gt;iptables -A OUTPUT -s 127.0.0.1 -p udp -j DROP&lt;/pre&gt; &lt;br /&gt;Now, let&amp;rsquo;s just hope that keeps me covered until I can find a little more advanced solution that will also write to a log when a packet gets dropped...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Resources:&lt;/b&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.netfilter.org/documentation/HOWTO/packet-filtering-HOWTO-7.html"&gt;netfilter&amp;rsquo;s documentation&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.linuxquestions.org/questions/showthread.php?t=385165"&gt;linuxquestions.org forums&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The manpage!  (man iptables)&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-7802155410853194174?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7802155410853194174'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/7802155410853194174'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2007/01/blocking-outgoing-udp-traffic-using.html' title='Blocking outgoing UDP traffic using iptables'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-2188106155647904337</id><published>2006-12-31T21:17:00.000-06:00</published><updated>2007-10-26T21:18:46.595-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Building a Debian-Based Webserver</title><content type='html'>Well, my server was recently compromised and used to perform a UPD flood Denial of Service attack (or so my &lt;a href="http://www.1and1.com/?k_id=7289629"&gt;provider&lt;/a&gt; claimed), so I&amp;rsquo;ve had to rebuild my server from scratch.  Now, this seems like a daunting task, but it has actually been quite easy.  I started out with a minimal Debian Linux system, and after a few apt-get install commands, and a few hours of uploading backups, I&amp;rsquo;m up and running again.&lt;br /&gt;&lt;br /&gt;How&amp;rsquo;d I do it?  Well, i just set up my apt-sources:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;### debian&lt;br /&gt;# stable&lt;br /&gt;deb http://http.us.debian.org/debian stable main contrib non-free&lt;br /&gt;deb http://non-us.debian.org/debian-non-US stable/non-US main contrib non-free&lt;br /&gt;deb http://security.debian.org stable/updates main contrib non-free&lt;br /&gt;# testing&lt;br /&gt;deb http://http.us.debian.org/debian testing main contrib non-free&lt;br /&gt;# unstable&lt;br /&gt;deb http://http.us.debian.org/debian unstable main contrib non-free&lt;br /&gt;### dotdeb&lt;br /&gt;deb http://packages.dotdeb.org stable all&lt;br /&gt;deb-src http://packages.dotdeb.org stable all&lt;/pre&gt;&lt;br /&gt;Then I ran the following apt-get commands:&lt;br /&gt;&lt;pre&gt;apt-get update&lt;br /&gt;apt-get upgrade&lt;br /&gt;apt-get install libperl5.8&lt;br /&gt;apt-get install postgresql&lt;br /&gt;apt-get install libapache2-mod-php4&lt;br /&gt;apt-get install php4-pgsql&lt;br /&gt;apt-get install gd&lt;br /&gt;apt-get install php4-cli&lt;/pre&gt;&lt;br /&gt;And I&amp;rsquo;m all set! (yeah... I&amp;rsquo;ve still got a few PHP4 sites, and I grabbed the &lt;a href="http://php.net/gd"&gt;PHP GD library&lt;/a&gt; and the &lt;a href="http://php.net/features.commandline"&gt;PHP command-line Interface&lt;/a&gt;).  Notice that I updated my apt sources before I started installing packages... this is kind of important!  I also knew that I needed some perl libraries before installing &lt;a href="http://www.postgresql.org/"&gt;Postgresql&lt;/a&gt; (my preferred database)  All in all, things went fairly well.  The worst part of (re)building  this server was uploading my backups!  (AND I CANNOT EXPRESS THE IMPORTANCE OF BACKUPS!)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-2188106155647904337?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2188106155647904337'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/2188106155647904337'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2006/12/building-debian-based-webserver.html' title='Building a Debian-Based Webserver'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-6359139082435247171</id><published>2006-12-23T21:18:00.000-06:00</published><updated>2007-10-26T21:20:29.792-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hardware'/><title type='text'>My Wife's New Laptop</title><content type='html'>Well, the semester has ended, so I've been working from home (trying to catch up on all of the "extra work", and, while out to the gym, &lt;a href="http://rosemarie.name"&gt;My wife&lt;/a&gt; gives me a call and starts asking about laptop specs!?  It turns out she's got her eye on a nice little bundle at &lt;a href="http://www.radioshack.com"&gt;Radio Shack&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;It seems they had an &lt;a href="http://hp.com"&gt;HP Pavilion dv2000 notebook&lt;/a&gt; (Turion 64 - 2Ghz/512Mb Ram/100Gb HD/nvidia Geforce go 6150) with a bundled 6.0 megapixel digital camera, and a printer/scanner/copier for less than $1000!  So, she makes a purchase, and the notebook is a surprisingly nice little machine.  It's a little lacking on Memory, but luckily, it seems &lt;a href="http://newegg.com"&gt;NewEgg&lt;/a&gt; has just what we need to fix that.  I'm particularly impressed with the graphics capabilities of this machine (which are usually lacking in notebooks).  &lt;br /&gt;&lt;br /&gt;So, while looking around for something to test its capabilities, I ran across the &lt;a href="http://www.warrock.net/"&gt;War Rock&lt;/a&gt; demo.  I thought I'd just try it out, and it plays quite nicely.  Hopefully, a memory upgrade will provide even greater performance, though. Perhaps one of the best features of this notebook is its display - a widescreen 14 inch, which runs at 1280x800 by default. Despite the relatively small size, it offers a very nice view for a laptop computer.  Overall, I'm quite proud of my wife's purchase, and I've probably used it more than she has!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-6359139082435247171?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/6359139082435247171'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/6359139082435247171'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2006/12/my-wifes-new-laptop.html' title='My Wife&apos;s New Laptop'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-476539276586140598</id><published>2006-12-17T21:20:00.000-06:00</published><updated>2007-10-26T21:21:42.455-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Spam'/><title type='text'>Spammers never die!</title><content type='html'>I've recently received a lot of spam through my &lt;a href="http://bradmontgomery.net/show.php?page=contact"&gt;contact form&lt;/a&gt;, and it's starting to get on my nerves. Apparently the spammers have noticed that many developers are trying to thwart their attempts to sneak extra header information into PHP's &lt;a href="http://php.net/mail"&gt;mail function&lt;/a&gt;, so they've resorted to spamming websites directly!  (or am I the only one?)&lt;br /&gt;&lt;br /&gt;The messages I've been getting actually include a long list of URLs of the form: [URL=http:www.whatever.com]whatever[/URL], so I'm guessing some web app that uses this type of markup has a security hole... Time to patch that up developers!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-476539276586140598?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/476539276586140598'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/476539276586140598'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2006/12/spammers-never-die.html' title='Spammers never die!'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-349748731139717256</id><published>2006-12-02T21:21:00.001-06:00</published><updated>2008-02-15T18:54:58.870-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Family'/><category scheme='http://www.blogger.com/atom/ns#' term='Fun'/><category scheme='http://www.blogger.com/atom/ns#' term='The Girls'/><title type='text'>That's Amazing</title><content type='html'>'Tiz the season... so today I went to Wal-Mart to get a &lt;a href="http://www.walmart.com/catalog/product.do?product_id=3162815"&gt;Pre-Lit Christmas Tree&lt;/a&gt;.  I'm still a little amazed at how easy this thing was to assemble when compared to a real tree of the same size.  Sure it doesn't smell like a real tree, but I don't have to worry about watering it so that it doesn't become a fire hazard.  Besides, &lt;a href="http://thegirls.bradmontgomery.net/"&gt;the girls&lt;/a&gt; really liked it, and that's all that counts!  In fact, Dakota (3yrs) walked into the den, looked at the tree, and just said "That's Amazing". Where does she learn these things!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-349748731139717256?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/349748731139717256'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/349748731139717256'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2006/12/thats-amazing.html' title='That&apos;s Amazing'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-4123748873183487963.post-5502284268658795751</id><published>2006-11-09T21:23:00.000-06:00</published><updated>2007-10-26T21:24:02.173-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>Voice Observation</title><content type='html'>Apparently, I haven't quite beat the flu... or whatever it is that's making me cough.  So head over to my local Medical Practitioner and get a nice pharmaceutical conglomeration injected into my hip.  Apparently, I could also use some antibiotics, too... so I head over to walgreens and drop off the magical note that lets them know it's OK to give me drugs. I wait several hours, and then I discover that my insurance has rejected a claim... Uhm, OK.  So my first thought is "Did someone pull an &lt;a href="http://www.imdb.com/title/tt0151804/"&gt;Office Space&lt;/a&gt; maneuver on me and forget to tell me I don't have a job!"&lt;br /&gt;I hope not.So, I call up my Insurance company to see if there's something wrong, and of course, I get an automated system.  However, instead of giving me a menu and letting me push a button, I'm greated by a voice that asks me to &lt;b&gt;speak&lt;/b&gt; my responses to the menu... (like I'm a dog or something).  Some people call this &lt;a href="http://en.wikipedia.org/wiki/Speech_recognition"&gt;Voice (or Speech) Recognition&lt;/a&gt;.  I didn't notice much recognizing though.  I guess computers aren't designed to interact with people who cough.  Basically the transcript of my conversation with the menu went like this:&lt;br /&gt;&lt;br /&gt;Menu: Do you need information about Health or Dental insurance?&lt;br /&gt;Me: Health (cough)(cough)&lt;br /&gt;Menu: I'm sorry, please say Health or Dental&lt;br /&gt;Me: Health (I then turn my head away from the phone and cough)&lt;br /&gt;Menu: Please say Health or Dental&lt;br /&gt;Me: HEALTH&lt;br /&gt;Menu: Did you say &amp;quot;Start Over?&amp;quot;&lt;br /&gt;Me: Ahhh!&lt;br /&gt;&lt;br /&gt;Menu: Welcome to... *click*&lt;br /&gt;&lt;br /&gt;Yeah, I hung up the phone.  Well... actually I just hit 0, which normally bypasses the automated system and sends you right to an operator.  Unfortunately, their offices were closed.  I guess I'll just call tomorrow and try to see why my insurance was cancelled.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4123748873183487963-5502284268658795751?l=bradmontgomery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/5502284268658795751'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4123748873183487963/posts/default/5502284268658795751'/><link rel='alternate' type='text/html' href='http://bradmontgomery.blogspot.com/2006/11/voice-observation.html' title='Voice Observation'/><author><name>Brad</name><uri>http://www.blogger.com/profile/10280352844796215079</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_FnxSpwZnwaA/TA6zXYcsqQI/AAAAAAAAAf4/U4yeo4BrWJ4/s1600-R/d57aec10399cbb252bd890c2bb3fe1c9.png'/></author></entry></feed>
