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:

/bookshelf/books/(isbn|ean)?format=(pdf|chm|…|json)

UPDATE A better approach is to use the pseudo file:

/bookshelf/books/(isbn|ean)(.(pdf|chm|…|json))

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

Available formats.

404/Not Found if the book does not exists.

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.

Share and Enjoy:
  • Print this article!
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks

11 Comments to “RESTful Django practice”

  1. Sebastian Rittau | January 15th, 2009 at 2:23 am

    Your proposed API looks very RESTy to me and I would probably have used the same one.

  2. damien | January 15th, 2009 at 4:31 am

    The url should probably be /bookshelf/books/(isbn|ean).(pdf|chm|…|json)

    Query string are more suitable for search.

  3. Masklinn | January 15th, 2009 at 10:55 am

    * 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

  4. bookstack | January 15th, 2009 at 8:08 pm

    I agree, the query string is less appropriate as the extension.

  5. bookstack | January 15th, 2009 at 8:54 pm

    @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

  6. Adrian | January 16th, 2009 at 12:00 am

    @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.

  7. Jon Moore | January 16th, 2009 at 1:35 pm

    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)?

  8. Jon Moore | January 16th, 2009 at 1:37 pm

    (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)?

  9. links for 2009-01-17 | zoia.org | January 17th, 2009 at 4:04 pm

    [...] RESTful Django practice | Refactor the Life (tags: django python rest webdev) [...]

  10. Martinez Marchand | March 28th, 2009 at 7:16 am

    The issue with query strings is that the search may not be entirely accurate.

  11. RaiulBaztepo | March 28th, 2009 at 5:06 pm

    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

Leave a Comment