Sat, 05 Nov 2011
A Prompt Piston Primer
At work, we use django-piston to easily provide some RESTful APIs. For those who don't know, Piston is a reusable Django app that allows you to give RESTful access to models by writing simple handlers. These handlers specify the accessible fields, appropriate access methods and suchforth.
In this post, I'll give a quick introduction to Piston (limiting myself to GET APIs only) which I will expand upon in future posts.
Imagine a simple library scenario with the following models:
class Shelf(models.Model):
location = models.CharField(...)
class Book(models.Model):
title = models.CharField(...)
shelf = models.ForeignKey(Shelf, related_name="books")
For some reason (it's an enterprise library, perhaps) we need a REST API on to our books data, so we can easily fetch their title and shelf via HTTP. We'll start by writing a basic handler for books:
from piston.handler import BaseHandler
from models import Book
class BookHandler(BaseHandler):
model = Book
That's it. Except it isn't quite, as we need to hook it up in our urls.py:
from django.conf.urls.defaults import patterns, include, url
from piston.resource import Resource
from handlers import BookHandler
book_resource = Resource(BookHandler)
urlpatterns = patterns('',
(r'^book/(?P<id>[^/.])', book_resource)
)
Here we wrap up our handler in a resource (which handles all of the view-like stuff for us) and set /book/<id> to point to it. If you run your server, you should now be able to point at /book/1 and receive a 404. If you create a Book (and a Shelf for it to live on), you should get it back in JSON:
{
"shelf": {
"_state": "<django.db.models.base.ModelState object at 0x2be6090>",
"id": 1,
"location": "Shelf Location 1"
},
"title": "Book 1"
}
Voila! You have the book's title and, even better, you have all of the shelf information included as well. Problem solved! Well, not quite. We don't actually want all of our shelf information included. _state is an internal Django thing [0]. id is an internal identifier which we don't want any consumer of our API to be able to access.
The most obvious option is to specify which fields we include in the API output:
class BookHandler(BaseHandler):
model = Book
fields = ('title',
('shelf', ('location',))
This syntax is, I hope, relatively obvious. fields contains a list of all the field we want our API output to include. Where we have a related model (shelf in this example) we can use a two-tuple specifying the field name and the list of fields on the related model to include in the output (we only want location). This gives us the following JSON output:
{
"shelf": {
"location": "Shelf Location 1"
},
"title": "Book 1"
}
This looks exactly as we want. Problem solved! Well, not quite. Let's say that we need to add a field to Book (e.g. page_count). Because we are hard-coding the fields that the API produces, we will have to add it to the fields attribute of our BookHandler. The same is true if we want to add another field to Shelf; we'll have to modify that shelf tuple to include it. There must be a better way.
There is. If we think back to our BookHandler, Piston worked out what fields there were on the Book model all by itself. Also, the Book model has an id field (and probably a _state attribute) and Piston didn't expose those through the API. This tells us two things about Piston handlers:
- They automatically find fields on the model which they handle, and
- They automatically filter out certain attributes which we probably don't want to reveal to the world at large.
These are exactly the properties that we are looking for when exposing a Book's Shelf through the API, so let's remove the fields attribute on our BookHandler and write a ShelfHandler:
class ShelfHandler(BaseHandler):
model = Shelf
class BookHandler(BaseHandler):
model = Book
If you hit our API now, you will see that the JSON returned is exactly the same as before we made the change. However, if you experiment with modifying the models, you will see that those changes are reflected in the API output with no further modifications by us.
This reflects the last lesson that I want to offer in this post: Piston keeps a registry of handlers for models. If a related model is referenced without any guidelines as to how to display it (as Shelf is implicitly referenced in our most recent BookHandler), Piston will use a handler defined for that model if it is available [1].
Let's quickly review what we've learned:
- Piston is a Django app for providing REST interfaces,
- How to write basic handlers for models,
- How to specify which fields on a model should be included in the API, both for the model itself and any related models, and
- How Piston uses handlers by default when working out how to represent related models in API output.
Whilst writing this post, I've been recording my work in git, you can find the repository at https://github.com/OddBloke/Books-Sample-API.
In a future blog post, I will cover how we handle circular dependencies. I may also write posts about how to get different output formats and, if I do some research, how to use Piston to provide more than a read-only API.
| [0] | I am using the most recent release of Piston. It would not surprise me if this particular foible had already been fixed in development. |
| [1] | One limitation of this functionality is that the registry only supports a single handler per model, and it will silently overwrite the entry in the registry if you define a second one, which can lead to very confusing behaviour. I will briefly touch on this when I write about circular dependencies. |
Posted: Sat, 05 Nov 2011 09:51 | | Comments: 4 |
Fri, 04 Nov 2011
Reducing the Vertical Height of Google Reader
If you're like me, then you're a big fan of the new Google Reader interface, except when using it on a small screen, where the vertical height it takes up is just too much. The good news is that I've discovered a simple way of fixing this if you're running Google Chrome:
Install Minimalist for Google Reader.
Open the options dialog.
It should open in the "General" tab. If not, click that in the sidebar.
Check the "Use custom CSS" option.
Paste the following in to the text box:
#top-bar { height: auto; } #search { padding: 5px 0; } #logo { margin-top: -13px; } #lhn-add-subscription-section { height: 35px; } #viewer-header { height: 35px; } #viewer-top-controls-container, #lhn-add-subscription { margin-top: -17px; } #title-and-status-holder { padding: .5ex 0 0 .5em; } #entries-status { margin-top: 3px; }Reload Google Reader.
Voila!
Posted: Fri, 04 Nov 2011 20:59 | | Comments: 1 |
Thu, 03 Nov 2011
reStructuredText Preview for PyBlosxom

This blog runs on blogging software called PyBlosxom, running on my VPS. I have never really used it as much as I could have, partly because my blog writing is sporadic at the best of times, and partly because I find the software itself somewhat difficult to work with. My main qualm is that, out of the box, you have to write plain HTML for your posts. This is moderately painful when it comes to writing free-form text, but truly terrible if you want to write technical posts including code.
So I started looking around at various platforms, most of which provided a rich-text editor. I've never found rich-text editors particularly useful, partly because I love me some semantic markup [1] but mostly because they tend to be designed, again, for free-form writing with occasional formatting.
At work, we've started using Sphinx for all of our documentation, which uses reStructuredText, of which I am a great fan. So I was thinking to myself, wouldn't it be great if I could use a blogging platform which allowed me to write in reST.
It transpires that PyBlosxom is just such a platform [2], so I'm sticking with it.
One of the advantages of writing my posts in HTML is that I could easily see what they were going to look like whilst writing them, by pointing a browser at the file on my local machine. With reST, this wasn't immediately possible, which posed something of a problem.
To solve this, I've written a little Python script which takes a file as its first argument, processes it, writes the HTML output (with the title included) on stdout and the metadata for the post on stderr. I've been using it to preview this post like so:
python preview_rest.py pyblosxom-rest-preview.rst > preview.html
You'll need to have docutils installed (python-docutils on Debian) for it to work. Enjoy!
| [1] | If I could easily write my blog posts in LaTeX, I probably would. |
| [2] | Come back, PyBlosxom, all is forgiven. See http://pyblosxom.bluesock.org/registry/text/rst.html. |
Posted: Thu, 03 Nov 2011 21:11 | | Comments: 1 |