<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>zoia.org &#187; Programming</title>
	<atom:link href="http://www.zoia.org/blog/category/programming/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.zoia.org/blog</link>
	<description>Por Roberto Zoia</description>
	<lastBuildDate>Thu, 22 Dec 2011 22:07:39 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Usos no convencionales del servicio DNS</title>
		<link>http://www.zoia.org/blog/2009/01/05/usos-no-convencionales-dns/</link>
		<comments>http://www.zoia.org/blog/2009/01/05/usos-no-convencionales-dns/#comments</comments>
		<pubDate>Mon, 05 Jan 2009 13:00:11 +0000</pubDate>
		<dc:creator>Roberto</dc:creator>
				<category><![CDATA[Hack]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[dns]]></category>
		<category><![CDATA[dns non conventional uses]]></category>
		<category><![CDATA[dns tunneling]]></category>
		<category><![CDATA[ip-over-dns]]></category>
		<category><![CDATA[txt]]></category>
		<category><![CDATA[wikipedia]]></category>

		<guid isPermaLink="false">http://www.zoia.org/blog/?p=322</guid>
		<description><![CDATA[David Leadbeater ha implementado un servicio que devuelve artículos resumidos de Wikipedia via el record TXT del servicio DNS. (Ver Wikipedia over DNS, via Hackzine.) Lo que me ha parecido interesante aquí es el uso no convencional del servicio DNS. Leadbeater está usando el subdominio como si fuera la llave de un diccionario (me refiero [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>David Leadbeater ha implementado un servicio que devuelve artículos resumidos de Wikipedia via el record TXT del servicio DNS. (Ver <a href="https://dgl.cx/wikipedia-dns">Wikipedia over DNS</a>, via <a href="http://www.hackszine.com/blog/archive/2009/01/wikipedia_over_dns.html?CMP=OTC-7G2N43923558">Hackzine</a>.)</p>

<p>Lo que me ha parecido interesante aquí es el uso no convencional del servicio <a href="http://es.wikipedia.org/wiki/Domain_Name_System">DNS</a>.  Leadbeater está usando  el subdominio como si fuera la llave de un diccionario (me refiero a diccionario en el sentido de <em>hashkey</em>).   Envía una solicitud DNS preguntando por el dominio <em>key</em>.example.com, y el servidor DNS devuelve la data asociada a <em>key</em>.</p>

<p>Leadbeater no da el código del programa (por lo menos no lo he encontrado en la página), pero no sería  complicado escribir un servicio parecido en Python (ver, por ejemplo, <a href="http://www.tranquilidadtecnologica.com/2006/04/servidor-fake-dns-en-python.html">Servidor Fake DNS en Python</a>)</p>

<p>Otra posibilidad es usar el servicio DNS para crear un <em>tunnel</em> entre nuestro equipo, supuestamente dentro de una &#8220;red hostil&#8221;, y un servidor remoto de nuestra propiedad que se encuentre fuera de esta red.  Este <em>tunnel</em> permite atravesar, en casi todos los casos, las restricciones que tenga la &#8220;red hostil&#8221; para acceder a Internet, sin que nadie pueda evitarlo fácilmente.  Esto se llama IP-over-DNS.</p>

<p>Esta cita de <a href="http://dnstunnel.de">dnstunnel.de</a> da idea de algunos usos:</p>

<blockquote>Did you ever sit at the airport or at a cafe and there was a unencrypted wireless access point nearby, but whenever you wanted to visit a site their website would pop up asking for a fee to use the internet through their AccessPoint (aka. Captive Portal)?</blockquote>

<p>(Esto, casi seguro, no es legal y no me parece un uso correcto.  Pero hay otros usos que sí lo son, y es algo que debe tener en cuenta el administrador de la red)</p>

<p>Algunos links interesantes que tienen que ver con este tema y con IP-over-DNS:</p>

<ul>
<li><a href="http://psichron.za.net/downloads/dns_tunneling.txt">DNS tunneling</a></li>
<li><a href="http://dnstunnel.de">DNSTunnel</a> </li>
<li>Un tutorial sobre <a href="http://thomer.com/howtos/nstx.html">/c</a> </li>
<li><a href="http://www.daemon.be/maarten/dnstunnel.html">DNS tunneling</a> </li>
<li><a href="http://savannah.nongnu.org/projects/nstx/">NSTX</a> (Nameserver Transfer Protocol) Makes it possible to create IP tunnels using DNS queries and replies for IP packet encapsulation where IP traffic other than DNS isn&#8217;t possible. </li>
<li><a href="http://code.kryo.se/iodine">iodine</a>. Hace lo mismo que NSTX pero funciona en MacOs. Aquí un <a href="http://www.brool.com/?p=94">tutorial sobre iodine</a>.</li>
<li>El <a href="http://www.tranquilidadtecnologica.com/2006/04/servidor-fake-dns-en-python.html">FakeDNS Server</a> escrito en Python que menciono arriba (el código podría ser más elegante&#8230; parece C)</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.zoia.org/blog/2009/01/05/usos-no-convencionales-dns/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Amarok, Songbird y modelos de desarrollo</title>
		<link>http://www.zoia.org/blog/2008/07/30/songbird-en-vez-de-amarok/</link>
		<comments>http://www.zoia.org/blog/2008/07/30/songbird-en-vez-de-amarok/#comments</comments>
		<pubDate>Wed, 30 Jul 2008 12:00:57 +0000</pubDate>
		<dc:creator>Roberto</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[amarok]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[media player]]></category>
		<category><![CDATA[music player]]></category>
		<category><![CDATA[necesidades reales]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[songbird]]></category>

		<guid isPermaLink="false">http://www.zoia.org/blog/?p=260</guid>
		<description><![CDATA[Estaba investigando cómo instalar Amarok en MacOs, pero finalmente no hizo falta, pues encontré (mejor dicho, re-descubrí) Songbird. Es gratis, hay versiones para MacOs, Linux y Windows. Además de una interfaz pulida, Songbird, como Amarok, permite extensiones (por ejemplo, el LyricsPlugin, como el que hay para Winamp o para Windows Media Player, y similar al [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>Estaba investigando cómo instalar Amarok en MacOs, pero finalmente no hizo falta, pues encontré (mejor dicho, re-descubrí) <a href="http://getsongbird.com/">Songbird</a>.  Es gratis, hay versiones para MacOs, Linux y Windows.  Además de una interfaz pulida, Songbird, como Amarok, permite extensiones (por ejemplo, el LyricsPlugin, como el que hay para Winamp o para Windows Media Player, y similar al que mostraba en el <a href="http://www.zoia.org/blog/2008/06/25/amarok-un-excelente-manejador-de-musica/">post anterior</a> sobre Amarok).  Y Songbird, al igual que Amarok, respeta mi estructura de directorio:  no se empeña en mover las canciones a &#8220;su&#8221; carpeta.  Recomendado.</p>

<p>&lt;a href=&#8221;http://www.zoia.org/blog/wp-content/uploads/2008/07/songbird.png&#8221; title=&#8221;songbird.png rel=&#8221;lightbox&#8221;><img src="http://www.zoia.org/blog/wp-content/uploads/2008/07/songbird-thumb.png" alt="songbird.png"  /></p>

<h2>Cuál es el problema que quieres resolver</h2>

<p>El post podría terminar con la foto de arriba, pero quería hacer unas consideraciones sobre dos enfoques distintos en el desarrollo de software.</p>

<p>Tanto Amarok como Songbird se proponen objetivos parecidos:  ser una alternativa a los <em>media players</em> tradicionales, proporcionando una buena experiencia de uso.  De sus respectivos websites:</p>

<ul>
<li><cite>Amarok is the music player for Linux and Unix with an intuitive interface. Amarok makes playing the music you love easier than ever before &#8211; and looks good doing it</cite></li>
<li><cite>Songbird is a highly customizable alternative to traditional desktop media players</cite></li>
</ul>

<p>La version 1.x de Amarok para Linux cumple bien este objetivo.  Basta seleccionar Amarok en el instalador de paquetes para que Linux descargue Amarok y lo instale.  Y funciona bien.</p>

<p>Desde hace algún tiempo el equipo de desarrollo de Amarok viene anunciando la versión 2.0, que correría también en MacOs (ver el <a href="http://amarok.kde.org/wiki/FAQ#Amarok_2.0_FAQ">wiki</a>) y en Windows.  Sin embargo, ahora que la versión 2.0 está en Alpha (es decir, se puede instalar, pero todavía tiene bastante por pulir), empiezan a decir que las versiones de MacOs y Windows <a href="http://www.sebruiz.net/49">no son una prioridad</a>, porque la mayoría de sus desarrolladores trabajan con software libre.  ¿Y antes no era así?</p>

<p>También están los cambios que se introducen en la versión 2.0 de Amarok.  El <a href="http://amarok.kde.org/wiki/FAQ#Amarok_2.0_FAQ">FAQ</a> explica sin empacho que no funcionarán los antiguos <em>scripts</em> (las extensiones); que lo más seguro es que los usuarios de la versión 1.x  pierdan sus <em>playlists</em> y los ranking de las canciones&#8230; salvo que uno de sus desarrolladores <em>logre</em> terminar un programa para convertirlos.  Y, lo siento, se podrán ver videos pero no habrá un manejador de video propiamente.  Los videos serán un archivo más (¿sólo que sin ranking, etc.?).</p>

<p>Si la idea de Amarok es una mejor experiencia para el usuario, ¿por qué romper con todo lo anterior? Si el formato interno de los playlists y ranking cambia, ¿no sería una característica importante que Amarok 2.0 reconozca <em>sin ninguna falla</em>, <em>de modo transparente para el usuario</em>, los de la versión 1.x? No, lo que nos dicen es que un desarrollador está haciendo algo en esa línea, quizá lo incluyan, quizá no.</p>

<p>Mi diagnóstico es que el equipo de Amarok ha perdido de vista qué necesidad resuelve su programa.  Están tan preocupados de que funcionen sus <a href="http://kopophex.blogspot.com/2008/07/weekly-update-unhidden-biases.html">unhidden biases</a>, que no se dan cuenta que la versión 2.0 quizá resuelva menos necesidades que la versión 1.x.</p>

<h2>Songbird y Los Pioneros de lo Inevitable</h2>

<p>Veamos el modelo de Songbird.  Para comenzar, vale la pena darle una mirada al <em>About Us</em> de Songbird (click en la foto) y en general a la página.  Con ver las oficinas ya me caen bien estos señores.  Además del look profesional de la página, tienen &#8220;algo&#8221; de experiencia en desarrollo de software y productos terminados (entre otros, <a href="http://winamp.com">Winamp</a> y el <a href="http://music.yahoo.com/jukebox/">Yahoo! Music Engine</a>.</p>

<p><a href="http://songbirdnest.com/about" target="_blank"><img src="http://www.zoia.org/blog/wp-content/uploads/2008/07/pioneersoftheinevitable.png" alt="Songbird's nest" /></a></p>

<p>Songbird ni siquiera está en la versión 1.0, y ya ofrece instaladores para Linux, MacOs y Windows.  Las tres distribuciones están siempre disponibles.  Las tres funcionan.  Las tres usan las mismas extensiones.  Las nuevas versiones no destruyen tus playlists, ni malogran tus <em>rankings</em>.</p>

<p>Por supuesto, quizá no puedo conectar Songbird a un servidor PosgreSQL&#8230; pero ¿hace falta?</p>

<p>Songbird tiene dos websites distintos: uno para los <a href="http://getsongbird.com">usuarios finales</a>, otro para <a href="http://wiki.songbirdnest.com/Developer">desarrolladores</a>.  Cada página tiene su propio enfoque, porque pretende resolver distintas necesidades. Si lo que te interesa es hackear un poco (que me parece es lo que les intersa a los desarrolladores de Amarok), o colaborar con el proyecto, la página de desarrollo de Songbird tiene tutorials, documentación de los APIs, explica cómo desarrollar extensiones&#8230; tienes un website que resuelve eso sin complicar a los demás usuarios, la gran mayoría.</p>

<p>Amarok, en cambio, empieza ofreciendo una página para usuarios finales, pero termina siendo una página para desarrolladores&#8230; esperemos que no pase lo mismo con la versión 2.0 de Amarok.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.zoia.org/blog/2008/07/30/songbird-en-vez-de-amarok/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Game Developer Magazine ahora en versión electrónica</title>
		<link>http://www.zoia.org/blog/2008/04/21/game-developer-magazine-ahora-en-version-electronica/</link>
		<comments>http://www.zoia.org/blog/2008/04/21/game-developer-magazine-ahora-en-version-electronica/#comments</comments>
		<pubDate>Mon, 21 Apr 2008 14:33:49 +0000</pubDate>
		<dc:creator>Roberto</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[game development]]></category>
		<category><![CDATA[Game programming]]></category>
		<category><![CDATA[magazine]]></category>

		<guid isPermaLink="false">http://www.zoia.org/blog/?p=208</guid>
		<description><![CDATA[Desde hace unos meses Game Developer ha empezado a ofrecer subscripción electrónica. Muy conveniente. Aquí se puede ver un ejemplar antiguo de muestra: http://gamedeveloper.texterity.com/gamedeveloper/sample/]]></description>
			<content:encoded><![CDATA[<p></p><p>Desde hace unos meses <a href="http://www.gdmag.com">Game Developer</a> ha empezado a ofrecer subscripción electrónica.  Muy conveniente.</p>

<p><a href='http://www.zoia.org/blog/wp-content/uploads/2008/04/gamedevmagonline.jpg' rel='lightbox'><img src="http://www.zoia.org/blog/wp-content/uploads/2008/04/gamedevmagonline-400x308.jpg" alt="Electronic version of Game Developer Magazine" title="Game Developer Magazine" width="400" height="308" class="alignnone size-medium wp-image-209" /></a></p>

<p>Aquí se puede ver un ejemplar antiguo de muestra:  <a href="http://gamedeveloper.texterity.com/gamedeveloper/sample/">http://gamedeveloper.texterity.com/gamedeveloper/sample/</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.zoia.org/blog/2008/04/21/game-developer-magazine-ahora-en-version-electronica/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Google App Engine</title>
		<link>http://www.zoia.org/blog/2008/04/10/google-app-engine/</link>
		<comments>http://www.zoia.org/blog/2008/04/10/google-app-engine/#comments</comments>
		<pubDate>Thu, 10 Apr 2008 17:26:06 +0000</pubDate>
		<dc:creator>Roberto</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[app_engine]]></category>
		<category><![CDATA[web_framework]]></category>
		<category><![CDATA[web_programming]]></category>

		<guid isPermaLink="false">http://www.zoia.org/blog/?p=195</guid>
		<description><![CDATA[Hace unos días Google lanzó Google App Engine. Me ahorro detalles que ya se han comentado en otros sitios. Por ahora estoy &#8220;en cola&#8221; esperando una cuenta de prueba. Algunos detalles: el lenguaje de programación es Python 2.5, permite trabajar con la mayoría de Python web frameworks y para comodidad del desarrollador incluye Django 0.96.1. [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>Hace unos días Google lanzó <a href="http://code.google.com/appengine">Google App Engine</a>.  Me ahorro detalles que ya se han <a href="http://eric.themoritzfamily.com/2008/04/08/google-apps-engine-without-django-colored-glasses/">comentado</a> <a href="http://hackermojo.com/mt-static/archives/2008/04/google-and-django-for-newspapers.html">en</a> <a href="http://lazutkin.com/blog/2008/apr/8/google-app-engine-first-look/">otros</a> <a href="http://simonwillison.net/2008/Apr/8/running/">sitios</a>.</p>

<p>Por ahora estoy &#8220;en cola&#8221; esperando una cuenta de prueba. Algunos detalles: el lenguaje de programación es Python 2.5, permite trabajar con la mayoría de Python <em>web frameworks</em> y para comodidad del desarrollador incluye <a href="http://djangoproject.com">Django</a> 0.96.1.</p>

<p>El video de abajo muestra una aplicación sencilla. (No es ninguna novedad, lo saqué de la página de Google App Engine.)  El framework, en este caso, no es Django (sí usa los templates de Django), pero a los que hayan programado con Django el código les resultará algo familiar.</p>

<p><object width="425" height="355"><param name="movie" value="http://www.youtube.com/v/bfgO-LXGpTM&#038;hl=en"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/bfgO-LXGpTM&#038;hl=en" type="application/x-shockwave-flash" wmode="transparent" width="425" height="355"></embed></object></p>
]]></content:encoded>
			<wfw:commentRss>http://www.zoia.org/blog/2008/04/10/google-app-engine/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Entrevista a Alex St. John, creador de DirectX</title>
		<link>http://www.zoia.org/blog/2008/03/22/entrevista-a-alex-st-john-creador-de-directx/</link>
		<comments>http://www.zoia.org/blog/2008/03/22/entrevista-a-alex-st-john-creador-de-directx/#comments</comments>
		<pubDate>Sat, 22 Mar 2008 21:20:36 +0000</pubDate>
		<dc:creator>Roberto</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[alex st]]></category>
		<category><![CDATA[alex st john]]></category>
		<category><![CDATA[directx]]></category>
		<category><![CDATA[Game programming]]></category>
		<category><![CDATA[microsoft]]></category>

		<guid isPermaLink="false">http://www.zoia.org/blog/?p=178</guid>
		<description><![CDATA[Encontré esta entrevista a Alex St. John, que estuvo en el proyecto inicial de DirectX (la librería de gráficos para juegos de Microsoft). Es de hace un año, pero muy buena.]]></description>
			<content:encoded><![CDATA[<p></p><p>Encontré esta <a href="http://www.shacknews.com/extras/2007/032907_alexstjohn1_1.x">entrevista a Alex St. John</a>, que estuvo en el proyecto inicial de <a href="http://www.microsoft.com/directx">DirectX</a> (la librería de gráficos para juegos de Microsoft).  Es de hace un año, pero muy buena.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.zoia.org/blog/2008/03/22/entrevista-a-alex-st-john-creador-de-directx/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Free the Dragon!</title>
		<link>http://www.zoia.org/blog/2008/03/09/free-the-dragon/</link>
		<comments>http://www.zoia.org/blog/2008/03/09/free-the-dragon/#comments</comments>
		<pubDate>Mon, 10 Mar 2008 03:17:56 +0000</pubDate>
		<dc:creator>Roberto</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[activestate]]></category>
		<category><![CDATA[editor]]></category>
		<category><![CDATA[Komodo]]></category>
		<category><![CDATA[mozilla code base]]></category>
		<category><![CDATA[python cookbook]]></category>

		<guid isPermaLink="false">http://www.zoia.org/blog/2008/03/09/free-the-dragon/</guid>
		<description><![CDATA[Hace unos días ActiveState ha sacado una versión open source de Komodo Edit, bajo los mismos términos que la licencia de Firefox. Esta decisión tiene sentido, pues Komodo Edit comparte parte del código fuente base de Mozilla. Desde hace años, ActiveState desarrolla herramientas para programar en Perl, Python, PHP, Ruby y otros. También aloja varios [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>Hace unos días ActiveState ha sacado una versión open source de Komodo Edit, bajo los mismos términos que la licencia de Firefox.  Esta decisión tiene sentido, pues Komodo Edit comparte parte del código fuente base de Mozilla.</p>

<div style="float: left; padding: 5px 10px 5px 0px;"><a href='http://www.zoia.org/blog/wp-content/uploads/2008/03/komodoedit.jpg' rel='lightbox' title='Komodo Edit editando un archivo de Python'><img src='http://www.zoia.org/blog/wp-content/uploads/2008/03/komodoedit_thumb.jpg' alt='Komodo Edit editando un archivo de Python' /></a></div>

<p>Desde hace años, ActiveState  desarrolla herramientas para programar en Perl, Python, PHP, Ruby y otros.  También aloja varios &#8220;Cookbooks&#8221; (recetarios) en su website, por ejemplo, el imprescindible <a href="http://aspn.activestate.com/ASPN/Cookbook/Python/">Python Cookbook</a>.</p>

<div style="clear: both"> </div>

<div style="float: right; padding: 5px 10px 5px 0px;"><a href='http://www.zoia.org/blog/wp-content/uploads/2008/03/komodoeditextensions.jpg' rel='lightbox' title='Komodo Edit extensions'><img src='http://www.zoia.org/blog/wp-content/uploads/2008/03/komodoeditextensions_thumb.jpg' alt='Komodo Edit extensions' /></a></div>

<p>Un detalle interesante, relacionado precisamente con Mozilla, son las extensiones que usa Komodo Edit.  Basta ver el segundo screenshot para entender a qué me refiero:  son extensiones escritas en XUL, el mismo sistema que usan Firefox y Thunderbird.  Y, otra cosa que se puede ver en el screenshot, Komodo viene con una extensión para desarrollar con <a href="http://www.djangoproject.com">Django</a>.</p>

<p>Hay versiones para Linux, MacOs y Windows, y se puede <a href="http://www.activestate.com/Products/komodo_ide/komodo_edit.mhtml">descargar del website de ActiveState</a>.</p>

<div style="clear: both"> </div>
]]></content:encoded>
			<wfw:commentRss>http://www.zoia.org/blog/2008/03/09/free-the-dragon/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Django file upload</title>
		<link>http://www.zoia.org/blog/2007/07/29/django-file-upload/</link>
		<comments>http://www.zoia.org/blog/2007/07/29/django-file-upload/#comments</comments>
		<pubDate>Sun, 29 Jul 2007 22:26:14 +0000</pubDate>
		<dc:creator>Roberto</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.zoia.org/blog/2007/07/29/django-file-upload/</guid>
		<description><![CDATA[Some interesting examples on how to handle file uploads using Django: Code snippet posted on Djangosnippets. The file is saved by declaring a save() method in the form class. This method is invoked when calling form.save(), which is standard Django newforms practice. (Note that this snipped uses clean_data. As of Django version 0.96, clean_data has [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>Some interesting examples on how to handle file uploads using <a href="http://www.djangoproject.com">Django</a>:</p>

<ul>
<li>Code snippet posted on <a href="http://www.djangosnippets.org/snippets/95/">Djangosnippets</a>. The file is saved by declaring a <code>save()</code> method in the form class.  This method is invoked when calling <code>form.save()</code>, which is standard Django newforms practice. (Note that this snipped uses <code>clean_data</code>.  As of Django version 0.96, <code>clean_data</code> <a href="http://code.djangoproject.com/changeset/5237">has been renamed</a> to <code>cleaned_data</code>, so you will have to change the code or it won&#8217;t work)</li>
<li><a href="http://www.oluyede.org/blog/2007/03/18/django-image-uploading-validation-and-newforms/">Django image upload and validation</a>. The author uses a model for the file and its related data.  The uploaded file is saved by calling the <code>save_FOO_file</code> method. (This method is automatically provided by Django for fields declared as <code>models.ImageField</code> or <code>models.FileField</code> in the model. See the <a href="http://www.djangoproject.com/documentation/db-api/#save-foo-file">db-api documentation</a>.)</li>
<li><a href="http://batiste.dosimple.ch/blog/2007-05-13-1/">Django image upload, form_for_instance and monkey-patching</a>.  The example code creates a form class from <code>request.user</code> by calling <code>form_for_instance</code>.  The resulting class in then monkey-patched to insert the avatar image validation code.  (Although the code is interesting the monkey patch seems unnecessary.  I wouldn&#8217;t mind inserting the avatar validation method in a <code>UserProfileForm</code> class derived from <code>form.Forms</code>.  The code would be certainly clearer:  I think KISS takes precedence over DRY in this case.)</li>
</ul>

<p>Interesting, there seems to be no easy way of limiting the uploaded file size.  The file can be rejected at validation time, but the data would have already been transfered.</p>

<h2>A file upload recipe</h2>

<p>After reading those posts, I think that a good recipe for handling file uploads in Django would be:</p>

<ul>
<li>Write a django model for the uploaded file and its related data. Using a Django model makes sense, because it is usually necessary for the application to keep track of the uploaded files.</li>
<li>Write a subclass of <code>form.Forms</code> and declare a <code>clean_FOO</code> method for each <code>models.FileInput</code> or <code>models.ImageInput</code> fields declared in the model class.  These clean_FOO methods are used to validate the uploaded files.</li>
<li>use a django view to receive the POST data, or display the form if no data is posted or errors are found.</li>
<li>validate the uploaded file or files by triggering the standard django newforms validation mechanism:  <code>is_valid()</code>.</li>
<li>save the file or files getting the data directly from the <code>request.FILES</code> object, by writing a <code>save()</code> method for the subclassed form or by calling <code>save_FOO_file</code> for the model instance.</li>
</ul>

<h2>A simpler way to upload a file</h2>

<p>The following short <a href="http://www.djangoproject.com">Django</a> example uses no data models, does no data validation, and saves the file directly to disk using python standard file functions.  It is just a simple test I wrote to get familiar with the <code>request.FILES</code> object. This is not production code:  it could be used to execute an arbitrary script on the server.
The directory where the file is to be saved must be writable by the user that is running the Django server script. (The example uses MEDIA_ROOT as defined in <code>settings.py</code>.)</p>

<h3>file: views.py</h3>

<pre>
</pre><pre class="brush: python; title: ; notranslate">
from django import http
from django import newforms as forms
from django.shortcuts import render_to_response
from djangotest.settings import MEDIA_ROOT



class SimpleFileForm(forms.Form):
    file = forms.Field(widget=forms.FileInput, required=False)


def directupload(request):
    &quot;&quot;&quot;
    Saves the file directly from the request object. 
    Disclaimer:  This is code is just an example, and should
    not be used on a real website.  It does not validate
    file uploaded:  it could be used to execute an 
    arbitrary script on the server. 
    &quot;&quot;&quot;

    template = 'fileupload.html'

    if request['method'] == 'POST':
        if 'file' in request.FILES:
            file = request.FILES['file']

            # Other data on the request.FILES dictionary:
            #   filesize = len(file['content'])   
            #   filetype = file['content-type'] 

            filename = file['filename']

            fd = open('%s/%s' % (MEDIA_ROOT, filename), 'wb')
            fd.write(file['content'])
            fd.close()

            return http.HttpResponseRedirect(' upload_success.html')
    else:
        # display the form
        form = SimpleFileForm()
        return render_to_response(template, { 'form': form })

</pre>


<h3>file:  fileupload.html</h3>

<pre>
</pre><pre class="brush: xml; title: ; notranslate">
{% extends &quot;base.html&quot; %}

{% block body  %}
     &lt;h1&gt;Upload a file&lt;/h1&gt;
     &lt;form action=&quot;.&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt;
             {{ form }}
             &lt;input type=&quot;submit&quot; value=&quot;Upload&quot; /&gt;
     &lt;/form&gt;
{% endblock %}
</pre>

]]></content:encoded>
			<wfw:commentRss>http://www.zoia.org/blog/2007/07/29/django-file-upload/feed/</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
		<item>
		<title>Django newforms documentation updated</title>
		<link>http://www.zoia.org/blog/2007/07/12/django-newforms-documentation-updated/</link>
		<comments>http://www.zoia.org/blog/2007/07/12/django-newforms-documentation-updated/#comments</comments>
		<pubDate>Fri, 13 Jul 2007 01:39:19 +0000</pubDate>
		<dc:creator>Roberto</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.zoia.org/blog/2007/07/12/django-newforms-documentation-updated/</guid>
		<description><![CDATA[The Django newforms documentation has been updated some days ago. It includes an explanation on how to use form_for_models and form_for_instance and provides some examples. More interesting, however, is when not to use them: &#8220;If you want to create a form whose fields map to more than one model, or a form that contains fields [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>The <a href="http://www.djangoproject.com/documentation/newforms">Django newforms documentation</a> has been updated some days ago. It includes <a href="http://www.djangoproject.com/documentation/newforms/#generating-forms-for-models">an explanation</a> on how to use <code>form_for_models</code> and <code>form_for_instance</code> and provides some examples.</p>

<p>More interesting, however, is when <em>not</em> to use them:  <i>&#8220;If you want to create a form whose fields map to more than one model, or a form that contains fields that aren’t on a model, you shouldn’t use these shortcuts.&#8221;</i></p>
]]></content:encoded>
			<wfw:commentRss>http://www.zoia.org/blog/2007/07/12/django-newforms-documentation-updated/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Aprendiendo Lisp</title>
		<link>http://www.zoia.org/blog/2007/07/08/aprendiendo-lisp/</link>
		<comments>http://www.zoia.org/blog/2007/07/08/aprendiendo-lisp/#comments</comments>
		<pubDate>Sun, 08 Jul 2007 23:03:21 +0000</pubDate>
		<dc:creator>Roberto</dc:creator>
				<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://www.zoia.org/blog/2007/07/08/aprendiendo-lisp/</guid>
		<description><![CDATA[Desde hace algunas semanas estoy aprendiendo Lisp. Estoy bien cómodo programando en Python, y he aprendido bastante dando clases de Java. Pero Lisp tiene algunas cosas que no tienen esos lenguajes (y ningún otro, al parecer), y que me interesa aprender, en particular la capacidad de extender la sintaxis del lenguaje como práctica habitual de [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>Desde hace algunas semanas estoy aprendiendo Lisp.  Estoy bien cómodo programando en Python, y he aprendido bastante dando clases de Java.  Pero Lisp tiene algunas cosas que no tienen esos lenguajes (y ningún otro, al parecer), y que me interesa aprender, en particular la capacidad de extender la sintaxis del lenguaje como práctica habitual de programación.  Esto en Lisp se llama Macros, y no tienen nada que ver con los macros como se entienden en C/C++.  Como siempre, hay que sacar tiempo de donde no hay, así que estoy yendo más lento de lo que quisiera.</p>

<p>Los ensayos de <a href="http://www.paulgraham.com">Paul Graham</a> son muy buenos y sugerentes, y el libro <em>Hackers and Painters</em> tampoco tiene pierde. Obviamente no quiere decir que coincida 100% con lo que dice, pero habla bastante de Lisp, por no decir que está absolutamente entusiasmado con Lisp.</p>

<p>Estoy usando dos libros:  <em>Practical Common Lisp</em>, de Peter Siebel, que se puede <a href="http://www.apress.com/free/">descargar sin costo</a> del website de APress.  Y <em>ANSI Common Lisp</em>, de Paul Graham, que compré en Amazon.</p>

<h2>Menos bla,bla,bla, quiero empezar a escribir programas&#8230;</h2>

<p>Primera tarea, instalar alguna versión de lisp en la computadora. En Ubuntu:<br />

$ sudo apt-get install clisp clisp-doc

Ya tenemos lisp en la computadora.  Ahora basta escribir:

$ clisp

<a href='http://www.zoia.org/blog/wp-content/uploads/2007/07/hello-worldconsole.jpg' title='Common Lisp Linux' rel="lightbox"><img src='http://www.zoia.org/blog/wp-content/uploads/2007/07/hello-worldconsole.jpg' alt='Common Lisp Linux' /></a></p>

<p><em>Practical Common Lisp</em> recomienda usar Emacs como editor de texto/entorno de desarrollo y la extensión SLIME (Superior Lisp Interaction Mode for Emacs).  (Más información sobre Emacs, ver <a href="http://en.wikipedia.org/wiki/Emacs">Wikipedia</a>.) Por ahora voy a seguir los consejos del libro, así que a instalar Emacs y Slime.</p>

<p>Intenté usar la versión de Emacs que está en los repositorios de Ubuntu, y que se integra con Gnome.  Pero las letras en la pantalla se veían francamente horribles.  Creo que la pantalla del Macro Assembler que tenía en la Atari 800XL que usaba en el siglo 20 se veía mejor.  Bien, siempre hay soluciones.  Buscando en Google &#8220;pretty emacs&#8221;, encontré <a href="http://peadrop.com/blog/2007/01/06/pretty-emacs/">esta página</a>, donde se explica cómo instalar una versión de Emacs más reciente, compilada para que haga un render decente de los fonts.  En la imagen se puede ver el resultado.</p>

<p><a href='http://www.zoia.org/blog/wp-content/uploads/2007/07/lisp-hello-world.jpg' title='Emacs pretty fonts' rel='lightbox'><img src='http://www.zoia.org/blog/wp-content/uploads/2007/07/lisp-hello-world_thumb.jpg' alt='Emacs pretty fonts thumb' /></a></p>

<p>Segundo paso, aprender lo suficiente de Emacs como para crear un programa, guardarlo en un archivo y compilarlo.  Emacs tiene fama de ser poco amigable, y lo es.  En este post <a href="http://www.erik-rasmussen.com/blog/2007/07/06/lisp-enlightenment-and-emacs-frustration/">Erik Rasmussen</a> explica con gracia cómo al comienzo no se explicaba por qué si Lisp es un lenguaje superior e increíble, es tan poco usado.  Al menos eso es lo que se preguntaba él hasta que intentó usar Emacs.</p>

<p>El némesis de Emacs es vim, otro editor muy común en Linux. <a href="http://vim.org">vim</a> es poderoso. He desarrollado websites enteros usando vim.  Incluso cuando estoy usando un procesador de texto me sorprendo a veces teclando &#8220;:w&#8221;, que es el comando de vim para guardar el archivo.  Pero la verdad que entre perder tiempo tratando de que Lisp y vim se entendieran, preferí dedicar veinte minutos a seguir el tutorial de que trae el mismo Emacs.  (Se accede al tutorial presionando Ctrl-h t.  O, como dicen en Emacs, C-h t.)   Y confirmado:  veinte minutos bastan para empezar y moverse en Emacs.</p>

<p>Uno de los comentarios del post de Rasmussen dice que hay un plugin de lisp para Eclipse:  más adelante le daremos una oportunidad.</p>

<p>Tercer y último paso, instalar <a href="http://common-lisp.net/project/slime/">SLIME</a>. Slime es un entorno de desarrollo para programar en Lisp dentro de Emacs.  En Ubuntu se puede instalar escribiendo:

$ sudo apt-get install slime

Como se explica <a href="http://common-lisp.net/project/slime/doc/html/Installation.html#Installation">aquí</a>, también es necesario añadir lo siguiente al archivo <code>~/.emacs</code>:

(setq inferior-lisp-program &#8220;/usr/bin/clisp&#8221;)
(add-to-list &#8216;load-path &#8220;/usr/share/emacs-snapshot/site-list/slime&#8221;)
(require &#8216;slime)
(slime-setup)

Listo.  Ahora sí, en Emacs:  M-x slime, y aparece SLIME.</p>

<p><a href='http://www.zoia.org/blog/wp-content/uploads/2007/07/slime-snapshot.jpg' title='SLIME' rel='lightbox'><img src='http://www.zoia.org/blog/wp-content/uploads/2007/07/slime-snapshot.jpg' alt='SLIME' /></a></p>

<p>Y podemos escribir:  <code>(hello-world)</code>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.zoia.org/blog/2007/07/08/aprendiendo-lisp/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Using dynamic choices with Django newforms and custom widgets</title>
		<link>http://www.zoia.org/blog/2007/04/23/using-dynamic-choices-with-django-newforms-and-custom-widgets/</link>
		<comments>http://www.zoia.org/blog/2007/04/23/using-dynamic-choices-with-django-newforms-and-custom-widgets/#comments</comments>
		<pubDate>Mon, 23 Apr 2007 23:28:30 +0000</pubDate>
		<dc:creator>Roberto</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.zoia.org/blog/2007/04/23/using-dynamic-choices-with-django-newforms-and-custom-widgets/</guid>
		<description><![CDATA[Most of the examples I have found on the web about replacing the default Django newforms widgets use hard coded values for the list of choices the widget displays. But those hard coded examples fall short when the widgets are bounded to many-to-many fields or to foreign keys in the model class. That is, when [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>Most of the examples I have found on the web about replacing the default <a href="http://www.djangoproject.com" target="_blank">Django</a> <code>newforms</code> widgets use hard coded values for the list of choices the widget displays.  But those hard coded examples fall short when the widgets are bounded to many-to-many fields or to foreign keys in the model class.   That is, when the list of choices is generated dynamically at run time, read from a database table.</p>

<p>The solution is obvious simple:  fetch the dictionary of choices from the original widget, and use it as a parameter for your new custom widget.</p>

<p>It sounds simple, and it is.  But it took me some time to figure out how to do it.  The Django <code>newforms</code> lilbrary documentation is still work in progress, at least at the time of this writing. I could not found where the original widgets were stored in the Form instance.  Thankfully, Django is an open source project, and Python has an interactive shell mode.  Inspection of the source code and regression tests, and some playing with the Python shell is really an enlightening process.  Anyway, I am posting this on the blog for further reference.  I also include a working example of a sample Django blogging application I wrote for testing:  pretty basic, but it and ilustrates, among other things, the use of custom widgets with dynamic choices.</p>

<p>Maybe the explanaition is &#8220;over-verbose&#8221;&#8230; suggestions accepted.</p>

<h2>Don&#8217;t hit the database twice</h2>

<p>Consider the following model class:</p>

<pre>
</pre><pre class="brush: python; title: ; notranslate">
# models.py Models for django blog application
from django.db import models

class Tag(models.Model):
    tag = models.CharField(maxlength=50)

class Author(models.Model):
    name = models.CharField(maxlength=100)
    email = models.EmailField()

class Article(models.Model):
        (...)
    author = models.ForeignKey(Author)
    tags = models.ManyToManyField(Tag)
</pre>


<p>The standard method for displaying a form for the model class in a webpage is to subclass forms.Form, create an instance of the subclass and return the rendered the template.  Examples of this can be found on the Django website and on the web.</p>

<p>Django <code>newforms</code> provides two methods that simplify the subclassing of <code>forms.Form</code>:  <code>form_for_instance</code> and <code>form_for_model</code>.  Both methods return a new class:  a subclass of Form, more precisely, tuned to your model class or model instance.  If you have a model class or model instance with a many-to-many relationship, and you don&#8217;t mind displaying the relationship as a listbox on the webpage, then these two methods will take care of querying the database, building the list of options and feeding it to SelectField or a SelectMultipleField widget.</p>

<pre>
</pre><pre class="brush: python; title: ; notranslate">
          #  ArticleForm is a subclass of form.Form
          ArticleForm = forms.models.form_for_model(Article)
</pre>


<p>The next step is to create an instance of <code>ArticleForm</code>, and return the rendered template:</p>

<pre>
</pre><pre class="brush: python; title: ; notranslate">
        form = ArticleForm()
        return render_to_response('post.html', { 'form': form })
</pre>


<p>I don&#8217;t like lists for multiple option selection, at least not in webpages.  I prefer checkboxes.  It takes some CSS tweaking to render them correctly and evenly spaced on the webpage, but it greatly enhances the user experience.</p>

<p>The class generated by form_for_model (and by form_for_instance) stores the widgets in a variable named <code>base_fields</code> (a dictionary).  This makes it simple to replace any of the default widgets with any other widget, provided it makes sense to do so.  For example, the following code replaces the <code>author</code> and <code>tags</code> widgets with a RadioSelect widget and a CheckboxSelectMultiple widget:</p>

<pre>
</pre><pre class="brush: python; title: ; notranslate">
        ArticleForm.base_fields['tags'].widget = CheckboxSelectMultiple(choices= ... )
        ArticleForm.base_fields['author'].widget = RadioSelect(choices= ... )
</pre>


<p>The only thing left is to provide the list of choices that both <code>CheckboxSelectMultiple</code> and <code>RadioSelect</code> constructors expect as one of their parameters.  One way to build such lists is to query the database and fetch the entries for the Tag and Author classes. Something like <code>tagChoices = Tag.objects.all()</code> and <code>authorChoices = Author.objects.all()</code>.  Convert the resulting queryset to a dictionary and use it as a parameter to the RadioSelect widget constructor.</p>

<p>But this is querying the database twice and for the same data each time the form is displayed on the webpage:  once for the original SelectMultiple widget, and then again for the custom widget.  And it would be a shame to have your name on such code&#8230;  Obviously, this can be avoided by retrieving the list of choices from the original SelectMultiple widget and using it as the list of choices for our custom widget:</p>

<pre>
</pre><pre class="brush: python; title: ; notranslate">
        ArticleForm = forms.models.form_for_model(Article)

        ArticleForm.base_fields['tags'].widget = CheckboxSelectMultiple(
                choices=ArticleForm.base_fields['tags'].choices) 

        ArticleForm.base_fields['author'].widget = RadioSelect(
                choices=ArticleForm.base_fields['author'].choices)

        form = ArticleForm()
        return render_to_response('post.html', { 'form': form })
</pre>


<p>That&#8217;s it.  The Author SelectField gets replaced by radio buttons, and the Tag MultipleSelectField by some nice checkboxes.  The database gets hit only once.  Nothing to be ashamed of.</p>

<h2>A complete example</h2>

<p><strong><u>Updated 2007-5-5</u></strong>: added editpost.html, which was not included by mistake.</p>

<p>The following is a test blog application I wrote.  It uses <code>form_for_instance</code> and <code>form_for_model</code> to define the proper subclass of form, then replaces the standard widgets with custom ones.  The widgets load the Author and Tags choices dynamically.  In addition, creation and edition of the blog posts are handled by the same method.  A regular expression in <code>urls.py</code> converts web page names to slugs as registered in the database, so permalinks are effectively implemented.</p>

<p>This blogging app is really basic:  the Django admin interface is needed to add new tags and authors; no authentication; post slugs have to be hand created; no plugins; etc. But deactivate the edit line in <code>urls.py</code>, use css to pump up the desigh,  edit your posts using the admin interface, and you have a Django-powered miniblog you can start customizing.  Maybe pass the <code>form</code> with the article content to another method, before rendering the template, and write some plugins to pre-process the content prior to show it on the webpage.</p>

<h2>Some screenshots of the Django mini-blog</h2>

<p>This is pretty spartan. Throw in some CSS if you like.</p>

<p><a href='http://www.zoia.org/blog/wp-content/uploads/2007/04/blogindex.jpg' title='Django Blog, main page' rel='lightbox'><img src='http://www.zoia.org/blog/wp-content/uploads/2007/04/blogindex_thumb.jpg' alt='Django Blog, main page' /></a></p>

<p><a href='http://www.zoia.org/blog/wp-content/uploads/2007/04/formcondata.jpg' title='Django blog, edit post' rel='lightbox'><img src='http://www.zoia.org/blog/wp-content/uploads/2007/04/formcondata_thumb.jpg' alt='Django blog, edit post' /></a></p>

<p><span id="more-129"></span></p>

<h2>template file:  ./base.html</h2>

<pre>
</pre><pre class="brush: xml; title: ; notranslate">
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&gt;
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;

&lt;head&gt;
&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=utf-8&quot; /&gt;

&lt;title&gt;{% block pagetitle %}Page title{% endblock %}&lt;/title&gt;


&lt;link rel=&quot;stylesheet&quot; href=&quot;&quot; type=&quot;text/css&quot; media=&quot;screen&quot; /&gt;
{% block head %}

{% endblock %}

&lt;/head&gt;
&lt;body&gt;
{% block body %}

{% endblock %}
&lt;/body&gt;
&lt;/html&gt;
</pre>


<h2>template file: ./blog/index.html</h2>

<pre>
</pre><pre class="brush: xml; title: ; notranslate">
{% extends &quot;base.html&quot; %}

{% block title %}
    A minimal blog, powered by Django
{% endblock %}

{% block body %}
&lt;h2&gt;Hello, world!&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;./add&quot;&gt;Write new post&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

{% for post in posts %}
&lt;div class=&quot;post&quot;&gt;
    &lt;h2&gt;&lt;a href=&quot;./{{ post.slug }}.html&quot; title=&quot;{{ post.title }}&quot;&gt;
        {{ post.title }}&lt;/a&gt;&lt;/h2&gt;
    &lt;div class=&quot;postmeta&quot;&gt;
        &lt;div class=&quot;postdate&quot;&gt;{{ post.pub_date }}&lt;/div&gt;
        &lt;div class=&quot;posttags&quot;&gt;
        Tags: 
        {% for tag in post.tags.all %}
            {{ tag.tag }},
        {% endfor %}
        &lt;/div&gt;
        &lt;div class=&quot;postauthor&quot;&gt;
            By {{ post.author }}.
        &lt;/div&gt;
    &lt;/div&gt;&lt;!-- end postmeta --&gt;
    &lt;a href=&quot;./edit/{{ post.id }}&quot;&gt;Edit post&lt;/a&gt;
    &lt;div class=&quot;postcontent&quot;&gt;
    {{ post.content }}
    &lt;/div&gt;
&lt;/div&gt;

{% endfor %}
{% endblock %}
</pre>


<h2>template file: ./blog/editpost.html</h2>

<pre>
</pre><pre class="brush: xml; title: ; notranslate">
{% extends &quot;base.html&quot; %}

{% block title %}
        Add post
{% endblock %}

{% block body %}
&lt;h2&gt;Write a post&lt;/h2&gt;
&lt;form method=&quot;POST&quot; action=&quot;.&quot;&gt;
        {{ form.as_p }}

        &lt;input type=&quot;submit&quot; value=&quot;submit&quot; /&gt;
&lt;/form&gt;

{% endblock %}
</pre>


<h2>models.py</h2>

<pre>
</pre><pre class="brush: python; title: ; notranslate">
# Models for django blog application

from django.db import models

class Tag(models.Model):
    tag = models.CharField(maxlength=50)  # tagname

    def __str__(self):
        return self.tag

    class Admin:
        pass

class Author(models.Model):
    name = models.CharField(maxlength=100)
    email = models.EmailField()

    def __str__(self):
        return self.name

    class Admin:
        pass

class Article(models.Model):
    title = models.CharField(maxlength=250)
    slug = models.CharField(maxlength=250)
    content = models.TextField()
    author = models.ForeignKey(Author)
    pub_date = models.DateTimeField('date published')
    mod_date = models.DateTimeField('date modified')
    tags = models.ManyToManyField(Tag)

    def __str__(self):
        return self.title

    class Admin:
        pass
</pre>


<h2>url.py</h2>

<pre>
</pre><pre class="brush: python; title: ; notranslate">
# -*- coding: utf-8 -*-
from django.conf.urls.defaults import *

urlpatterns = patterns('',
    # Example:
    # (r'^djangotest/', include('djangotest.foo.urls')),

    (r'^blog/$', 'djangotest.blog.views.index'),

    #  regex for slugs.  Note that the regex matches even 
    #  .html without slug:  this gets handled in views.single
    (r'^blog/(?P&amp;lt;slug&amp;gt;((\w+|-)*))(\.html)\/?$', 'djangotest.blog.views.single'),

    # edit post
    (r'^blog/edit/(?P&amp;lt;post_id&amp;gt;\d+)/$', 'djangotest.blog.views.edit'),   

    # add new post
    (r'^blog/add/$', 'djangotest.blog.views.edit'),

    # Uncomment this for admin:
    (r'^admin/', include('django.contrib.admin.urls')),
)
</pre>


<h2>views.py</h2>

<pre>
</pre><pre class="brush: python; title: ; notranslate">
# -*- coding: utf-8 -*-
# views.py

from django.http import HttpResponse, HttpResponseRedirect
from django.template import Context, loader
from django.shortcuts import render_to_response, get_list_or_404

from django import newforms as forms
from django.newforms.widgets import *

from djangotest.blog.models import *

def index(request):
    &quot;&quot;&quot; Display last 5 articles &quot;&quot;&quot;
    # TODO:  make '5' not a magic number
    last_five = Article.objects.all().order_by('-pub_date')[:5]

    t = loader.get_template('blog/index.html')
    c = Context({
        'posts' : last_five,
    })

    return HttpResponse(t.render(c))


def single(request, slug):
    # handle page not found
    articles = get_list_or_404(Article, slug__exact=slug)
    return render_to_response('blog/index.html', { 'posts':articles })

def edit(request, post_id=None):

    if post_id is None:
        # no id, user is creating new post
        ArticleForm = forms.models.form_for_model(Article)
    else:
        # editing or updating post
        try:
            article = Article.objects.get(id=post_id)
        except Article.DoesNotExist:
            HttpResponseRedirect('blog/postdoesnotexist.html')

        ArticleForm = forms.models.form_for_instance(article)

    # we need to feed the new widget with the choices from the 
        # default one:  SelectMultiple

    ArticleForm.base_fields['tags'].widget = CheckboxSelectMultiple(
        choices=ArticleForm.base_fields['tags'].choices)
    ArticleForm.base_fields['author'].widget = RadioSelect(
        choices=ArticleForm.base_fields['author'].choices)

    if request.method == 'POST':
        # form has been submited (i.e., new post or old post update)
        form = ArticleForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect('/blog')

    else:
        # empty form
        form = ArticleForm()

    return render_to_response('blog/editpost.html', { 'form': form })
</pre>


<h2>References</h2>

<ul>The following pages were of great help in understanding the newforms library better:
<li>The Django <a href="http://www.djangoproject.com/documentation/newforms/" target="_blank">documentation for newforms</a>. (Still work in progress.)</li>
<li>Some examples of using a select field with static choices and hidden fields: <a href="http://weblog.bignerdranch.com/?p=31" target="_blank">Big nerd ranch</a>. (qué tal nombre&#8230;)</li>
<li>How to render <a href="http://eggdrop.ch/blog/2007/02/15/django-dynamicforms/" target="_blank">dynamic forms using Django newforms</a>.  From the author&#8217;s blog:  <em>the newforms library works only for static forms. Forms that have a fixed number of fields. What I wanted to do, is create a page that allows you to edit multiple instances of a model at once.</em>.  Uses Javascript, no AJAX.</li>
<li>How to use the <a href="http://unpythonic.blogspot.com/2007/01/django-newforms-for-models.html" target="_blank">model specified defaults as the default values for fields</a>, and how to make the help_text entries available in the fields for rendering.</li> 
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.zoia.org/blog/2007/04/23/using-dynamic-choices-with-django-newforms-and-custom-widgets/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
	</channel>
</rss>

