<?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>Refactor the Life &#187; Web</title>
	<atom:link href="http://kunxi.org/archives/category/web/feed/" rel="self" type="application/rss+xml" />
	<link>http://kunxi.org</link>
	<description>Yet another code monkey blog.</description>
	<lastBuildDate>Wed, 05 Oct 2011 13:00:17 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Authorize the REST web service</title>
		<link>http://kunxi.org/archives/2009/04/authorize-the-rest-web-service/</link>
		<comments>http://kunxi.org/archives/2009/04/authorize-the-rest-web-service/#comments</comments>
		<pubDate>Sun, 19 Apr 2009 22:27:23 +0000</pubDate>
		<dc:creator>bookstack</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[pattee django REST]]></category>

		<guid isPermaLink="false">http://kunxi.org/?p=366</guid>
		<description><![CDATA[Once we step into the REST territory, session-based AuthenticationMiddleware is no longer an option due to the violation of stateless principle. Digest authentication seems one of the very few options left for this situation. The basic concept is that the client and server share a private secret key, the client signs the HTTP request and [...]]]></description>
			<content:encoded><![CDATA[<p>Once we step into the REST territory, session-based AuthenticationMiddleware is no longer an option due to the violation of stateless principle. Digest authentication seems one of the very few options left for this situation. The basic concept is that the client and server share a private secret key, the client signs the HTTP request and the server validates the signature before further operations. </p>
<p>There is no off-the-shelf digest authentication middleware available yet, let&#8217;s roll up sleeves and home-brew our own or more specifically, shameless copy the S3(<a href="http://docs.amazonwebservices.com/AmazonS3/latest/index.html?RESTAuthentication.html">authentication spec</a>) python library with slightly simplification: </p>
<p>The entities under the radar are cut to five:  <em>HTTP verb</em>, <em>Content-Length</em>, <em>Content-Type</em>, <em>Date</em> and the body. The HTTP verb and content type specify the REST request, the date prevents the man-in-the-middle replay attack. The signature is then digested on the stream, and appended into <em>Authentication</em> header.<br />
<strong>Update</strong>: URL is also essential, the man-in-the-middle may record the REST operation in one entry, and replay in another entry point.</p>
<p>It is also pretty straightforward to wrap up the digest authentication as a middleware: create a new model named as <em>Token</em> and add <em>access_id</em> and <em>acess_key </em>pair, also the <em>User</em> as foreign key. We could just copy <em>AuthenticateMiddleware</em> and override <em>get_user</em> method to integrate the digest validation. You may check the <a href="http://my-trac.assembla.com/pattee/changeset?new=%2F%4022&#038;old=%2F%4021">revision 21 to 22 on pattee</a> for more details if you are interested in.</p>
<p>There is another issue worthy our attention: both digest and session-based authentications are required, the latter is the gate keeper to access admin interface to manages the token used for the former, but they may not play well together: the resource for the cookie management in the server side is totally wasted to handle REST requests, and furthermore, it leaves the door open for security exploit by stolen cookies. We will discuss this issue later, stay tune.</p>
<div id="crp_related"><h2>Related Posts:</h2><ul><li><a href="http://kunxi.org/archives/2009/01/restful-django-practice/" rel="bookmark">RESTful Django practice</a></li><li><a href="http://kunxi.org/archives/2007/09/learning-django-by-example4-first-user-authenticated/" rel="bookmark">Learning Django by Example(4): First user authenticated</a></li><li><a href="http://kunxi.org/archives/2008/09/reverse-mashup/" rel="bookmark">Reverse mashup</a></li><li><a href="http://kunxi.org/archives/2008/01/pythonsoap-second-encounter/" rel="bookmark">Python/SOAP: second encounter</a></li><li><a href="http://kunxi.org/archives/2009/01/customize-the-django-newform-admin-ui/" rel="bookmark">Customize the Django newform admin UI</a></li></ul></div>]]></content:encoded>
			<wfw:commentRss>http://kunxi.org/archives/2009/04/authorize-the-rest-web-service/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Book Review: Django 1.0 Template Development</title>
		<link>http://kunxi.org/archives/2009/03/book-review-django-10-template-development/</link>
		<comments>http://kunxi.org/archives/2009/03/book-review-django-10-template-development/#comments</comments>
		<pubDate>Fri, 13 Mar 2009 06:42:02 +0000</pubDate>
		<dc:creator>bookstack</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[review]]></category>

		<guid isPermaLink="false">http://kunxi.org/?p=357</guid>
		<description><![CDATA[This is not a paid review, I did not receive a dime from author, publisher or the affiliated; however, I did get a free copy of the book, so some harsh critics may be sugar-coated. Read with caution. Though I am a die-hard RTFM guy, it never hurt to take the advantage of expertise of [...]]]></description>
			<content:encoded><![CDATA[<div class="warning">This is <strong>not</strong> a paid review, I did not receive a dime from author, publisher or the affiliated; however, I did get a free copy of the book, so some harsh critics may be sugar-coated. Read with caution.</div>
<p>Though I am a die-hard RTFM guy, it never hurt to take the advantage of expertise of the peers. <a href="http://www.packtpub.com/django-1.0-template-design-practical-guide/book">Django 1.0 Template Development</a> focuses on a relatively narrow topic, the template system of Django. The author puts himself in an dilemma: he expects the target audience to have basic ideas of Django system, but still has to go over all the hassles(not really though) to kick start a new project to make the book self-contained. I think the author did a great job for a beginners, but I still highly recommend the official <a href="http://www.djangobook.com/">DjangoBook</a>, <a href="http://docs.djangoproject.com/en/dev/intro/tutorial01/">tutorial</a> and <a href="http://docs.djangoproject.com/en/dev/">documentation</a>.</p>
<p>After two chapters warm-up, Chapter 3 shows the magic of <em>Context</em> and <em>RequestContext</em>. It is interesting to see how the project evolve from the low-level operation to the shortcuts. Chapter 4 introduces the built-in tags in the toolbox. Chapter 5 and 6 demonstrates the template inheritance and how multiple templates are served. In chapter 7, the developers can extend their toolbox by creating new filter tags. Chapter 9 gives series examples for admin UI customization. Chapter 8 and 10 are about the performance, pagination and cache. Last but not the least, L10N in chapter 11.</p>
<p>The examples in each chapter is atomic for easy understanding, but in my humble opinion, most of them do not impress the readers the power of Django. The framework shines to solve BIG and complex questions. I just wonder what if the author starts a much more ambitious project with complicated specification, and later decompose it to small tasks and address them in each chapter to make the point, just like <a href="www.diveintopython.org/">Dive into Python</a> does.</p>
<p>Furthermore, I would appreciate if the author could share more first-hand experience with readers. Engineering is always about question-solving. The framework is naturally easy to learn, otherwise, why bother? But it may suck in the big time if it does not scale. Any real world case would help to establish the confidence for further acceptance.</p>
<p>The bottom line: a good book for beginners, some chapters are quite beefy for the topic. As Django is a fast-evolving project, I hope the author will bring more juicy examples in the future edition.</p>
<div id="crp_related"><h2>Related Posts:</h2><ul><li><a href="http://kunxi.org/archives/2009/01/restart-the-django-engine/" rel="bookmark">Restart the Django engine</a></li><li><a href="http://kunxi.org/archives/2007/09/learning-django-by-example1-start-the-engine/" rel="bookmark">Learning Django by Example(1): Start the Engine</a></li><li><a href="http://kunxi.org/archives/2008/01/learning-django-by-example7-attach-a-tag/" rel="bookmark">Learning Django by Example(7) Attach a tag</a></li><li><a href="http://kunxi.org/archives/2007/02/c-study-note-3-typename-vs-class/" rel="bookmark">C++ Study Note (3) - typename, class and template</a></li><li><a href="http://kunxi.org/archives/2007/09/learning-django-by-example3-just-works/" rel="bookmark">Learning Django by Example(3): Just works</a></li></ul></div>]]></content:encoded>
			<wfw:commentRss>http://kunxi.org/archives/2009/03/book-review-django-10-template-development/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>How to PUT a file in Django</title>
		<link>http://kunxi.org/archives/2009/01/how-to-put-a-file-in-django/</link>
		<comments>http://kunxi.org/archives/2009/01/how-to-put-a-file-in-django/#comments</comments>
		<pubDate>Thu, 29 Jan 2009 07:38:15 +0000</pubDate>
		<dc:creator>bookstack</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[pattee]]></category>
		<category><![CDATA[wsgi]]></category>

		<guid isPermaLink="false">http://kunxi.org/?p=355</guid>
		<description><![CDATA[Once we decide to go for PUT instead of POST, we step out the comfort zone of django, there is no mapped form filed, no validation, we have to deal with the raw WSGI interface by ourselves. Anyway, we can still use the the django.core.file.File. If we dig into the source code, the django.core.file.File defines: [...]]]></description>
			<content:encoded><![CDATA[<p>Once we decide to go for PUT instead of POST, we step out the comfort zone of django, there is no mapped form filed, no validation, we have to deal with the raw WSGI interface by ourselves. Anyway, we can still use the the <em>django.core.file.File</em>.</p>
<p>If we dig into the source code, the <em>django.core.file.File</em> defines: <em>open</em>, <em>close</em>, <em>read</em>, <em>tell</em>, <em>seek</em>, <em>flush</em> and some other django-specific operations, like <em>chunks</em>, <em>readlines</em>, <em>xreadlines</em> etc. <a href="http://code.djangoproject.com/ticket/8501">Ticket #8501</a> glues <em>File</em> and file object when <em>chunks</em> method is missing.</p>
<p>It is interesting that the interface <em>File</em> exposed explicitly requires that the underlying file object supports random access, which is most likely overqualified for general use. Sometimes, <strong>less is more</strong>. And it implicitly expects <em>read</em> will return EOF, which is also not true for <em>WSGI.input</em>. So we end up to brew our own:</p>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">class</span> SocketFile<span class="br0">&#40;</span>File<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; <span class="co1"># Only forward access is allowed</span><br />
&nbsp; &nbsp; <span class="kw1">def</span> <span class="kw4">__init__</span><span class="br0">&#40;</span><span class="kw2">self</span>, <span class="kw3">socket</span>, size<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">super</span><span class="br0">&#40;</span>SocketFile, <span class="kw2">self</span><span class="br0">&#41;</span>.<span class="kw4">__init__</span><span class="br0">&#40;</span><span class="kw3">socket</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">self</span>._size = <span class="kw2">int</span><span class="br0">&#40;</span>size<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">self</span>._pos = 0</p>
<p>&nbsp; &nbsp; <span class="kw1">def</span> read<span class="br0">&#40;</span><span class="kw2">self</span>, num_bytes=<span class="kw2">None</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> num_bytes <span class="kw1">is</span> <span class="kw2">None</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; num_bytes = <span class="kw2">self</span>._size &#8211; <span class="kw2">self</span>._pos<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; num_bytes = <span class="kw2">min</span><span class="br0">&#40;</span>num_bytes, <span class="kw2">self</span>._size &#8211; <span class="kw2">self</span>._pos<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">self</span>._pos += num_bytes<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">self</span>.<span class="kw2">file</span>.<span class="me1">read</span><span class="br0">&#40;</span>num_bytes<span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">def</span> tell<span class="br0">&#40;</span><span class="kw2">self</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">self</span>._pos</p>
<p>&nbsp; &nbsp; <span class="kw1">def</span> seek<span class="br0">&#40;</span><span class="kw2">self</span>, position<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">pass</span></div>
</div>
<p>The <em>SocketFile</em> object is initialized with the length of the socket file object, aka CONTENT_LENGTH, the <em>read</em> method gatekeeps the operation to return EOF. <em>seek</em> is inherited from <em>File</em>, so just bypass it. Just wrap the raw WSGI.input with <em>SocketFile</em>, and use it as <em>File</em>. Please check <a href="http://my-trac.assembla.com/pattee/browser/trunk/bookshelf/views.py?rev=21">views.py</a> for the usage.</p>
<div id="crp_related"><h2>Related Posts:</h2><ul><li><a href="http://kunxi.org/archives/2007/10/learning-django-by-example6-ajax-file-upload/" rel="bookmark">Learning Django by Example(6): AJAX File Upload</a></li><li><a href="http://kunxi.org/archives/2009/01/customize-the-django-newform-admin-ui/" rel="bookmark">Customize the Django newform admin UI</a></li><li><a href="http://kunxi.org/archives/2008/02/bite-by-memoryerror/" rel="bookmark">Bite by MemoryError</a></li><li><a href="http://kunxi.org/archives/2006/07/return-of-lex/" rel="bookmark">Return of the Lex</a></li><li><a href="http://kunxi.org/archives/2008/01/learning-django-by-example7-attach-a-tag/" rel="bookmark">Learning Django by Example(7) Attach a tag</a></li></ul></div>]]></content:encoded>
			<wfw:commentRss>http://kunxi.org/archives/2009/01/how-to-put-a-file-in-django/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>RESTful Django practice</title>
		<link>http://kunxi.org/archives/2009/01/restful-django-practice/</link>
		<comments>http://kunxi.org/archives/2009/01/restful-django-practice/#comments</comments>
		<pubDate>Thu, 15 Jan 2009 07:29:21 +0000</pubDate>
		<dc:creator>bookstack</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[pattee]]></category>
		<category><![CDATA[rest]]></category>

		<guid isPermaLink="false">http://kunxi.org/?p=343</guid>
		<description><![CDATA[After several rounds reading RESTfull Web Services, I still have feeble confidence on my understanding the hyped REST idea, so please never hesitate to criticize, suggest in the comment. Expose the resources A book can be easily be identified by ISBN or EAN, however, it may stand for the specific book other than other books, [...]]]></description>
			<content:encoded><![CDATA[<p>After several rounds reading <a href="http://oreilly.com/catalog/9780596529260/">RESTfull Web Services</a>, I still have feeble confidence on my understanding the hyped REST idea, so please never hesitate to criticize, suggest in the comment.</p>
<h1>Expose the resources</h1>
<p>A book can be easily be identified by ISBN or EAN, however, it may stand for the specific book other than other books, or it refers an eBook instance to for content presentation. We use an additional argument, <em>format</em> to differentiate them:</p>
<div class="codesnip-container" >/bookshelf/books/(isbn|ean)?format=(pdf|chm|&#8230;|json)</div>
<p><strong>UPDATE</strong> A better approach is to use the pseudo file:</p>
<div class="codesnip-container" >/bookshelf/books/(isbn|ean)(.(pdf|chm|&#8230;|json))</div>
<p>The default format is JSON, the server will render the meta data and available eBook formats in JSON. </p>
<table class="enum">
<tbody>
<tr>
<th>Client request</th>
<th>Server response</th>
</tr>
<tr>
<td>GET ../isbn(.pdf)</td>
<td>
<p>Book meta data and available formats, or the eBook data.</p>
<p><em>404/Not Found</em> if the book does not exists</p>
</td>
</tr>
<tr>
<td>PUT ../isbn</td>
<td>
<p><em>201/Created</em> if the server create a new Book instance.</p>
<p><em>200/OK</em> if the book exists.</p>
<p><em>400/Bad Request</em> if the ISBN is invalid.</p>
</td>
</tr>
<tr>
<td>PUT ../isbn.pdf</td>
<td>
<p><em>201/Created</em> if the specified format eBook does not exist.</p>
<p><em>200/OK</em> if the eBook exists. The admin needs to moderate later.</p>
<p><em>400/Bad Request</em> if the ISBN is invalid.</p>
</td>
</tr>
<tr>
<td><del datetime="2009-01-16T00:05:57+00:00">HEAD ../isbn</del></td>
<td>
<p><del datetime="2009-01-16T00:05:57+00:00">Available formats.</del></p>
<p><del datetime="2009-01-16T00:05:57+00:00"><em>404/Not Found</em> if the book does not exists.</del></p>
</td>
</tr>
<tr>
<td>HEAD ../isbn.pdf</td>
<td>
<p>The content length and other information about the file.</p>
<p><em>404/Not Found</em> if the book does not exists.</p>
</td>
</tr>
<tr>
<td>DELETE ../isbn</td>
<td>
<p>200/Accepted Remove the Book instance if no related eBook exists.</p>
<p><em>404/Not Found</em> if the book does not exists.</p>
<p><em>409/Conflict</em> if there exist at least one eBook related.</p>
</td>
</tr>
<tr>
<td>DELETE ../isbn.pdf</td>
<td>
<p>200/Accepted Remove the eBook instance, the admin needs to moderate.</p>
<p><em>404/Not Found</em> if the book does not exists.</p>
</td>
</tr>
</tbody>
</table>
<p>Furthermore, the URL representation is supposed to be discoverable. So we add two boring URL:</p>
<table class="enum">
<tbody>
<tr>
<th>Client request</th>
<th>Server response</th>
</tr>
<tr>
<td>GET /bookshelf/</td>
<td>
<p>Available list, currently only books supported.</p>
</td>
</tr>
<tr>
<td>GET /bookshelf/books</td>
<td>
<p>All books with pagination, <em>?page=n</em></p>
</td>
</tr>
</tbody>
</table>
<h1>Serialization</h1>
<p>There exists a generic RESTful Django project,<a href="http://code.google.com/p/django-rest-interface/"> django-rest-interface</a>, no surprise it takes the built-in JSON serializer.</p>
<p>The default JSON serializer is convenient, but from my understanding, it is more or less gears towards the round trip of data serialization, while we favor presentation only:</p>
<ul>
<li>Too much database details exposed to the end users.</li>
<li>ForeignKey and ManyToMany are interpreted as external link using the id field. </li>
</ul>
<p>Furthermore, the JSON serializer is not lazy enough: the data has to be fetched from the database and stored in the memory before it is dumped to the stream. This may result in a serious scaling issue. The side-effect of the writing policy make it impossible to serialize in a recursive fashion, just because the stream has not been flushed until <strong>all</strong> the objects have been addressed. Otherwise, the ForeighKey and ManyToMany can be easily addressed. A better solution is to take the similar approach as SAX does. The tags are emitted recursively once a new object needs to be serialized. </p>
<p>We will discuss the Pattee&#8217;s implementation next time.</p>
<div id="crp_related"><h2>Related Posts:</h2><ul><li><a href="http://kunxi.org/archives/2009/01/customize-the-django-newform-admin-ui/" rel="bookmark">Customize the Django newform admin UI</a></li><li><a href="http://kunxi.org/archives/2007/09/learning-django-by-example3-just-works/" rel="bookmark">Learning Django by Example(3): Just works</a></li><li><a href="http://kunxi.org/archives/2007/09/learning-django-by-example2-show-me-your-data/" rel="bookmark">Learning Django by Example(2): Show me your data</a></li><li><a href="http://kunxi.org/archives/2009/01/tip-reuse-django-view-in-urlconf/" rel="bookmark">Tip: Reuse Django view in urlconf</a></li><li><a href="http://kunxi.org/archives/2007/10/learning-django-by-example6-ajax-file-upload/" rel="bookmark">Learning Django by Example(6): AJAX File Upload</a></li></ul></div>]]></content:encoded>
			<wfw:commentRss>http://kunxi.org/archives/2009/01/restful-django-practice/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Tip: Reuse Django view in urlconf</title>
		<link>http://kunxi.org/archives/2009/01/tip-reuse-django-view-in-urlconf/</link>
		<comments>http://kunxi.org/archives/2009/01/tip-reuse-django-view-in-urlconf/#comments</comments>
		<pubDate>Mon, 12 Jan 2009 04:14:12 +0000</pubDate>
		<dc:creator>bookstack</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[pattee]]></category>

		<guid isPermaLink="false">http://kunxi.org/?p=340</guid>
		<description><![CDATA[The Pro Django introduces a convenient way to reuse the way: fill an optional dictionary object to feed extra information. We may extend this trick a little bit further. Book model has two unique fields, isbn and ean. They are essential the same except the queried field. We can reuse the view by categorizing the [...]]]></description>
			<content:encoded><![CDATA[<p>The <a href="http://www.amazon.com/gp/product/1430210478?ie=UTF8&#038;tag=prodjango-20&#038;linkCode=as2&#038;camp=1789&#038;creative=9325&#038;creativeASIN=1430210478">Pro Django</a> introduces a convenient way to reuse the way: fill an optional dictionary object to feed extra information.</p>
<p>We may extend this trick a little bit further. <em>Book</em> model has two unique fields, <em>isbn</em> and <em>ean</em>. They are essential the same except the queried field. We can reuse the view by categorizing the request by the length:</p>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;">urlpatterns = patterns<span class="br0">&#40;</span><span class="st0">&#8221;</span>,<br />
&nbsp; &nbsp; <span class="br0">&#40;</span>r<span class="st0">&#8216;^books/(?P&lt;isbn&gt;[<span class="es0">\d</span><span class="es0">\w</span>]{10})$&#8217;</span>, views.<span class="me1">detail</span><span class="br0">&#41;</span> , <span class="co1"># ISBN</span><br />
&nbsp; &nbsp; <span class="br0">&#40;</span>r<span class="st0">&#8216;^books/(?P&lt;ean&gt;[<span class="es0">\d</span><span class="es0">\w</span>]{13})$&#8217;</span>, views.<span class="me1">detail</span><span class="br0">&#41;</span>, <span class="co1"># EAN</span><br />
<span class="br0">&#41;</span></div>
</div>
<p>Then in <em>detail</em>, using the magic kwargs to bring the information in:</p>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">def</span> detail<span class="br0">&#40;</span>request, <span class="sy0">**</span>kwargs<span class="br0">&#41;</span>:<br />
&nbsp; book_qs = Book.<span class="me1">objects</span>.<span class="kw2">filter</span><span class="br0">&#40;</span><span class="sy0">**</span>kwargs<span class="br0">&#41;</span></div>
</div>
<div id="crp_related"><h2>Related Posts:</h2><ul><li><a href="http://kunxi.org/archives/2008/01/learning-django-by-example7-attach-a-tag/" rel="bookmark">Learning Django by Example(7) Attach a tag</a></li><li><a href="http://kunxi.org/archives/2007/09/learning-django-by-example5-time-to-attack/" rel="bookmark">Learning Django by Example(5): Time to Attack</a></li><li><a href="http://kunxi.org/archives/2009/01/customize-the-django-newform-admin-ui/" rel="bookmark">Customize the Django newform admin UI</a></li><li><a href="http://kunxi.org/archives/2009/01/restful-django-practice/" rel="bookmark">RESTful Django practice</a></li><li><a href="http://kunxi.org/archives/2007/09/learning-django-by-example1-start-the-engine/" rel="bookmark">Learning Django by Example(1): Start the Engine</a></li></ul></div>]]></content:encoded>
			<wfw:commentRss>http://kunxi.org/archives/2009/01/tip-reuse-django-view-in-urlconf/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Customize the Django newform admin UI</title>
		<link>http://kunxi.org/archives/2009/01/customize-the-django-newform-admin-ui/</link>
		<comments>http://kunxi.org/archives/2009/01/customize-the-django-newform-admin-ui/#comments</comments>
		<pubDate>Mon, 12 Jan 2009 02:50:19 +0000</pubDate>
		<dc:creator>bookstack</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[gelman]]></category>
		<category><![CDATA[newform]]></category>
		<category><![CDATA[pattee]]></category>

		<guid isPermaLink="false">http://kunxi.org/?p=326</guid>
		<description><![CDATA[One of the exciting features Django 1.0 brings to the table is the integration of newform into the admin UI. Hat off to Brian, great work. The essential workload in the Pattee&#8217;s admin UI is to input the meta information of the book, then link it to the eBook file. It is more convenient to [...]]]></description>
			<content:encoded><![CDATA[<p>One of the exciting features Django 1.0 brings to the table is the integration of newform into the admin UI. Hat off to <a href="http://oebfare.com/blog/2008/jul/20/newforms-admin-migration-and-screencast/">Brian</a>, great work. </p>
<p>The essential workload in the Pattee&#8217;s admin UI is to input the meta information of the book, then link it to the eBook file. It is more convenient to take the advantage of Amazon Web Service, so the user scenario is:</p>
<ol>
<li>Search Amazon by keyword or ISBN</li>
<li>Select the correct book</li>
<li>Update the eBook file</li>
<li>Save it to the database</li>
</ol>
<div class="img-shadow">
<img src="http://static.kunxi.org/customize-the-django-newform-admin-ui/add-book.png" alt="Add a Book" /></div>
<p>It is straightforward to override the default template and implementation: redirect the admin URL to our own version of <em>change_form.html</em> template and add_view implementation, then save the object regardless the data integration; or we may reuse the infrastructure, and just implement our required function. Pattee takes the latter approach.<br />
<br clear="all"/></p>
<p>First, declare the <em>BookAdmin</em> and <em>eBookAdmin</em> in <em>admin.py</em>, and hook <em>eBook</em> inline:</p>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">class</span> eBookAdmin<span class="br0">&#40;</span>admin.<span class="me1">StackedInline</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; model = eBook<br />
&nbsp; &nbsp; extra = 1</p>
<p><span class="kw1">class</span> BookAdmin<span class="br0">&#40;</span>admin.<span class="me1">ModelAdmin</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; form = BookForm<br />
&nbsp; &nbsp; inlines = <span class="br0">&#91;</span>eBookAdmin<span class="br0">&#93;</span><br />
&nbsp; &nbsp; change_form_template = <span class="st0">&#8216;book_change_form.html&#8217;</span></div>
</div>
<p>and don&#8217;t forget to register it to <em>admin.site</em>, which is required by Django 1.0.</p>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;">admin.<span class="kw3">site</span>.<span class="me1">register</span><span class="br0">&#40;</span>Book, BookAdmin<span class="br0">&#41;</span></div>
</div>
<p>Then in <em>BookForm</em>, we could override <em>full_clean</em> to provide the <em>cleaned_data</em>. And in <em>BookAdmin</em>, the following tricks will guard against duplication of <em>Book</em> instances:</p>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">def</span> save_form<span class="br0">&#40;</span><span class="kw2">self</span>, request, form, change<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> form.<span class="me1">is_valid</span><span class="br0">&#40;</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">try</span> :<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; instance = <span class="kw2">self</span>.<span class="me1">model</span>.<span class="me1">objects</span>.<span class="me1">get</span><span class="br0">&#40;</span>isbn = form.<span class="me1">cleaned_data</span><span class="br0">&#91;</span><span class="st0">&#8216;isbn&#8217;</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">except</span> ObjectDoesNotExist:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">super</span><span class="br0">&#40;</span>BookAdmin, <span class="kw2">self</span><span class="br0">&#41;</span>.<span class="me1">save_form</span><span class="br0">&#40;</span>request, form, change<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> instance</div>
</div>
<p>That is all. You may consider to check out the source code for details:</p>
<div class="codesnip-container" >svn checkout http://my-svn.assembla.com/svn/pattee/trunk pattee</div>
<div id="crp_related"><h2>Related Posts:</h2><ul><li><a href="http://kunxi.org/archives/2009/01/restful-django-practice/" rel="bookmark">RESTful Django practice</a></li><li><a href="http://kunxi.org/archives/2007/09/learning-django-by-example1-start-the-engine/" rel="bookmark">Learning Django by Example(1): Start the Engine</a></li><li><a href="http://kunxi.org/archives/2009/01/tip-reuse-django-view-in-urlconf/" rel="bookmark">Tip: Reuse Django view in urlconf</a></li><li><a href="http://kunxi.org/archives/2007/09/learning-django-by-example3-just-works/" rel="bookmark">Learning Django by Example(3): Just works</a></li><li><a href="http://kunxi.org/archives/2007/09/learning-django-by-example5-time-to-attack/" rel="bookmark">Learning Django by Example(5): Time to Attack</a></li></ul></div>]]></content:encoded>
			<wfw:commentRss>http://kunxi.org/archives/2009/01/customize-the-django-newform-admin-ui/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Restart the Django engine</title>
		<link>http://kunxi.org/archives/2009/01/restart-the-django-engine/</link>
		<comments>http://kunxi.org/archives/2009/01/restart-the-django-engine/#comments</comments>
		<pubDate>Sun, 11 Jan 2009 03:01:15 +0000</pubDate>
		<dc:creator>bookstack</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[gelman]]></category>
		<category><![CDATA[pattee]]></category>

		<guid isPermaLink="false">http://kunxi.org/?p=324</guid>
		<description><![CDATA[It has been one year since the last update of my Learning Django by Example series, partially in account of the demanding day job, the main reason lies in that the project is bloated with some advanced features which delay scratching my itch. Thanks to the snow storm in the holiday season, our vacation to [...]]]></description>
			<content:encoded><![CDATA[<p>It has been one year since the last update of my <em>Learning Django by Example</em> series, partially in account of the demanding day job, the main reason lies in that the project is bloated with some advanced features which delay scratching my itch. Thanks to the snow storm in the holiday season, our vacation to Las Vegas was canceled, I had time to restart the Django engine.</p>
<p>The new project, <a href="http://my-trac.assembla.com/pattee/">Pattee</a>, named after the library in Penn State University, aims to a simple, stupid eBook management system. It is so simple that it even does not have a dedicated UI, only integrated to a third-party web application(currently, <a href="http://www.douban.com">douban.com</a>). You may check it out at here:</p>
<div class="codesnip-container" >svn checkout http://my-svn.assembla.com/svn/pattee/trunk pattee</div>
<p>There is no step-by-step tutorial to follow the footprints of <a href="http://www.djangobook.com/en/2.0/">the Django Book</a>, each post will target a specific topic.</p>
<p>I may post</p>
<div id="crp_related"><h2>Related Posts:</h2><ul><li><a href="http://kunxi.org/archives/2009/01/customize-the-django-newform-admin-ui/" rel="bookmark">Customize the Django newform admin UI</a></li><li><a href="http://kunxi.org/archives/2007/12/learning-django-by-example5-software-is-hard/" rel="bookmark">Learning Django by Example(5): Software is hard</a></li><li><a href="http://kunxi.org/archives/2009/01/how-to-put-a-file-in-django/" rel="bookmark">How to PUT a file in Django</a></li><li><a href="http://kunxi.org/archives/2009/03/book-review-django-10-template-development/" rel="bookmark">Book Review: Django 1.0 Template Development</a></li><li><a href="http://kunxi.org/archives/2008/09/reverse-mashup/" rel="bookmark">Reverse mashup</a></li></ul></div>]]></content:encoded>
			<wfw:commentRss>http://kunxi.org/archives/2009/01/restart-the-django-engine/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

