Piston Documentation

    • Piston Documentation

      • Getting Started
      • Resources
      • Emitters
      • Mapping URLs

        • Anonymous Resources
      • Working with Models
      • Configuring Handlers

        • Model
        • Fields/Exclude
        • Anonymous
      • Authentication

        • OAuth
      • Form Validation
      • Helpers, utils & @decorators
      • Throttling
      • Generating Documentation

        • Resource URIs
      • Tests
      • Receiving data
      • Streaming
      • Configuration variables

Getting Started

Getting started with Piston is easy. Your API code will look and behave just like any other Django application. It will have an URL mapping and handlers defining resources.

To get started, it is recommended that you place your API code in a separate folder, e.g. 'api'.

Your application layout could look like this:

urls.py
settings.py
myapp/
   __init__.py
   views.py
   models.py
api/
   __init__.py
   urls.py
   handlers.py

Then, define a "namespace" where your API will live in your top-level urls.py, like so:

urlpatterns = patterns('',
   # all my other url mappings
   (r'^api/', include('mysite.api.urls')),
)

This will include the API's urls.py for anything beginning with 'api/'.

Next up we'll look at how we can create resources and how to map URLs to them.

Resources

A "Resource" is an entity mapping some kind of resource in code. This could be a blog post, a forum or even something completely arbitrary.

Let's start out by creating a simple handler in handlers.py:

from piston.handler import BaseHandler
from myapp.models import Blogpost

class BlogpostHandler(BaseHandler):
   allowed_methods = ('GET',)
   model = Blogpost

   def read(self, request, post_slug):
      ...

Piston lets you map resource to models, and by doing so, it will do a lot of the heavy lifting for you.

A resource can be just a class, but usually you would want to define at least 1 of 4 methods:

read is called on GET requests, and should never modify data (idempotent.)

create is called on POST, and creates new objects, and should return them (or rc.CREATED.)

update is called on PUT, and should update an existing product and return them (or rc.ALL_OK.)

delete is called on DELETE, and should delete an existing object. Should not return anything, just rc.DELETED.

In addition to these, you may define any other methods you want. You can use these by including their names in the fields directive, and by doing so, the function will be called with a single argument: The instance of the model. It can then return anything, and the return value will be used as the value for that key.

NB: These "resource methods" should be decorated with the @classmethod decorator, as they will not always receive an instance of itself. For example, if you have a UserHandler defined, and you return a User from another handler, you will not receive an instance of that handler, but rather the UserHandler.

Since a single handler can be responsible for both single- and multiple-object data sets, you can differentiate between them in the read() method like so:

from piston.handler import BaseHandler
from myapp.models import Blogpost

class BlogpostHandler(BaseHandler):
   allowed_methods = ('GET',)
   model = Blogpost

    def read(self, request, blogpost_id=None):
        """
        Returns a single post if `blogpost_id` is given,
        otherwise a subset.

        """
        base = Blogpost.objects

        if blogpost_id:
            return base.get(pk=blogpost_id)
        else:
            return base.all() # Or base.filter(...)

Emitters

Emitters are what spews out the data, and are the things responsible for speaking YAML, JSON, XML, Pickle and Django. They currently reside in emitters.py as XMLEmitter, JSONEmitter, YAMLEmitter, PickleEmitter and DjangoEmitter.

Writing your own emitters is easy, all you have to do is create a class that subclasses Emitter and has a render method. The render method will receive 1 argument, 'request', which is a copy of the request object, which is useful if you need to look at request.GET (like defining callbacks, like the JSON emitter does.)

To get the data to serialize/render, you can call self.construct() which always returns a dictionary. From there, you can do whatever you want with the data and return it (as a unicode string.)

NB: New in 23ebc37c78e8
: Emitters can now be registered with the Emitter.register function, and can be removed (in case you want to remove a built-in emitter) via the Emitter.unregister function.

The built-in emitters are registered like so:

class JSONEmitter(Emitter):
   ...

Emitter.register('json', JSONEmitter, 'application/json; charset=utf-8')

If you write your own emitters, you can import Emitter and call 'register' on it to put your emitter into action. You can also overwrite built-in, or existing emitters, by using the same name (the first argument.)

This makes it very easy to add support for extended formats, like protocol buffers or CSV.

Emitters are accessed via the ?format GET argument, e.g. '/api/blogposts/?format=yaml', but since 23ebc37c78e8
, it is now possible to access them via a special keyword argument in your URL mapping. This keyword is called 'emitter_format' (to not clash with your own 'format' keyword), and can be used like so:

urlpatterns = patterns('',
   url(r'^blogposts(\.(?P<emitter_format>.+))

发表回复