RESTful Django practice
Python, Web January 14th, 2009
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, or it refers an eBook instance to for content presentation. We use an additional argument, format to differentiate them:
UPDATE A better approach is to use the pseudo file:
The default format is JSON, the server will render the meta data and available eBook formats in JSON.
| Client request | Server response |
|---|---|
| GET ../isbn(.pdf) |
Book meta data and available formats, or the eBook data. 404/Not Found if the book does not exists |
| PUT ../isbn |
201/Created if the server create a new Book instance. 200/OK if the book exists. 400/Bad Request if the ISBN is invalid. |
| PUT ../isbn.pdf |
201/Created if the specified format eBook does not exist. 200/OK if the eBook exists. The admin needs to moderate later. 400/Bad Request if the ISBN is invalid. |
|
|
|
| HEAD ../isbn.pdf |
The content length and other information about the file. 404/Not Found if the book does not exists. |
| DELETE ../isbn |
200/Accepted Remove the Book instance if no related eBook exists. 404/Not Found if the book does not exists. 409/Conflict if there exist at least one eBook related. |
| DELETE ../isbn.pdf |
200/Accepted Remove the eBook instance, the admin needs to moderate. 404/Not Found if the book does not exists. |
Furthermore, the URL representation is supposed to be discoverable. So we add two boring URL:
| Client request | Server response |
|---|---|
| GET /bookshelf/ |
Available list, currently only books supported. |
| GET /bookshelf/books |
All books with pagination, ?page=n |
Serialization
There exists a generic RESTful Django project, django-rest-interface, no surprise it takes the built-in JSON serializer.
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:
- Too much database details exposed to the end users.
- ForeignKey and ManyToMany are interpreted as external link using the id field.
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 all 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.
We will discuss the Pattee’s implementation next time.







Your proposed API looks very RESTy to me and I would probably have used the same one.
The url should probably be /bookshelf/books/(isbn|ean).(pdf|chm|…|json)
Query string are more suitable for search.
* The format should be specified through an Accept HTTP header rather than through a querystring
* I fail to see how a HEAD request on a resource could return “the available formats”, at best it can return the default format through Content-Type, unless you’re using custom HTTP headers of course
* You don’t describe the format (the content type) of `/bookshelf/books` even though it should link to the sub-resources, and your list of URLs isn’t really interesting. In other words, your API doesn’t seem hypertext-driven, I fear Roy Fielding would slaughter you if he knew: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
I agree, the query string is less appropriate as the extension.
@Masklinn:
If the user wants to check out the available eBook formats, but no intention to know the details of authors, publishers, … I am wondering whether HEAD could acts as a simple query? If not so, it can be dropped.
I also checked out roy.biv.com, he assumes that each URL should be representable, and guide the blind web crawler to find all the contents. In that sense, we need the following list:
/bookshelf/: list available object list: books, authors, publishers, etc
/bookshelf/books: list all available books with pagination. This is where query string shines: ?page=n
@Masklinn having the format in the querystring as an optional parameter makes it easier to “smoke test” services on the browser… Just my opinion, I think (and I’ve usually done that) you should have both, but enforce the header option.
In _RESTful Web Services_, one suggestion would be to have /bookshelf/books/{isbn} be metadata; this could be returned in a structured format (HTML, XML, JSON) and could contain elements pointing to the available representations, as in:
This makes the formats discoverable and “hypertext-driven”.
One downside to the use of the HTTP Accept header to specify format is that some clients cannot easily modify their headers (e.g. how do I make my web browser change that for a request)?
(Whoops, did not escape my markup in the first post!)
In _RESTful Web Services_, one suggestion would be to have /bookshelf/books/{isbn} be metadata; this could be returned in a structured format (HTML, XML, JSON) and could contain elements pointing to the available representations, as in:
<link rel=”alternate” href=”/bookshelf/books/{isbn}.json” type=”application/json”/>
This makes the formats discoverable and “hypertext-driven”.
One downside to the use of the HTTP Accept header to specify format is that some clients cannot easily modify their headers (e.g. how do I make my web browser change that for a request)?
[...] RESTful Django practice | Refactor the Life (tags: django python rest webdev) [...]
The issue with query strings is that the search may not be entirely accurate.
Hello!
Very Interesting post! Thank you for such interesting resource!
PS: Sorry for my bad english, I’v just started to learn this language ;)
See you!
Your, Raiul Baztepo