← Back to team overview

gavel-team team mailing list archive

Basic REST API Works

 

A basic REST API has been created. It uses two models, a Motion and a
Vote. URLs under the /api/ tree are used to access these objects in a
RESTful fashion, using HTTP methods.

A Motion object can be created by sending a POST request
to /api/motion/ with the appropriate data. The POST request will return
the contents of the object. The slug field in the object becomes the
URL for the object. The slug field can be customized but is derived
from the title by default. The slug is the URL for the object, the API
scheme being /api/motion/motions-slug/.

A Motion object can now be read with a GET request
to /api/motion/slug/, and updated with a PUT request to the same URL
with proper data. Changing the slug isn't supported. The motion can
also be deleted by sending a DELETE request to the URL, which just
returns HTTP Status Code 204, as per the HTTP rules on deleting.

A Vote object is related Many-to-One to a Motion object. At first I
thought One-to-One would be better, and we can still go back to that,
it'd be simpler. But currently there can be several votes on a motion.
Each Vote is dated and ordered by date for each Motion.

A Vote is created by sending a POST request with required data
to /api/motion/slug/vote/. Votes are ordered sequentially and
automatically. The first vote on a motion will have the
URL /api/motion/slug/vote/0/, the second .../1/, etc. The number is the
order field of the Vote object. The numbers are permanent; the numbers
will not be reordered if a Vote object is deleted.

Votes are one-time events and immutable. The Vote.save() method is
rigged to only run once, if it is run a second time it raises an
exception. Also, the REST handler for the Vote model does not allow the
PUT method. Votes can be GETed and DELETEd just as Motions can.

Attached to this email is a copy of a session using the UNIX
command-line HTTP tool curl to demonstrate using the REST API. This
file is also in the lp:gavel Bazaar repository as
rest_api_curl_example.txt. Hope it makes things clearer.

The next step is to implement a simple JavaScript app that uses this
API. This will be the frontend, while the Python remains the backend.
I'd use Pyjamas but Google doesn't use REST at all, preferring XML-RPC
and SOAP. As such, Pyjamas, which is based on Google Web Toolkit,
doesn't support REST but only XML-RPC, JSON-RPC, and SOAP. Completely
different Web API styles are the SOAPy styles and the RESTful styles.
Maybe there's a way to get Pyjamas to speak REST. If there isn't, we'll
be probably using either Dojo or YUI, as those are fully-featured
JavaScript frameworks. I've some familiarity with Dojo, but YUI from
what I hear is really nice, and Yahoo! uses REST internally quite a bit
so I expect YUI to support REST very well. Dojo has some nifty things
that fit nicely with Django, though, like its own implementation in
JavaScript of Django's template language.

-Josh
$ # This document demonstrates using the REST API for Gavel. It uses
$ # the command-line HTTP tool curl.
$ 
$ # We can create an object by retreiving URL with POST.
$ # If data is provided on the command line, curl defaults to POST. Without it it defaults to GET.
$ curl http://127.0.0.1:8000/api/motion/ -F "title=A Motion" -F "text=Create a parliamentary procedure program."
{
    "text": "Create a parliamentary procedure program.", 
    "slug": "a-motion", 
    "title": "A Motion"
}
$ 
$ # We can now view that object by retreiving its url, composed of the slug, with GET.
$ curl http://127.0.0.1:8000/api/motion/a-motion/
{
    "text": "Create a parliamentary procedure program.", 
    "slug": "a-motion", 
    "title": "A Motion"
}
$ 
$ # We can modify an object by sending data with PUT.
$ # With curl, the method used is defined with the -X option.
$ curl http://127.0.0.1:8000/api/motion/a-motion/ -X PUT -F "text=Create a parliamentary procedure program with Python."
{
    "text": "Create a parliamentary procedure program with Python.", 
    "slug": "a-motion", 
    "title": "A Motion"
}
$ 
$ # We can now create a vote object for the motion object. Again using POST.
$ curl http://127.0.0.1:8000/api/motion/a-motion/vote/ -F "passed=True" -F "positive=4" -F "negative=3"
{
    "abstain": null, 
    "positive": "4", 
    "negative": "3", 
    "datetime": "2010-03-31 23:15:40", 
    "motion": {
        "text": "Create a parliamentary procedure program with Python.", 
        "slug": "a-motion", 
        "title": "A Motion"
    }, 
    "passed": "True", 
    "order": 0
}
$ 
$ # We can also view the vote object with GET.
$ # Vote objects are immutable. Not only is the save method short-circuited, but the PUT method is not allowed.
$ curl http://127.0.0.1:8000/api/motion/a-motion/vote/0/
{
    "abstain": null, 
    "positive": 4, 
    "negative": 3, 
    "datetime": "2010-03-31 23:15:40", 
    "motion": {
        "text": "Create a parliamentary procedure program with Python.", 
        "slug": "a-motion", 
        "title": "A Motion"
    }, 
    "passed": true, 
    "order": 0
}
$ 
$ # Vote objects can, however, be deleted with the DELETE method.
$ curl http://127.0.0.1:8000/api/motion/a-motion/vote/0/ -X DELETE
$ # As you can see below, it's gone. Still need to implement a 404 of some kind.
$ curl http://127.0.0.1:8000/api/motion/a-motion/vote/0/
Piston/0.2.3rc1 (Django 1.1.1) crash report:

Traceback (most recent call last):

  File "/home/josh/Projects/Gavel/simple_gavel/gavel/../gavel/rules/api/handlers.py", line 43, in read
    return motion.vote_set.get(order=vote_id)

  File "/usr/lib/pymodules/python2.6/django/db/models/manager.py", line 120, in get
    return self.get_query_set().get(*args, **kwargs)

  File "/usr/lib/pymodules/python2.6/django/db/models/query.py", line 305, in get
    % self.model._meta.object_name)

DoesNotExist: Vote matching query does not exist.
$ 
$ # We can also delete motion objects.
$ curl http://127.0.0.1:8000/api/motion/a-motion/ -X DELETE
$ 
$ # Well, that explains the RESTful API now in place. Motions and Votes
$ # can be created, read, updated (motions only), and deleted using only
$ # HTTP methods. Only thing I can see being needed is a way to list all objects,
$ # or apply filters on full lists even.
$