<?xml version="1.0" encoding="utf-8"?>
<!-- generator="wordpress/2.0.5" -->
<rss version="2.0" 
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	>

<channel>
	<title>Matt Croydon::Postneo</title>
	<link>http://www.postneo.com</link>
	<description>what comes next?</description>
	<pubDate>Sun, 01 Jun 2008 13:19:46 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.0.5</generator>
	<language>en</language>
			<item>
		<title>Covering Kansas Democratic Caucus Results</title>
		<link>http://www.postneo.com/2008/02/05/covering-kansas-democratic-caucus-results</link>
		<comments>http://www.postneo.com/2008/02/05/covering-kansas-democratic-caucus-results#comments</comments>
		<pubDate>Wed, 06 Feb 2008 01:19:06 +0000</pubDate>
		<dc:creator>Matt Croydon</dc:creator>
		
		<category>Projects</category>

		<category>Django</category>

		<category>Journalism</category>

		<guid isPermaLink="false">http://www.postneo.com/2008/02/05/covering-kansas-democratic-caucus-results</guid>
		<description><![CDATA[I think we&#8217;re about ready for caucus results to start coming in.
We&#8217;re covering the Caucus results at LJWorld.com and on Twitter.
Turnout is extremely heavy.  So much so that they had to split one of the caucus sites in two because the venue was full.
Later&#8230;
How did we do it?
We gained access to the media results [...]]]></description>
			<content:encoded><![CDATA[<p>I think we&#8217;re about ready for caucus results to start coming in.</p>
<p>We&#8217;re covering the Caucus results at <a href="http://ljworld.com/">LJWorld.com</a> and <a href="http://twitter.com/ljworld">on Twitter</a>.</p>
<p>Turnout is extremely heavy.  So much so that they had to split one of the caucus sites in two because the venue was full.</p>
<p><em>Later&#8230;</em></p>
<p>How did we do it?</p>
<p>We gained access to the media results page from <a href="http://ksdp.org/">the Kansas Democratic Party</a> on Friday afternoon.  On Sunday night I started writing a scraper/importer using <a href="http://www.crummy.com/software/BeautifulSoup/">BeautifulSoup</a> and rouging out the <a href="http://www.djangoproject.com/">Django</a> models to represent the caucus data.  I spent Monday refining the models, helper functions, and front-end hooks that our designers would need to visualize the data.  Monday night and in to Tuesday morning was spent finishing off the importer script, exploring <a href="http://code.google.com/apis/chart/">Google Charts</a>, and making sure that Ben and <a href="http://mintchaos.com/">Christian</a> had everything they needed.</p>
<p>After a few hours of sleep, most of the morning was spent testing everything out on our staging server, fixing bugs, and improving performance.  By early afternon Ben was wrapping up <a href="http://ktka.com/">KTKA</a> and Christian was still tweaking his design in Photoshop.  Somewhere between 1 and 2 p.m. he started coding it up and pretty soon we had our results page running on test data on the staging server.</p>
<p>While the designers were finishing up I turned my focus to the planned <a href="http://twitter.com/ljworld">Twitter feed</a>.  Thanks to some handy wrappers from <a href="http://www.b-list.org/">James</a>, I wrote a quick script that generated a short message based on the caucus results we had, compared it to the last version of the message, and <a href="http://twitter.com/ljworld">sent a post to Twitter</a> if the message had changed.</p>
<p style="text-align: center"><a href="http://twitter.com/ljworld"><img src="http://postneo.com/pix/lj_election_twitter.png" /></a></p>
<p>Once results started coming in, we activated <a href="http://www2.ljworld.com/elections/2008/feb/05/races/democratic_presidential_nomination/">our coverage</a>.  After fixing one quick bug, I&#8217;ve been spending most of the evening watching importers feed data in to our databases and watching the twitter script <a href="http://twitter.com/ljworld">send out updates</a>.  Because we&#8217;ve been scraping the <a href="http://www.ksdp.org/">Kansas Democratic Party</a> media results all night and showing them immediately, we&#8217;ve been picking up caucuses seconds after they&#8217;ve been reported and have been ahead of everything else I&#8217;ve looked at.</p>
<p style="text-align: center"><a href="http://www2.ljworld.com/elections/2008/feb/05/races/democratic_presidential_nomination/"><img src="http://postneo.com/pix/ljworld_caucus_results.png" /></a></p>
<p>Because we just recently finished moving our various Kansas Weekly papers to <a href="http://www.ellingtoncms.com/">Ellington</a> and a unified set of templates, it was quite trivial to include detailed election results on the websites for <a href="http://www.lansingcurrent.com/">The Lansing Current</a>, <a href="http://www2.baldwincity.com">Baldwin City Signal</a>, <a href="http://www2.basehorinfo.com">Basehor Sentinel</a>, <a href="http://www2.bonnersprings.com/">The Chieftain</a>, <a href="http://www2.desotoexplorer.com">The De Soto Explorer, <a href="http://www2.eudoranews.com">The Eudora News</a>, <a href="http://www.shawneedispatch.com">Shawnee Dispatch</a>, and <a href="http://www2.tonganoxiemirror.com">The Tonganoxie Mirror</a></p>
<p style="text-align: center"><a href="http://www2.basehorinfo.com/"><img src="http://postneo.com/pix/basehor_election_caucus.png" /></a></p>
<p>While there are definitely things we could have done better as a news organization (there always are), I&#8217;m quite pleased at what we&#8217;ve done tonight.  Our servers hummed along quite nicely all night, we got information to our audience as quickly as possible, and generally things went quite smoothly.  Many thanks to everyone involved.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.postneo.com/2008/02/05/covering-kansas-democratic-caucus-results/feed/</wfw:commentRss>
		</item>
		<item>
		<title>We&#8217;re hiring!</title>
		<link>http://www.postneo.com/2008/01/22/were-hiring</link>
		<comments>http://www.postneo.com/2008/01/22/were-hiring#comments</comments>
		<pubDate>Tue, 22 Jan 2008 15:35:32 +0000</pubDate>
		<dc:creator>Matt Croydon</dc:creator>
		
		<category>Python</category>

		<category>Django</category>

		<category>Journalism</category>

		<guid isPermaLink="false">http://www.postneo.com/2008/01/22/were-hiring</guid>
		<description><![CDATA[Wow, the Django job market is heating up.  I posted a job opening for both junior and senior-level Django developers on djangogigs just a few days ago, and it has already fallen off the front page.
So I&#8217;ll mention it again: We&#8217;re hiring!  We&#8217;re growing and we have several positions open at both the [...]]]></description>
			<content:encoded><![CDATA[<p>Wow, the Django job market is heating up.  I <a href="http://djangogigs.com/gigs/63/">posted a job opening for both junior and senior-level Django developers on djangogigs</a> just a few days ago, and it has already fallen off the front page.</p>
<p>So I&#8217;ll mention it again: <a href="http://djangogigs.com/gigs/63/">We&#8217;re hiring!</a>  We&#8217;re growing and we have several positions open at both the junior and senior level.  We&#8217;d love to talk to you if you&#8217;ve been working with Django since back in the day when everything was a tuple.  We&#8217;d love to talk to you if you&#8217;re smart and talented but don&#8217;t have a lot of (or any) Django experience.</p>
<p>Definitely check out <a href="http://djangogigs.com/gigs/63/">the listing at djangogigs</a> for more, or feel free to <a href="http://www2.ljworld.com/staff/matt_croydon/contact/">drop me a line</a> if you&#8217;d like to know more.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.postneo.com/2008/01/22/were-hiring/feed/</wfw:commentRss>
		</item>
		<item>
		<title>2008 Digital Edge Award Finalists</title>
		<link>http://www.postneo.com/2007/12/20/2008-digital-edge-award-finalists</link>
		<comments>http://www.postneo.com/2007/12/20/2008-digital-edge-award-finalists#comments</comments>
		<pubDate>Thu, 20 Dec 2007 12:42:15 +0000</pubDate>
		<dc:creator>Matt Croydon</dc:creator>
		
		<category>Django</category>

		<category>Journalism</category>

		<guid isPermaLink="false">http://www.postneo.com/2007/12/20/2008-digital-edge-award-finalists</guid>
		<description><![CDATA[The 2008 DIgital Edge Award finalists were just announced, and I&#8217;m excited to see several World Company sites and projects on there as well as a couple of sites running Ellington and even the absolutely awesome Django-powered PolitiFact.com.
At work we don&#8217;t do what we do for awards.  We do it to serve our readers, [...]]]></description>
			<content:encoded><![CDATA[<p>The <a href="http://www.naa.org/blog/digitaledge/1/2007/12/Digital-Edge-Award-Finalists.cfm">2008 DIgital Edge Award finalists</a> were just announced, and I&#8217;m excited to see several World Company sites and projects on there as well as a couple of sites running <a href="http://www.ellingtoncms.com/">Ellington</a> and even the absolutely awesome Django-powered <a href="http://www.politifact.com/">PolitiFact.com</a>.</p>
<p>At work we don&#8217;t do what we do for awards.  We do it to serve our readers, tell a story, get information out there, and do it as best we can.  At the same time even being nominated as finalists is quite an honor, and evokes warm fuzzy feelings in this programmer.</p>
<p>Here are the various World Company projects and sites that were nominated (in the less than 75,000 circulation category):</p>
<ul>
<li>Most Innovative Multimedia Storytelling: <a href="http://www2.ljworld.com/news/24_hours_in_lawrence/2007/may/">24 Hours in Lawrence</a> (<a href="http://www2.ljworld.com/">LJWorld.com</a>)</li>
<li>Best Local Guide or Entertainment Site: <a href="http://www.lawrence.com/">Lawrence.com</a></li>
<li>Best Design and Site Architecture: <a href="http://www2.ljworld.com">LJWorld.com</a></li>
<li>Best Overall News Site: <a href="http://www2.ljworld.com">LJWorld.com</a></li>
</ul>
<p>Not too shabby for a little media company in Kansas.  I&#8217;m particularly excited about the <a href="http://www2.ljworld.com">LJWorld.com</a> nominations since it hasn&#8217;t been too long since we re-designed and re-launched the site with a lot of new functionality.  Scanning the finalists I also see a couple of other sites running <a href="http://www.ellingtoncms.com/">Ellington</a> as well as several special projects by those sites.</p>
<p>As someone who writes software for news organizations for a living I&#8217;m definitely going to take some time this morning to take a look at the other finalists.  I&#8217;m particularly excited to check out projects from names that I&#8217;m not familiar with.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.postneo.com/2007/12/20/2008-digital-edge-award-finalists/feed/</wfw:commentRss>
		</item>
		<item>
		<title>All I want to do is convert my schema!</title>
		<link>http://www.postneo.com/2007/01/16/all-i-want-to-do-is-convert-my-schema</link>
		<comments>http://www.postneo.com/2007/01/16/all-i-want-to-do-is-convert-my-schema#comments</comments>
		<pubDate>Wed, 17 Jan 2007 01:46:52 +0000</pubDate>
		<dc:creator>Matt Croydon</dc:creator>
		
		<category>Web Services</category>

		<category>Projects</category>

		<category>Open Source</category>

		<category>Java</category>

		<category>Python</category>

		<category>Django</category>

		<guid isPermaLink="false">http://www.postneo.com/2007/01/16/all-i-want-to-do-is-convert-my-schema</guid>
		<description><![CDATA[I&#8217;m working on a django in which I want to store GPS track information in GPX format.  The bests way to store that in django is with an XMLField.  An XMLField is basically just a TextField with validation via a RELAX NG Compact schema.
There is a schema for GPX.  Great!  The [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m working on a <a href="http://www.djangoproject.com/">django</a> in which I want to store GPS track information in <a href="http://www.topografix.com/GPX/">GPX</a> format.  The bests way to store that in django is with an <a href="http://www.djangoproject.com/documentation/model_api/#xmlfield">XMLField</a>.  An XMLField is basically just a <a href="http://www.djangoproject.com/documentation/model_api/#textfield">TextField</a> with validation via a <a href="http://www.relaxng.org/">RELAX NG Compact</a> schema.</p>
<p>There is <a href="http://www.topografix.com/GPX/1/1/gpx.xsd">a schema for GPX</a>.  Great!  The schema is an <a href="http://en.wikipedia.org/wiki/XSD">XSD</a> though, but that&#8217;s okay, it&#8217;s a schema for XML so it should be pretty easy to just convert that to RELAX NG compact, right?</p>
<p>Wrong.</p>
<p>I pulled out my handy dandy schema swiss army knife, <a href="http://www.thaiopensource.com/relaxng/trang.html">Trang</a> but was shocked to find out that while it can handle Relax NG (both verbose and compact), DTD, and an XML file as input and even XSD as an output, there was just no way that I was going to be able to coax it to read an XSD.  Trang is one of those things (much like <a href="http://www.thaiopensource.com/relaxng/jing.html">Jing</a> that I rely on pretty heavily that hasn&#8217;t been updated in years.  That scares me a bit, but I keep on using &#8216;em.</p>
<p>With Trang out of the picture, I struck out with various google searches (which doesn&#8217;t happen very often).  <a href="http://javobian.org/</a>Jacob was kind enough to point me at <a href="http://relaxng.org/#conversion">the conversion section of the RELAX NG website</a>.  The first thing that struck my eye was the <a href="http://wwws.sun.com/software/xml/developers/relaxngconverter/">Sun RELAX NG Converter</a>.  Hey, Sun&#8217;s got it all figured out.  I clicked the link and was somewhat confused when I ended up at <a href="http://java.sun.com/xml/">their main XML page</a>.  I scanned around and even searched the site but was unable to find any useful mention of their converter.  A quick google search for <a href="http://www.google.com/search?hl=en&#038;q=sun+%22relax+ng+converter%22&#038;btnG=Google+Search">sun &#8220;relax ng converter&#8221;</a> yielded nothing but people talking about how cool it was and a bunch of confused people (just like me) wondering where they could get it.</p>
<p>At this point I was grasping at straws so I pulled up <a href="http://web.archive.org/web/20021211211928/wwws.sun.com/software/xml/developers/relaxngconverter/">The Internet Archive version of the extinct Sun RELAX NG Converter page</a>.  That tipped me off to the fact that I really needed to start tracking down <code>rngconf.jar</code>.  <a href="http://www.google.com/search?hl=en&#038;lr=&#038;safe=off&#038;q=rngconv&#038;btnG=Search">A google search</a> turned up several Xdoclet and Maven cvs repositories.  I grabbed a copy of the jar but it wouldn&#8217;t work without something called Sun Multi-Schema XML Validator.</p>
<p>That&#8217;s the phrase that pays, folks.</p>
<p>A search for <a href="http://www.google.com/search?hl=en&#038;q=Sun+%22Multi-Schema+XML+Validator%22&#038;btnG=Google+Search">Sun &#8220;Multi-Schema XML Validator&#8221;</a> brought me to <a href="https://msv.dev.java.net/">the java.net project page</a> and included a prominent link to <a href="https://msv.dev.java.net/servlets/ProjectDocumentList?folderID=101">nightly builds of the multi-schema validator as well as nightly builds of rngconv</a>.  These nightly builds are a few months old, but I&#8217;m not going to pick nits at this point.</p>
<p>After downloading msv.zip and rngconv.zip and making sure all the jars were in the same directory I had the tools I needed to convert the XSD in hand to RELAX NG Compact.  First I converted the XSD to RELAX NG Verbose with the following command: <code>java -jar rngconv.jar gpx.xsd > gpxverbose.rng</code>.  That yielded <a href="http://postneo.com/schemas/gpxverbose.rng.txt">the following RELAX NG (very) Verbose schema</a>.  Once I had that I could fall back to trusty Trang to do the rest: <code>trang -I rng -O rnc gpxverbose.rng gpx.rng</code>.  It errored out on <code>any(lax:##other)</code> so I removed that bit and tried again.  After a lot more work than should have been required, I had <a href="http://postneo.com/schemas/gpx.rng.txt">my RELAX NG Compact schema for GPX</a>.</p>
<p>My experience in finding the right tools to convert XSD to RELAX NG was so absurd that I had to write it up, if only to remind myself where to look when I need to do this again in two years.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.postneo.com/2007/01/16/all-i-want-to-do-is-convert-my-schema/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Pardon the Dust</title>
		<link>http://www.postneo.com/2006/12/20/pardon-the-dust</link>
		<comments>http://www.postneo.com/2006/12/20/pardon-the-dust#comments</comments>
		<pubDate>Wed, 20 Dec 2006 12:39:07 +0000</pubDate>
		<dc:creator>Matt Croydon</dc:creator>
		
		<category>Projects</category>

		<category>Weblogs</category>

		<category>PHP</category>

		<category>MySQL</category>

		<category>Python</category>

		<category>Django</category>

		<guid isPermaLink="false">http://www.postneo.com/2006/12/20/pardon-the-dust</guid>
		<description><![CDATA[Sorry about the short outage there.  I finally consolidated the various co-location, shared hosting, and virtual private hosting services that I was consuming every month in to one VPS account.  I still have some legacy URLs to do some rewrite magic for, but the archives back to 2002 is here.
Because my new box [...]]]></description>
			<content:encoded><![CDATA[<p>Sorry about the short outage there.  I finally consolidated the various co-location, shared hosting, and virtual private hosting services that I was consuming every month in to one VPS account.  I still have some legacy URLs to do some rewrite magic for, but <a href="http://www.postneo.com/2002/07/05/">the archives back to 2002</a> is here.</p>
<p>Because my new box is very <a href="http://djangoproject.com/">Django</a>-oriented, I am now running <a href="http://wordpress.org">Wordpress</a> via <a href="http://php.net">PHP5</a> (FastCGI) and <a href="http://www.mysql.com/">MySQL5</a> on <a href="http://www.lighttpd.net">lighttpd</a> behind <a href="http://www.danga.com/perlbal/">perlbal</a>.</p>
<p>One of the things I really enjoyed about the move from Wordpress on Apache with a really gnarly .htaccess file for URL rewriting to lighttpd was the simplicity of it all.  Getting Wordpress to &#8220;just work&#8221; for me on lighttpd was as simple as adding a 404 handler for the site:</p>
<p><code>server.error-handler-404 = "/index.php?error=404"</code></p>
<p>Everything should be smoothing out shortly and of course the eventual goal is to move this blog over to Django trunk.  I did just that <a href="http://postneo.com/pix/django-blog.png">a few months ago</a> but I need to revisit the code, find the importer, and give it a lot of layout love.
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.postneo.com/2006/12/20/pardon-the-dust/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Acknowledging the Mobile Web with Django</title>
		<link>http://www.postneo.com/2006/07/26/acknowledging-the-mobile-web-with-django</link>
		<comments>http://www.postneo.com/2006/07/26/acknowledging-the-mobile-web-with-django#comments</comments>
		<pubDate>Wed, 26 Jul 2006 13:01:45 +0000</pubDate>
		<dc:creator>Matt Croydon</dc:creator>
		
		<category>Python</category>

		<category>Mobile</category>

		<category>Django</category>

		<guid isPermaLink="false">http://www.postneo.com/2006/07/26/acknowledging-the-mobile-web-with-django</guid>
		<description><![CDATA[I was reading up on HowToProvideAlternateViewsForMobileDevices on the Rails wiki this morning and couldn&#8217;t help but notice how much easier it is to set up a mobile version of a Django site.  At World Online we have stripped-down barebones no frills &#8220;all we want are the facts ma&#8217;am&#8221; versions of all of our sites. [...]]]></description>
			<content:encoded><![CDATA[<p><img align="right" alt="KTKA breaking news homepage" title="KTKA breaking news homepage" src="http://postneo.com/pix/ktka_mobile_breaking.jpg" />I was reading up on <a href="http://wiki.rubyonrails.com/rails/pages/HowToProvideAlternateViewsForMobileDevices">HowToProvideAlternateViewsForMobileDevices</a> on the <a href="http://wiki.rubyonrails.com/">Rails wiki</a> this morning and couldn&#8217;t help but notice how much easier it is to set up a mobile version of a <a title="Django" href="http://www.djangoproject.com">Django</a> site.  At World Online we have stripped-down barebones no frills &#8220;all we want are the facts ma&#8217;am&#8221; versions of all of our sites.  They prove extremely useful during <a title="mobile kusports writeup" href="http://www.postneo.com/2006/01/28/mobile-kusports">KU basketball games</a> or <a title="mobile.lawrence.com" href="http://mobile.lawrence.com/restaurants/opennow/">when you&#8217;re in downtown lawrence and want to know what restaurants are open</a>.  Since our mobile sites are just alternate templates on the same views, setup goes something like this:</p>
<p>In <code>main_site.settings</code>:</p>
<pre><code>TEMPLATE_DIRS = (
'/path/to/templates/mainsite.com/',
'/path/to/templates/default/',
)</code></pre>
<p>In <code>mobile_site.settings</code>:</p>
<pre><code>from main_site.settings import *
TEMPLATE_DIRS = (
'/path/to/templates/mobile.mainsite.com/',
'/path/to/templates/default/'
) </code></pre>
<p>The first line imports all of the settings from your main site.  We then overwrite the <code>TEMPLATE_DIRS</code> setting to point to the mobile version of our templates (and fall back to default templates if there isn&#8217;t a mobile specific version).  Because every app that we write also gets a default template we can have a complete mobile site up and running by creating just one or two mobile base templates.</p>
<p>While Django can&#8217;t help you debate internally the &#8220;one web&#8221; versus &#8220;two webs&#8221; philosophies, it can definitely help you produce lightweight mobile-friendly content with minimum effort.
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.postneo.com/2006/07/26/acknowledging-the-mobile-web-with-django/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Clickable Bylines</title>
		<link>http://www.postneo.com/2006/02/18/clickable-bylines</link>
		<comments>http://www.postneo.com/2006/02/18/clickable-bylines#comments</comments>
		<pubDate>Sat, 18 Feb 2006 19:41:12 +0000</pubDate>
		<dc:creator>Matt Croydon</dc:creator>
		
		<category>Python</category>

		<category>Django</category>

		<category>Journalism</category>

		<guid isPermaLink="false">http://www.postneo.com/2006/02/18/clickable-bylines</guid>
		<description><![CDATA[Clickable bylines are the new black in online journalism according to this post and related comments at Poynter.  I have to admit that I thought that this was the norm rather than the exception, since this had been the case at the Journal-World long before I arrived in Lawrence.
A few days ago Dan asked [...]]]></description>
			<content:encoded><![CDATA[<p>Clickable bylines are the new black in online journalism according to <a title="E-Media tidbits" href="http://poynter.org/column.asp?id=31&#038;aid=96755">this post</a> and <a title="Poynter comments" href="http://poynter.org/article_feedback/article_feedback_list.asp?user=&#038;id=96755">related comments</a> at <a title="Poynter" href="http://www.poynter.org">Poynter</a>.  I have to admit that I thought that this was the norm rather than the exception, since this had been the case at the <a title="Lawrence Journal-World" href="http://www.ljworld.com">Journal-World</a> long before I arrived in Lawrence.</p>
<p>A few days ago Dan asked me how long it would take to whip up per-writer RSS feeds.  Thanks to <a title="django syndication framework documentation" href="http://www.djangoproject.com/documentation/syndication/">django&#8217;s syndication framework</a> the answer was no time at all.  Over the next couple of days and with the direction of Dan and David, we tweaked the feeds to include both per-writer and per-photographer feeds.  David made it easy to set up search alerts for every time a staff member posted a story.  We also updated the staff bio pages to make all of this information easier to get to.</p>
<p>Here is an example from <a title="LJWorld story" href="http://www2.ljworld.com/news/2006/feb/13/downtowns_nightlife_boon_or_headache/">a recent story</a> by <a title="Joel's bio page" href="http://www2.ljworld.com/staff/joel_mathis/">Joel Mathis</a>:</p>
<p><img border="0" title="in-story byline" alt="in-story byline" src="http://postneo.com/pix/bylines_story.png" /></p>
<p>If you click on Joel&#8217;s name, you&#8217;ll be taken to <a title="Joel's bio page" href="http://www2.ljworld.com/staff/joel_mathis/">his bio page</a>.  If you click on <a title="Joel's contact form" href="http://www2.ljworld.com/staff/joel_mathis/contact/">Contact</a>, you&#8217;ll be taken directly to his contact form.  There&#8217;s nothing new there (for us anyway).  The new stuff happens on the bio page:</p>
<p><img border="0" title="Joel's bio page" alt="Joel's bio page" src="http://postneo.com/pix/bylines_bio.png" /></p>
<p>The very top of every bio page contains more metainformation than you can shake a stick at.  First and foremost is Joel&#8217;s number and <a title="Joel's contact form" href="http://www2.ljworld.com/staff/joel_mathis/contact/">contact form</a>.  After that we have <a title="RSS feed of Joel's stories" href="http://www2.ljworld.com/rss/authors/joel_mathis/">an RSS feed of his latest stories</a>.  Following that is the <a title="Joel's search alert form" href="http://www2.ljworld.com/searchalerts/create/?query=Joel%20Mathis&#038;idx=ljworld_users&#038;idx=ljworld_blog_entries&#038;idx=ljworld_stories">search alert form</a> that allows you to be notified every time Joel posts a story.  Since Joel is such a converged guy and takes pictures too, you can <a title="Joel's latest photos" href="http://www2.ljworld.com/photos/joel_mathis/">check out the latest photos he has taken</a> or subscribe to <a title="Joel's photocasting feed" href="http://www2.ljworld.com/rss/photographers/joel_mathis/">an RSS feed of those photos</a>.  You can also subscribe to that feed as a photocast in the latest iPhoto.</p>
<p>Joel&#8217;s bio page also also contains a short biography that makes me want to head up the street to <a title="Rudy's pizza" href="http://www.lawrence.com/places/rudys_pizzeria/">Rudy&#8217;s</a> every time I read it.  Below the bio and mugshot is a list of recent stories by Joel and a form that lets you <a title="Joel's personal search page" href="http://www2.ljworld.com/search/?author=Joel+Mathis">quickly search his stories</a>.</p>
<p>I think these tools go well beyond what other news organizations are just beginning to do.  At the same time there is always room for improvement, so don&#8217;t be suprised if more information is added to these pages.
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.postneo.com/2006/02/18/clickable-bylines/feed/</wfw:commentRss>
		</item>
		<item>
		<title>mobile.kusports.com</title>
		<link>http://www.postneo.com/2006/01/28/mobile-kusports</link>
		<comments>http://www.postneo.com/2006/01/28/mobile-kusports#comments</comments>
		<pubDate>Sat, 28 Jan 2006 21:43:26 +0000</pubDate>
		<dc:creator>Matt Croydon</dc:creator>
		
		<category>Python</category>

		<category>Mobile</category>

		<category>Django</category>

		<guid isPermaLink="false">http://www.postneo.com/2006/01/28/mobile-kusports</guid>
		<description><![CDATA[Over the past few days I&#8217;ve been spending some free time and downtime tweaking mobile.kusports.com and adding a couple of really cool (IMHO) features.  We send out a ton of cel phone updates during each game, but I really wanted to bring our awesome live stats to mobile devices.
The first order of business was [...]]]></description>
			<content:encoded><![CDATA[<p>Over the past few days I&#8217;ve been spending some free time and downtime tweaking <a title="mobile.kusports.com" href="http://mobile.kusports.com">mobile.kusports.com</a> and adding a couple of really cool (IMHO) features.  We send out a ton of <a title="kusports cel phone alerts" href="http://www2.kusports.com/alerts/">cel phone updates</a> during each game, but I really wanted to bring our <a title="KUSports livestats" href="http://postneo.com/pix/kusports_livestats.png">awesome live stats</a> to mobile devices.</p>
<p>The first order of business was to see how the low-tech live stats view looked on the mobile site.  Since pretty much all of our templates extend a base template, it looked pretty darn good out of the box (thanks to <a title="Django template inheritance" href="http://www.djangoproject.com/documentation/templates/#template-inheritance">template inheritance</a> and some great default templates by Wilson and David).  Most of the time spent on this template was to condense the stats a little bit to require less scrolling on small devices.  Here is the live stats view using <a title="Opera Mini" href="http://www.opera.com/products/mobile/operamini/">Opera Mini</a> on my <a title="Noki 6682" href="http://www.nokiausa.com/phones/6682">6682</a>:</p>
<p align="center"><img title="mobile.kusports.com live stats" alt="mobile.kusports.com live stats" src="http://postneo.com/pix/kusports_mobile2.jpg" /></p>
<p>Having the live stats accessable from mobile devices is great, but it&#8217;s important to make it extremely easy for someone to get to them if they come to mobile.kusports.com while a game is in progress.  The solution was to present the current score on the home page if and only if a game is in progress.  This was accomplished by <a title="Django template documentation" href="http://www.djangoproject.com/documentation/templates_python/#writing-custom-template-tags"> writing a custom template tag</a> (which took literally a few minutes) and a tweak to the home page template:</p>
<p align="center"><img alt="KUSports mobile home page" title="KUSports mobile home page" src="http://postneo.com/pix/kusports_mobile1.jpg" /></p>
<p>Now the score and the stats are first and foremost while there&#8217;s a game on, but nowhere to be found if there isn&#8217;t.  The last thing I wanted to do was to make sure that the current score (and a link to the live stats) was available from other interior pages in an unobtrusive way.  I figure that if someone is reading a story or viewing photos while a game is on, they probably wouldn&#8217;t mind knowing the current score either.  I decided to present the information without announcing itself quite as loudly as the score on the home page:</p>
<p align="center"><img alt="KUSports mobile interior page" title="KUSports mobile home page" src="http://postneo.com/pix/kusports_mobile3.jpg" /></p>
<p>I hope to do some more tweaking of our mobile sites here and there as free time permits, and I&#8217;ll do my best to highlight the changes here if I think they are particularly clever.
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.postneo.com/2006/01/28/mobile-kusports/feed/</wfw:commentRss>
		</item>
		<item>
		<title>We&#8217;re Moving to Kansas!</title>
		<link>http://www.postneo.com/2005/10/31/moving-to-kansas</link>
		<comments>http://www.postneo.com/2005/10/31/moving-to-kansas#comments</comments>
		<pubDate>Mon, 31 Oct 2005 12:00:12 +0000</pubDate>
		<dc:creator>Matt Croydon</dc:creator>
		
		<category>Projects</category>

		<category>Weblogs</category>

		<category>Open Source</category>

		<category>Python</category>

		<category>Django</category>

		<guid isPermaLink="false">http://www.postneo.com/?p=3791</guid>
		<description><![CDATA[No really, we&#8217;re moving to Kansas.  I&#8217;ve accepted a position at World Online, the online division of the Lawrence Journal-World.  I&#8217;ll be working on some award winning sites including LJWorld.com, lawrence.com, KUsports.com using my favorite web framework: Django.
I&#8217;m really excited about working with an awesome team of people doing some really cool stuff. [...]]]></description>
			<content:encoded><![CDATA[<p>No really, we&#8217;re moving to Kansas.  I&#8217;ve accepted a position at World Online, the online division of the <a href="http://www.ljworld.com">Lawrence Journal-World</a>.  I&#8217;ll be working on some award winning sites including <a href="http://www.ljworld.com">LJWorld.com</a>, <a href="http://www.lawrence.com">lawrence.com</a>, <a href="http://www.kusports.com">KUsports.com</a> using my favorite web framework: <a href="http://www.djangoproject.com">Django</a>.</p>
<p>I&#8217;m really excited about working with an awesome team of people doing some really cool stuff.  And of course I&#8217;m completely stoked about working with <a href="http://www.djangoproject.com">Django</a> on a daily basis.  I&#8217;ll talk about what I&#8217;m up to when I can but there will be times when I have to keep my lips zipped.  I guess now might be a good time to mention that this is my personal weblog and that views/opinions/etc expressed here are mine and do not necessarily reflect those of my employer.</p>
<p>Needless to say I&#8217;ve been a bit busy with getting up to speed at work and planning the move.  I&#8217;ve been meaning to write this post for some time now and had to delete a completely out of date post that I had half written while in Lawrence.  Blogging will probably be light until things settle out, but in the meantime keep an eye on <a href="http://del.icio.us/mcroydon/">my del.icio.us links</a>.</p>
<p>Strap in, Toto!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.postneo.com/2005/10/31/moving-to-kansas/feed/</wfw:commentRss>
		</item>
		<item>
		<title>The Emerging Django Job Market</title>
		<link>http://www.postneo.com/2005/08/31/the-emerging-django-job-market</link>
		<comments>http://www.postneo.com/2005/08/31/the-emerging-django-job-market#comments</comments>
		<pubDate>Wed, 31 Aug 2005 10:11:25 +0000</pubDate>
		<dc:creator>Matt Croydon</dc:creator>
		
		<category>Python</category>

		<category>Django</category>

		<guid isPermaLink="false">http://www.postneo.com/?p=3761</guid>
		<description><![CDATA[A company in Cincinnati, Ohio is looking for a web developer.  Check out the framework they&#8217;re using:

We are developing e-commerce sites based on Python and Django framework.  Experience in Python, object oriented programming, MySql, PostgreSQL, and other web programming technologies is requested.  This person would also be responsible for maintaining other scripts [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://zope.freerecruiting.com/Resumes/Members/8904/index_html">A company in Cincinnati, Ohio is looking for a web developer</a>.  Check out the framework they&#8217;re using:</p>
<blockquote>
<p>We are developing e-commerce sites based on Python and Django framework.  Experience in Python, object oriented programming, MySql, PostgreSQL, and other web programming technologies is requested.  This person would also be responsible for maintaining other scripts on various websites we maintain and host.  The main focus will be on e-commerce development.</p>
</blockquote>
<p>It&#8217;s great to see jobs emerging for this amazing little framework that has been public for just a month and a half.  This one popped up on <a href="http://feedster.com/search.php?q=django&#038;sort=date&#038;ie=UTF-8&#038;hl=&#038;content=full&#038;&#038;limit=15">a feedster search for django</a> in my aggregator.  I can&#8217;t wait to see where we are a year from now.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.postneo.com/2005/08/31/the-emerging-django-job-market/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Django: Big Integer Fields</title>
		<link>http://www.postneo.com/2005/08/26/django-big-integer-fields</link>
		<comments>http://www.postneo.com/2005/08/26/django-big-integer-fields#comments</comments>
		<pubDate>Sat, 27 Aug 2005 01:49:49 +0000</pubDate>
		<dc:creator>Matt Croydon</dc:creator>
		
		<category>Projects</category>

		<category>MySQL</category>

		<category>Python</category>

		<category>Django</category>

		<guid isPermaLink="false">http://www.postneo.com/?p=3759</guid>
		<description><![CDATA[I submitted a patch to Django Ticket #399 (request for a bigint field type).  It still needs testing but works at a quick glance on mysql.  Here&#8217;s a shot of them in action from the admin interface (the input is just too small and just too big respectively):

Update: BigIntegerField works perfectly on PostgreSQL [...]]]></description>
			<content:encoded><![CDATA[<p>I submitted <a href="http://code.djangoproject.com/attachment/ticket/399/bigint_patch.txt">a patch</a> to <a href="http://code.djangoproject.com/ticket/399">Django Ticket #399</a> (request for a bigint field type).  It still needs testing but works at a quick glance on mysql.  Here&#8217;s a shot of them in action from the admin interface (the input is just too small and just too big respectively):</p>
<p align="center"><a href="http://postneo.com/pix/django_bigint.png"><img src="http://postneo.com/pix/django_bigint_tn.png" alt="BigInt Admin" border="0" /></a></p>
<p><strong>Update:</strong> <code>BigIntegerField</code> works perfectly on PostgreSQL but because it doesn&#8217;t have an unsigned integer type (that I can find), <code>PostitiveBigIntegerField</code> isn&#8217;t going to make it all the way up to 18,446,744,073,709,551,615 without using an arbitrary precision <code>NUMERIC</code> or mapping zero to -9,223,372,036,854,775,808.  Both solutions are messy and it would be a shame to have the mysql and postgres backends behave so differently.  As an aside, it looks like this is already the case with mysql&#8217;s <code>IntegerFields</code> being <code>UNSIGNED</code> while Postgres just checks to make sure that the integers are positive before inserting.</p>
<p>The best solution would probably be to employ backend-specific range checking for these monsterous numbers.  That way you won&#8217;t end up out of range in PostgreSQL but you&#8217;re also not penalizing MySQL for being able to count to 18 bajillion.  At this point it would be safe to drop in <code>BigIntegerField</code> as is (as soon as I check it out on sqlite), but <code>PostitiveBigIntegerField</code> still needs some pondering.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.postneo.com/2005/08/26/django-big-integer-fields/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Django Model Syntax Change</title>
		<link>http://www.postneo.com/2005/08/25/django-model-syntax-change</link>
		<comments>http://www.postneo.com/2005/08/25/django-model-syntax-change#comments</comments>
		<pubDate>Fri, 26 Aug 2005 00:38:33 +0000</pubDate>
		<dc:creator>Matt Croydon</dc:creator>
		
		<category>Python</category>

		<category>Django</category>

		<guid isPermaLink="false">http://www.postneo.com/2005/08/25/django-model-syntax-change</guid>
		<description><![CDATA[Adrian has committed the Django model syntax changes.  Current models will have to be rewritten &#8212; once &#8212; but the new syntax looks a lot cleaner and I think it will reduce the learning curve for newcomers to the platform.
Be sure to check out the full documentation as well as a brief screencast highlighting [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.djangoproject.com/weblog/2005/aug/25/modelsyntax/">Adrian has committed the Django model syntax changes</a>.  Current models will have to be rewritten &#8212; once &#8212; but the new syntax looks a lot cleaner and I think it will reduce the learning curve for newcomers to the platform.</p>
<p>Be sure to check out the <a href="http://code.djangoproject.com/wiki/ModelSyntaxChangeInstructions">full documentation</a> as well as <a href="http://www.djangoproject.com/screencasts/model_syntax_change/">a brief screencast highlighting the changes</a>.  I will do my best to update my <a href="http://www.postneo.com/2005/08/17/django-generic-views-crud">CRUD Generic Views tutorial</a> as soon as possible to reflect the syntax change.</p>
<p><strong>Update:</strong> <a href="http://postneo.com/projects/django/tasks.py.txt">Here&#8217;s the updated tasks.py</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.postneo.com/2005/08/25/django-model-syntax-change/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Django Generic Views: CRUD</title>
		<link>http://www.postneo.com/2005/08/17/django-generic-views-crud</link>
		<comments>http://www.postneo.com/2005/08/17/django-generic-views-crud#comments</comments>
		<pubDate>Wed, 17 Aug 2005 12:19:50 +0000</pubDate>
		<dc:creator>Matt Croydon</dc:creator>
		
		<category>Projects</category>

		<category>Python</category>

		<category>Django</category>

		<guid isPermaLink="false">http://www.postneo.com/?p=3743</guid>
		<description><![CDATA[Note: I&#8217;ve not yet updated this to reflect the new model syntax.  For the time being you can take a look at the new model syntax for tasks here.
There are lots of gems buried in Django that are slowly coming to light.  Generic views and specifically the CRUD (create, update, delete) generic views [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Note:</strong> I&#8217;ve not yet updated this to reflect the new model syntax.  For the time being <a href="http://postneo.com/projects/django/tasks.py.txt">you can take a look at the new model syntax for tasks here</a>.</p>
<p>There are lots of gems buried in <a href="http://www.djangoproject.com">Django</a> that are slowly coming to light.  <a href="http://www.djangoproject.com/documentation/generic_views/">Generic views</a> and specifically the CRUD (create, update, delete) generic views are extremely powerful but underdocumented.  This brief tutorial will show you how to make use of CRUD generic views in your Django application.</p>
<p>One of my first encounters with <a href="http://www.rubyonrails.com">Rails</a> was <a href="http://manuals.rubyonrails.com/read/book/7">the simple todo list tutorial</a> which managed to relate lots of useful information by creating a simple yet useful application.  While I will do my best to point out interesting and useful things along the way, it is probably best that you be familiar with <a href="http://www.djangoproject.com/documentation/">the official Django tutorials</a>.  Now would also probably be a good time to mention that this tutorial works for me using <a href="http://www.mysql.com/">MySQL</a> and revision 524.  Django is under constant development, so things may change.  I&#8217;ll do my best to keep up with changes.</p>
<h3>Getting Started</h3>
<p>As with all Django projects, the best place to start is to start with <code>django-admin.py startproject todo</code>.  Make sure that the directory you created your project in is in your <code>PYTHONPATH</code>, then edit <code>todo/settings/main.py</code> to point to the database of your choice.  Now would be a good time to set your <code>DJANGO_SETTINGS_MODULE</code> to <code>"todo.settings.main"</code>. Next move to your <code>apps/</code> dir and create a new application: <code>django-admin.py startapp tasks</code> and <code>django-admin.py init</code>.  The initial setup process is covered in much more detail in <a href="http://www.djangoproject.com/documentation/tutorial1/">tutorial 1</a>.</p>
<h3>The Model</h3>
<p>Now that we have the project set up, let&#8217;s take a look at our rather simple model (<code>todo/apps/tasks/models/tasks.py</code>):</p>
<p><code>
<pre>from django.core import meta

# Create your models here.

class Task(meta.Model):
  fields = (
    meta.CharField('title', maxlength=200),
    meta.TextField('description'),
    meta.DateTimeField('create_date', 'date created'),
    meta.DateTimeField('due_date', 'date due'),
    meta.BooleanField('done'),
  )

  admin = meta.Admin(
    list_display = ( 'title', 'description', 'create_date', 'due_date', 'done' ),
    search_fields = ['title', 'description'],
    date_hierarchy = 'due_date',
  )

  def __repr__(self):
    return self.title</pre>
<p></code></p>
<p>The model is short and sweet, storing a title, description, two dates, and if the task is done or not.   To play with your model in the admin, add the following to <code>INSTALLD_APPS</code> in <code>todo/settings/main.py</code>: <code>'todo.apps.tasks',</code></p>
<p>Feel free to play around with your model using the admin site.  For details, <a href="http://www.djangoproject.com/documentation/tutorial2/">see tutorial 2</a>.</p>
<h3>URL Configuration</h3>
<p>Now let&#8217;s configure our URLs.  We&#8217;ll fill in the code behind these URLs as we go.  I edited <code>todo/settings/urls/main.py</code> directly, but you&#8217;re probably best off decoupling your URLs to your specific app as mentiond in <a href="http://www.djangoproject.com/documentation/tutorial3/">tutorial 3</a>.</p>
<p><code>
<pre>from django.conf.urls.defaults import *

info_dict = {
  'app_label': 'tasks',
  'module_name': 'tasks',
}

urlpatterns = patterns('',
  (r'^tasks/?$', 'todo.apps.tasks.views.tasks.index'),
  (r'^tasks/create/?$', 'django.views.generic.create_update.create_object',
  dict(info_dict, post_save_redirect=&quot;/tasks/&quot;) ),
  (r'^tasks/update/(?P&lt;object_id&gt;d+)/?$',
  'django.views.generic.create_update.update_object', info_dict),
  (r'^tasks/delete/(?P&lt;object_id&gt;d+)/?$',
  'django.views.generic.create_update.delete_object',
  dict(info_dict, post_delete_redirect=&quot;/tasks/new/&quot;) ),
  (r'^tasks/complete/(?P&lt;object_id&gt;d+)/?$',
  'todo.apps.tasks.views.tasks.complete'),
)</pre>
<p></code></p>
<p>Note: I had to alter the formatting of the urlpatterns in order to make them fit.  It looks a lot better in <a href="http://postneo.com/projects/django/urlpatterns.txt">its original formatting</a>.</p>
<p>We use the <code>info_dict</code> to pass information about our application and module to the generic view handlers .  The CRUD generic views need only provide these two pieces of information, but some generic views need more.  See <a href="http://www.djangoproject.com/documentation/generic_views/">the generic views documentation</a> for an explanation.</p>
<p>Let&#8217;s look at each of these URLs one at a time, along with the code behind them.</p>
<h3>Index</h3>
<p><code>(r'^tasks/?$', 'todo.apps.tasks.views.tasks.index'),</code></p>
<p>This points to our index view, which is an index function in <code>todo/apps/tasks/views/tasks.py</code>:</p>
<p><code>
<pre>from django.core import template_loader
from django.core.extensions import DjangoContext as Context
from django.utils.httpwrappers import HttpResponse, HttpResponseRedirect
from django.models.tasks import tasks
from django.core.exceptions import Http404

def index(request):
  notdone_task_list = tasks.get_list(order_by=['-due_date'], done__exact=False)
  done_task_list = tasks.get_list(order_by=['-due_date'], done__exact=True)
  t = template_loader.get_template('tasks/index')
  c = Context(request, {
    'notdone_tasks_list': notdone_task_list,
    'done_tasks_list': notdone_task_list,
  })
  return HttpResponse(t.render(c))</pre>
<p></code></p>
<p>This view creates two lists for us to work with in our template, <code>notdone_tasks_list</code> is (not suprisingly) a list of tasks that are not done yet.  Similarly, <code>done_tasks_list</code> contains a list of tasks that have been completed.  We will use the template <code>tasks/index.html</code> to render this view.</p>
<p>Make sure that you have a template directory defined in todo.settings.main (this refers to <code>todo/settings/main.py</code>).  Here&#8217;s mine:</p>
<p><code>
<pre>TEMPLATE_DIRS = (
 &quot;/home/mcroydon/django/todo/templates&quot;,
)</pre>
<p></code></p>
<p>Now let&#8217;s take a look at the template that I&#8217;m using for the index:</p>
<p><code>
<pre>{% if notdone_tasks_list %}
    &lt;p&gt;Pending Tasks:&lt;/p&gt;
    &lt;ul&gt;
    {% for task in notdone_tasks_list %}
        &lt;li&gt;{{ task.title }}: {{ task.description }} &lt;br/&gt;
          Due {{ task.due_date }} &lt;br/&gt;
          &lt;a href=&quot;/tasks/update/{{ task.id }}/&quot;&gt;Update&lt;/a&gt;
          &lt;a href=&quot;/tasks/complete/{{ task.id }}/&quot;&gt;Complete&lt;/a&gt;
        &lt;/li&gt;
    {% endfor %}
    &lt;/ul&gt;
{% else %}
    &lt;p&gt;No tasks pending.&lt;/p&gt;
{% endif %}
    &lt;p&gt;Completed Tasks:&lt;/p&gt;
    &lt;ul&gt;
{% if done_tasks_list %}
    {% for task in done_tasks_list %}
        &lt;li&gt;{{ task.title }}: {{ task.description }} &lt;br/&gt;
          &lt;a href=&quot;/tasks/delete/{{ task.id }}/&quot;&gt;Delete&lt;/a&gt;
        &lt;/li&gt;
    {% endfor %}
    &lt;/ul&gt;
{% else %}
    &lt;p&gt;No completed pending.&lt;/p&gt;
{% endif %}
&lt;p&gt;&lt;a href=&quot;/tasks/create/&quot;&gt;Add a task&lt;/a&gt;&lt;/p&gt;</pre>
<p></code></p>
<p>Don&#8217;t let this index scare you, it&#8217;s just a little bit of logic, a little looping, and some links to other parts of the application.  See <a href="http://www.djangoproject.com/documentation/templates/">the template authoring guide</a> if you have questions.  Here&#8217;s a picture to give you a better idea as to how the above barebones template renders in <a href="http://www.mozilla.org/products/firefox/">Firefox</a>:</p>
<p align="center"><a href="http://postneo.com/pix/tasks_index.png"><img src="http://postneo.com/pix/tasks_index_tn.png" border="0" alt="Tasks thumbnail" /></a></p>
<h3>Create Generic View</h3>
<p>Now let&#8217;s take a look at the following URL pattern:</p>
<p><code>(r'^tasks/create/?$', 'django.views.generic.create_update.create_object', dict(info_dict, post_save_redirect=&quot;/tasks/&quot;) ),</code></p>
<p>There&#8217;s a lot of magic going on here that&#8217;s going to make your life really easy.  First off, we&#8217;re going to call the <code>create_object</code> generic view every time we visit <code>/tasks/create/</code>.  If we arrive there with a <code>GET</code> request, the generic view displays a form.  Specifically it&#8217;s looking for <code>module_name_form.html</code>.  In our case it will be looking for <code>tasks_form</code>.  It knows what model to look for because of the information we gave it in <code>info_dict</code>.  If however we reach this URL via a <code>POST</code>, the <code>create_object</code> generic view will create a new object for us and then redirect us to the URL of our choice (as long as we give it a <code>post_save_redirect</code>).</p>
<p>Here&#8217;s the template that I am using for <code>tasks_form.html</code>:</p>
<p><code>
<pre>{% block content %}

{% if object %}
&lt;h1&gt;Update task:&lt;/h1&gt;
{% else %}
&lt;h1&gt;Create a Task&lt;/h1&gt;
{% endif %}

{% if form.has_errors %}
&lt;h2&gt;Please correct the following error{{ form.errors|pluralize }}:&lt;/h2&gt;
{% endif %}

&lt;form method="post" action="."&gt;
&lt;p&gt;&lt;label for="id_title"&gt;Title:&lt;/label&gt; {{ form.title }}
{% if form.title.errors %}*** {{ form.title.errors|join:", " }}{% endif %}&lt;/p&gt;
&lt;p&gt;&lt;label for="id_description"&gt;Description:&lt;/label&gt; {{ form.description }}
{% if form.description.errors %}*** {{ form.description.errors|join:", " }}{% endif %}&lt;/p&gt;
&lt;p&gt;&lt;label for="id_create_date_date"&gt;Create Date:&lt;/label&gt; {{ form.create_date_date }}
{% if form.create_date_date.errors %}*** {{ form.create_date_date.errors|join:", " }}{% endif %}&lt;/p&gt;
&lt;p&gt;&lt;label for="id_create_date_time"&gt;Create Time:&lt;/label&gt; {{ form.create_date_time }}
{% if form.create_date_time.errors %}*** {{ form.create_date_time.errors|join:", " }}{% endif %}&lt;/p&gt;
&lt;p&gt;&lt;label for="id_due_date_date"&gt;Due Date:&lt;/label&gt; {{ form.due_date_date }}
{% if form.due_date_date.errors %}*** {{ form.due_date_date.errors|join:", " }}{% endif %}&lt;/p&gt;
&lt;p&gt;&lt;label for="id_due_date_time"&gt;Due Time:&lt;/label&gt; {{ form.due_date_time }}
{% if form.due_date_time.errors %}*** {{ form.due_date_time.errors|join:", " }}{% endif %}&lt;/p&gt;
&lt;p&gt;&lt;label for="id_done"&gt;Done:&lt;/label&gt; {{ form.done }}
{% if form.done.errors %}*** {{ form.done.errors|join:", " }}{% endif %}&lt;/p&gt;
&lt;input type="submit" /&gt;
&lt;/form&gt;
&lt;!--
This is a lifesaver when debugging!
&lt;p&gt; {{ form.error_dict }} &lt;/p&gt;
--&gt;

{% endblock %}</pre>
<p></code></p>
<p>Here&#8217;s what the create template looks like rendered:</p>
<p align="center"><a href="http://postneo.com/pix/tasks_create.png"><img src="http://postneo.com/pix/tasks_create_tn.png" border="0" alt="Create Tasks" /></a></p>
<p>If we fill out the form without the proper (or correctly formatted) information, we&#8217;ll get an error:</p>
<p align="center"><a href="http://postneo.com/pix/tasks_create_error.png"><img src="http://postneo.com/pix/tasks_create_error_tn.png" border="0" alt="Create Tasks" /></a></p>
<h3>Update</h3>
<p><code>(r'^tasks/update/(?P&lt;object_id&gt;\d+)/?$', 'django.views.generic.create_update.update_object', info_dict),</code></p>
<p>This URL pattern handles updates.  The beautiful thing is it sends requests to <code>tasks_form.html</code>, so with a little logic, 90% of the form can be exactly the same as the create form.  If we go to <code>/tasks/create/</code>, we get the blank form.  If we visit <code>/tasks/update/1/</code> we will go to the same form but it will be prepopulated with the data from the task with the ID of 1.  Here&#8217;s the logic that I used to change the header:</p>
<p><code>
<pre>{% if object %}
&lt;h1&gt;Update task:&lt;/h1&gt;
{% else %}
&lt;h1&gt;Create a Task&lt;/h1&gt;
{% endif %}</pre>
<p></code></p>
<p>So if there&#8217;s no object present, we&#8217;re creating.  If there&#8217;s an object present, we&#8217;re updating.  Same form.  Pretty cool.</p>
<p><strong>Warning:</strong> It looks like form.create_date_date and form.create_date_time have broken between the time I wrote this and wrote it up.  This form will not prepopulate the form with the stored information.  There&#8217;s a ticket for this, and I&#8217;ll update as neccesary when it has been fixed.</p>
<h3>Delete</h3>
<p>Here&#8217;s the URL pattern to delte a task:</p>
<p><code>(r'^tasks/delete/(?P&lt;object_id&gt;\d+)/?$', 'django.views.generic.create_update.delete_object', dict(info_dict, post_delete_redirect=&quot;/tasks/new/&quot;) ),</code></p>
<p>There&#8217;s another little Django gem in the delete function.  If we end up at <code>/tasks/delete/1</code> using a <code>GET</code> request, Django will automatically send us to the <code>tasks_form_delete.html</code> template.  This allows us to make sure that the user <strong>really</strong> wanted to delete the task.  here&#8217;s my very simple <code>tasks_form_delete.html</code> template:</p>
<p><code>
<pre>&lt;form method=&quot;post&quot; action=&quot;.&quot;&gt;
&lt;p&gt;Are you sure?&lt;/p&gt;
&lt;input type=&quot;submit&quot; /&gt;
&lt;/form&gt;</pre>
<p></code></p>
<p>Once this form is submitted, the actual delete takes place and we are redirected to the main index page (because we set <code>post_delete_redirect</code>.</p>
<h3>CRUD Generic Views And the Rest of Your Application</h3>
<p>That pretty much covers the basics of the CRUD generic views.  The great thing about generic views is that you can use them along side your custom views.  There&#8217;s no need to do a ton of custom programming for list/detail, date-based, or CRUD since those generic views are available to you.  Because we set up our URL patterns, we can make sure that we craft URLs that look pretty and make sense.</p>
<p>For my sample tasks application I decided that I wanted to create links that would immediately set a task as complete and then redirect to the index.  This is pretty much trivial and can be accomplished by adding the following URL pattern and backing it up with the appropriate view.  Here&#8217;s the pattern:</p>
<p><code>(r'^tasks/complete/(?P&lt;object_id&gt;\d+)/?$', 'todo.apps.tasks.views.tasks.complete'),</code></p>
<p>And here&#8217;s the view that goes along with it (from <code>todo/apps/tasks/models/tasks.py</code>):</p>
<p><code>
<pre>def complete(request, object_id):
  try:
    t = tasks.get_object(pk=object_id)
  except:
    # do something better than this
    raise Http404
  try:
    t.done=True
    t.save()
    return HttpResponseRedirect('/tasks/')
  except:
    # do something better than this
    raise Http404</pre>
<p></code></p>
<p>I do plan to actually handle errors and respond accordingly, but it was late last night and I just wanted to see it work (and it does).</p>
<h3>Conclusion</h3>
<p>Django rocks.  Generic views rock.  The framework and specifically the generic views make your life easy.  My little tasks app took a few hours to put together, but a significant portion of that was reading up on the documentation, trying to figure out generic views using the existing docs and reading the source, and of course pestering the DjangoMasters about generic views and other stuff on #django (thanks all).</p>
<p>I hope this overview of CRUD generic views helps, but if anything confuses you, don&#8217;t hesitate to comment or get in touch with me (matt at ooiio dot com).  Also expect to see updates to this tutorial as APIs change and I get a little more time to clean up my code.</p>
<p>Feel free to download and play with my little todo app: <a href="http://postneo.com/projects/django/todo-tutorial.tar.gz">todo-tutorial.tar.gz</a> or <a href="http://postneo.com/projects/django/todo-tutorial.zip">todo-tutorial.zip</a>.  Consider them released under a <a href="http://www.opensource.org/licenses/bsd-license.php">BSD-style license</a>.  Above all, don&#8217;t sue me.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.postneo.com/2005/08/17/django-generic-views-crud/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Rob Curley Blew My Mind</title>
		<link>http://www.postneo.com/2005/08/14/rob-curley-blew-my-mind</link>
		<comments>http://www.postneo.com/2005/08/14/rob-curley-blew-my-mind#comments</comments>
		<pubDate>Sun, 14 Aug 2005 22:18:26 +0000</pubDate>
		<dc:creator>Matt Croydon</dc:creator>
		
		<category>Weblogs</category>

		<category>Python</category>

		<category>Django</category>

		<guid isPermaLink="false">http://www.postneo.com/?p=3741</guid>
		<description><![CDATA[A few days back I listened to this IMA Keynote by Rob Curley on IT Conversations.  It blew my mind.  I started out looking for some insight in to the team behind Django, The Lawrence Journal-World, lawrence.com, and so on.  I got a whole lot more than I had bargained for.
If you&#8217;re [...]]]></description>
			<content:encoded><![CDATA[<p>A few days back I listened to <a href="http://www.itconversations.com/shows/detail550.html">this IMA Keynote by Rob Curley</a> on <a href="http://www.itconversations.com">IT Conversations</a>.  It blew my mind.  I started out looking for some insight in to the team behind <a href="http://www.djangoproject.com/">Django</a>, <a href="http://www.ljworld.com/">The Lawrence Journal-World</a>, <a href="http://www.lawrence.com/">lawrence.com</a>, and so on.  I got a whole lot more than I had bargained for.</p>
<p>If you&#8217;re involved in old media, new media, any media, or just want a glimpse of the future (I hope), please have a listen.  It will be very interesting to see what happens in the next few months as Django takes flight, <a href="http://www.poynter.org/column.asp?id=31&#038;aid=84692">Rob heads to Naples</a> (with technical backup from <a href="http://eric.themoritzfamily.com/">Eric</a>) and <a href="http://www.holovaty.com/blog/archive/2005/08/03/0202">Adrian telecommutes to The Washington Post</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.postneo.com/2005/08/14/rob-curley-blew-my-mind/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Django Markup Template Tags</title>
		<link>http://www.postneo.com/2005/08/10/django-markup-template-tags</link>
		<comments>http://www.postneo.com/2005/08/10/django-markup-template-tags#comments</comments>
		<pubDate>Wed, 10 Aug 2005 19:45:45 +0000</pubDate>
		<dc:creator>Matt Croydon</dc:creator>
		
		<category>Python</category>

		<category>Django</category>

		<guid isPermaLink="false">http://www.postneo.com/?p=3731</guid>
		<description><![CDATA[Changeset 467 contains template tags for renderingTextile (using PyTextile), Markdown (using python-markdown), and ReStructured Text (using docutils).  You can find usage examples and some basic tests in markup.py.
You need to make sure that you have the correct modules installed to do the heavy lifting, but the tags are extremely easy to use:

{{ textile_content&#124;textile }}
{{ [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://code.djangoproject.com/changeset/467">Changeset 467</a> contains <a href="http://www.djangoproject.com/documentation/templates/#built-in-tag-reference">template tags</a> for rendering<a href="http://textism.com/tools/textile/">Textile</a> (using <a href="http://dealmeida.net/en/Projects/PyTextile/">PyTextile</a>), Markdown (using <a href="http://www.freewisdom.org/projects/python-markdown/">python-markdown</a>), and <a href="http://docutils.sourceforge.net/rst.html">ReStructured Text</a> (using <a href="http://docutils.sourceforge.net/">docutils</a>).  You can find usage examples and some basic tests in <a href="http://code.djangoproject.com/file/django/trunk/tests/othertests/markup.py?rev=467">markup.py</a>.</p>
<p>You need to make sure that you have the correct modules installed to do the heavy lifting, but the tags are extremely easy to use:</p>
<ul>
<li><code>{{ textile_content|textile }}</code></li>
<li><code>{{ markdown_content|markdown }}</code></li>
<li><code>{{ rest_content|restructuredtext }}</code></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.postneo.com/2005/08/10/django-markup-template-tags/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Django: Tutorial 4 and RSS</title>
		<link>http://www.postneo.com/2005/08/08/django-tutorial-4-and-rss</link>
		<comments>http://www.postneo.com/2005/08/08/django-tutorial-4-and-rss#comments</comments>
		<pubDate>Mon, 08 Aug 2005 21:10:59 +0000</pubDate>
		<dc:creator>Matt Croydon</dc:creator>
		
		<category>Python</category>

		<category>Django</category>

		<guid isPermaLink="false">http://www.postneo.com/?p=3724</guid>
		<description><![CDATA[Today Adrian posted Django Tutorial 4 which covers form processing and generic views.  Everyone dive in!
In other news, if you&#8217;re looking to generate RSS using Django &#8220;the easy way,&#8221; look no further than this #django log and associated pastebin.
As always, svn up if you haven&#8217;t in the last few hours.
Update: The pastebin dissapeared, so [...]]]></description>
			<content:encoded><![CDATA[<p>Today Adrian posted <a href="http://www.djangoproject.com/documentation/tutorial4">Django Tutorial 4</a> which covers form processing and generic views.  Everyone dive in!</p>
<p>In other news, if you&#8217;re looking to generate RSS using Django &#8220;the easy way,&#8221; look no further than <a href="http://loglibrary.com/show_page/view/179?Multiplier=3600&#038;Interval=6&#038;StartTime=1123348943">this #django log</a> and <a href="http://pastebin.com/330893">associated pastebin</a>.</p>
<p>As always, <code>svn up</code> if you haven&#8217;t in the last few hours.</p>
<p><strong>Update:</strong> The pastebin dissapeared, so <a href="http://postneo.com/projects/django/pastebin.txt">here&#8217;s a transcribed version of what was in there</a>.  Typos are mine and not Adrian&#8217;s of course.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.postneo.com/2005/08/08/django-tutorial-4-and-rss/feed/</wfw:commentRss>
		</item>
		<item>
		<title>HTTP Headers and Django</title>
		<link>http://www.postneo.com/2005/08/08/http-headers-and-django</link>
		<comments>http://www.postneo.com/2005/08/08/http-headers-and-django#comments</comments>
		<pubDate>Mon, 08 Aug 2005 07:09:06 +0000</pubDate>
		<dc:creator>Matt Croydon</dc:creator>
		
		<category>Python</category>

		<category>Django</category>

		<guid isPermaLink="false">http://www.postneo.com/2005/08/08/http-headers-and-django</guid>
		<description><![CDATA[Andrew Brehaut has a hot tip about HTTP headers and Django:
HttpResponse objects contain a headers dictionary, that you can easily add and remove headers from using dictionary notation

You can read more about it and see usage examples in his post.
]]></description>
			<content:encoded><![CDATA[<p><a href="http://brehaut.net/blog/2005/08/08/sending-http-headers-with-django/">Andrew Brehaut has a hot tip</a> about HTTP headers and <a href="http://www.djangoproject.com">Django</a>:</p>
<blockquote><p><code>HttpResponse</code> objects contain a headers dictionary, that you can easily add and remove headers from using dictionary notation</p>
</blockquote>
<p>You can read more about it and see usage examples in his post.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.postneo.com/2005/08/08/http-headers-and-django/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Migrating Your App to Django</title>
		<link>http://www.postneo.com/2005/08/05/migrating-your-app-to-django</link>
		<comments>http://www.postneo.com/2005/08/05/migrating-your-app-to-django#comments</comments>
		<pubDate>Sat, 06 Aug 2005 01:44:24 +0000</pubDate>
		<dc:creator>Matt Croydon</dc:creator>
		
		<category>Python</category>

		<category>Django</category>

		<guid isPermaLink="false">http://www.postneo.com/?p=3716</guid>
		<description><![CDATA[A few days ago I mentioned Changeset 384 which included a new command, django-admin.py inspectdb &#60;dbname&#62;.  It has also been tweaked and improved since it was initially committed.  The other day I tried it out on a simple database structure, but I decided to throw a more complex example at it.
I decided to [...]]]></description>
			<content:encoded><![CDATA[<p>A few days ago <a href="http://www.postneo.com/2005/08/03/the-django-shuffle">I mentioned</a> <a href="http://code.djangoproject.com/changeset/384">Changeset 384</a> which included a new command, django-admin.py inspectdb &lt;dbname&gt;.  It has also been tweaked and improved since it was initially committed.  The other day I tried it out on a simple database structure, but I decided to throw a more complex example at it.</p>
<p>I decided to take the final depot application from the excellent <a href="http://www.pragmaticprogrammer.com/titles/rails/index.html">Agile Web Development with Rails</a> book.  Beta books rule by the way.  I executed the SQL in <code>rails-code/depot_final/db/create.sql</code> from the <a href="http://media.pragprog.com/titles/rails/code/rails-code.tgz">the code tarball</a> to set up the database structure.  I then created a new project with <code>django-admin startproject</code> and edited <code>settings/main.py</code> to tell Django how to log in to my mysql database.  After exporting the correct <code>DJANGO_SETTINGS_MODULE</code> I ran <code>django-admin.py inspectdb depot_rails</code> which gave me the following model:</p>
<pre><code># This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
#     * Rearrange models' order
#     * Add primary_key=True to one field in each model.
# Feel free to rename the models, but don't rename
# db_table values or field names.
#
# Also note: You'll have to insert the output of
# 'django-admin.py sqlinitialdata [appname]'
# into your database.

from django.core import meta

class LineItem(meta.Model):
    db_table = 'line_items'
    fields = (
        meta.IntegerField('id'),
        meta.IntegerField('product_id'),
        meta.IntegerField('order_id'),
        meta.IntegerField('quantity'),
        meta.FloatField('unit_price'),
    )

class Order(meta.Model):
    db_table = 'orders'
    fields = (
        meta.IntegerField('id'),
        meta.CharField('name', maxlength=100),
        meta.CharField('email', maxlength=255),
        meta.TextField('address'),
        meta.CharField('pay_type', maxlength=10),
        meta.DateTimeField('shipped_at'),
    )

class Product(meta.Model):
    db_table = 'products'
    fields = (
        meta.IntegerField('id'),
        meta.CharField('title', maxlength=100),
        meta.TextField('description'),
        meta.CharField('image_url', maxlength=200),
        meta.FloatField('price'),
        meta.DateTimeField('date_available'),
    )

class User(meta.Model):
    db_table = 'users'
    fields = (
        meta.IntegerField('id'),
        meta.CharField('name', maxlength=100),
        meta.CharField('hashed_password', maxlength=40),
    )</code></pre>
<p>Per the comment block at the top, you&#8217;re not home free yet, but at lot of tedious work has been done for you.  This should definitely jumpstart the porting of existing applications to the Django platform.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.postneo.com/2005/08/05/migrating-your-app-to-django/feed/</wfw:commentRss>
		</item>
		<item>
		<title>The Django Shuffle</title>
		<link>http://www.postneo.com/2005/08/03/the-django-shuffle</link>
		<comments>http://www.postneo.com/2005/08/03/the-django-shuffle#comments</comments>
		<pubDate>Wed, 03 Aug 2005 11:02:34 +0000</pubDate>
		<dc:creator>Matt Croydon</dc:creator>
		
		<category>Python</category>

		<category>Django</category>

		<guid isPermaLink="false">http://www.postneo.com/?p=3711</guid>
		<description><![CDATA[Lots of changes are happening in the newly formed Django world these days.  Tons of bugfixes and feature additions have been streaming in to the subversion repository.  One of my recent favorites is Changeset 384 which adds a django-admin.py inspectdb command.  It&#8217;s not perfect yet but it should help out people trying [...]]]></description>
			<content:encoded><![CDATA[<p>Lots of changes are happening in the newly formed <a href="http://www.djangoproject.com">Django</a> world these days.  Tons of bugfixes and feature additions have been streaming in to the subversion repository.  One of my recent favorites is <a href="http://code.djangoproject.com/changeset/384">Changeset 384</a> which adds a <code>django-admin.py inspectdb</code> command.  It&#8217;s not perfect yet but it should help out people trying to integrate existing databases with Django.  The new command will do its darndest to output a Django model given a particular database name.</p>
<p>In other news, <a href="http://eric.themoritzfamily.com/?p=48">congrats to Eric (<code>slashzero</code>) on the new gig in Naples</a> and <a href="http://www.holovaty.com/blog/archive/2005/08/03/0202">to Adrian (<code>adrian_h</code>) on his new gig at The Washington Post</a>.</p>
<p><strong>Update:</strong> Hugo&#8217;s at it again and has notes on <a href="http://hugo.muensterland.org/2005/08/03/django-apache-and-fcgi/">using Django with Apache and mod_fcgi</a> which build on his experience with <a href="http://hugo.muensterland.org/2005/07/27/django-lighttpd-and-fcgi-second-take/">Django, lighthttpd and FastCGI</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.postneo.com/2005/08/03/the-django-shuffle/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Django on Dreamhost via FastCGI</title>
		<link>http://www.postneo.com/2005/07/27/django-on-dreamhost-via-fastcgi</link>
		<comments>http://www.postneo.com/2005/07/27/django-on-dreamhost-via-fastcgi#comments</comments>
		<pubDate>Wed, 27 Jul 2005 23:02:45 +0000</pubDate>
		<dc:creator>Matt Croydon</dc:creator>
		
		<category>Python</category>

		<category>Django</category>

		<guid isPermaLink="false">http://www.postneo.com/?p=3703</guid>
		<description><![CDATA[Thanks to the hard work by Hugo and an excellent efford by skabber and jdanks, there are now instructions for installing Django on Dreamhost using FastCGI on the Dreamhost Wiki.
I&#8217;m still amazed at how quickly Django development is happening and how quickly a community is being built out around it.  Keep an eye on [...]]]></description>
			<content:encoded><![CDATA[<p>Thanks to <a href="http://hugo.muensterland.org/2005/07/27/django-lighttpd-and-fcgi-second-take/#more-4959">the hard work by Hugo</a> and <a href="http://www.socialistsoftware.com/?p=9">an excellent efford by skabber</a> and jdanks, there are now instructions for <a href="http://wiki.dreamhost.com/index.php/Django">installing Django on Dreamhost using FastCGI</a> on <a href="http://wiki.dreamhost.com/index.php/Main_Page">the Dreamhost Wiki</a>.</p>
<p>I&#8217;m still amazed at how quickly <a href="http://www.djangoproject.com">Django</a> development is happening and how quickly a community is being built out around it.  Keep an eye on the <a href="http://code.djangoproject.com/timeline/">Django Trac timeline</a> for a glimpse at the latest and greatest.</p>
<p><strong>Update:</strong> Sorry about the mislink, Jay.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.postneo.com/2005/07/27/django-on-dreamhost-via-fastcgi/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
