this is the last status update for my Summer of Code project [1]
before (or while) the final evaluation takes place. After a few words
on last week's changes, I would like to summarize what is currently
possible using the REST interface, what is still left to do and why
the REST interface needs your help.
During the last week, I added the option to specify the submission
content type for model-based resources. In addition to the default
setting, form submission, data to create and update models can now be
in any of the formats that the serialization framework can handle. Web
browsers may use JSON to create an object, software clients may use
XML. Additionally, besides besides bug fixes thanks to Malcolm's code
review, you might have noticed that the class for model-based
resources (model_resource.Collection) and the class for generic
resources (resource.Resource) have become subclasses of ResourceBase.
This is due to architectural changes that are still not completely
finished (more on that below).
In my initial proposal [2], I mentioned two success criteria. First, a
few lines of code in urls.py should be sufficient to make existing
model data accessible for create/read/update/delete (CRUD) operations
via XML and JSON. Second, resources that don't correspond directly to
models should be possible and these resources should integrate well
with the rest of the API.
What can the REST interface do for your existing Django project --
today? Let's assume you want to make your model data available in XML
format (you could also have chosen JSON, any other serialized format
or templates using a mimetype of your choice). Create a Collection
instance in urls.py, specify a few options, add the collection to your
urlpatterns and note that you just added a complete XML API to your
project [3]. You can specify which subset of models you want to make
public, which CRUD operations you want to allow and which model fields
you want to expose. You can secure your API with an authentication
class of your choice (HTTP Basic and HTTP Digest are included). Since
last week, you can also specify whether you want PUT/POST requests to
be standard form submissions or whether your API should handle XML or
JSON data. Each of these options takes exactly one line of code.
Not every resource you may want to expose will map neatly to a Django
model. For example, if you want to expose the relation between two
models as a resource or add an index page for service discovery, you
can do so by subclassing resource.Resource, overwriting some or all of
the create/read/update/delete methods and adding it to your
urlpatterns, just as you would for model-based resources. Much of the
automation that makes model-based resources so easy cannot be used
here, but part of it can be used -- namely, authentication and
permitted methods -- and I hope to further extend the automation for
generic resources in the future.
Yesterday, I went through code and wiki pages, collected all to-do
items and added them to the Google Code ticket tracker [4]. If you
feel that the REST interface lacks important features or if you
stumble upon any bugs, please open a ticket (I get e-mail
notifications for any ticket changes). Submit code that fixes any of
the problems and I -- and probably quite a few other people -- will be
very grateful. Now, since the official Summer of Code period is over,
committing foreign code is definitely no longer a problem.
Besides the tasks mentioned in the tickets, there are a few places
where the overall architecture doesn't feel perfect. I will mention
the two most striking here, hoping that some of you may see solutions
that I overlooked or throw in ideas that help us in finding
improvements together.
Initially, I imagined the model_resource classes Entry and Collection
as both being subclasses of a common resource class (as they are both
resources), with both reusing quite a bit of code from the generic
class. It turned out that there is really not much code that is common
to Entry and Collection and that it does not make sense to make Entry
a subclass of ResourceBase, at least not within my current design. Can
you think of a way to refactor Entry and Collection to make the two
classes a little less asymmetric?
I am also not completely happy with the relation between
model_resource.Collection and the responder classes. I really like the
compartmentalization into the 'managing' resource class and the
largely independent responder/receiver/authentication classes, but it
is this independence that makes it hard to access the URLs assigned to
collections and entries for the inclusion in serialized output and for
reverse URIs in templates as suggested by David Larlet [5]. I
appreciate any suggestions on how to improve the data flow between the
different parts of the REST interface.
The REST interface needs your help. Now that the project contains
enough functionality to make it an interesting extension at least for
your pet projects, please give it a try, test, test, test, create
tickets for any bugs you find and then, test some more.
Thank you, Malcolm Tredinnick, for great advice and great patience.
Next time, I will try to reduce the number of times I made your head
explode and your brain leak out of your nose a bit :-). I am also
grateful to anyone who asked questions or suggested features and, of
course, to the community of Django developers in general.
I hope you like and use the Django REST interface.
Cheers,
Andreas Stuhlmüller
[1] http://code.google.com/p/django-rest-interface/
[2] http://www.aiplayground.org/artikel/improving-django/
[3] http://django-rest-interface.googlecode.com/svn/trunk/django_restapi_tests/examples/
[4] http://code.google.com/p/django-rest-interface/issues/list
[5] http://groups.google.com/group/django-developers/msg/00481aec2ef71a38
Hi Andreas
I cant find a dedicated mailing list for django-rest-interface so I am
posting here for the time being. I am not quite sure if I am doing something
wrong or not, but I can happily GET and DELETE objects but I am having
trouble getting PUT and POST to work, and your examples don't really go into
details about how and where the REST client should PUT/POST too..
I have the following (simplified) model:
class Device(models.Model):
MacAddress = models.CharField(primary_key=True, maxlength=12)
SerialNo = models.CharField(maxlength=20)
def __str__(self):
return str(self.MacAddress)
def save(self):
super(Device, self).save() # Call the "real" save() method.
syslog.syslog('Saving data for: ' + self.MacAddress)
def delete(self):
syslog.syslog('Deleting: ' + self.MacAddress)
super(Device, self).delete() # Call the "real" delete() method.
def post(self):
syslog.syslog('Updating: ' + self.MacAddress)
super(Device, self).post() # Call the "real" post() method.
def put(self):
syslog.syslog('Adding: ' + self.MacAddress)
super(Device, self).put() # Call the "real" delete() method.
In urls.py I have:
device_resource = Collection(
queryset = Device.objects.all(),
permitted_methods = ('GET', 'POST', 'PUT', 'DELETE'),
receiver = XMLReceiver(),
responder = XMLResponder(paginate_by = 10)
)
urlpatterns = patterns('',
(r'^xml/device/(.*?)/?$', device_resource),
)
I have written a simple client using "restclient". You can see it works here
for GET and DELETE:
# ./atclient -g 000000000001
Processing MAC: 000000000001
<?xml version="1.0" encoding="utf-8"?>
<django-objects version="1.0"><object pk="000000000001"
model="provisioning.device"><field type="CharField"
name="SerialNo">000000000001</field></object></django-objects>
# ./atclient -d 000000000001
Deleting MAC: 000000000001
Object successfully deleted.
However trying to PUT a new object fails..
# ./atclient -a 000000000001
Adding MAC: 000000000001
<?xml version="1.0" encoding="utf-8"?>
<django-error><error-message>404 NOT
FOUND</error-message><status-code>404</status-code></django-error>
I see the PUT arriving at the web server:
[10/Sep/2007 15:03:50] "PUT /xml/device/ HTTP/1.1" 404 142
What exactly should I PUT, and to what url to make it add an object into the
database?
My client code currently looks like:
#! /usr/bin/python
import sys
import getopt
from restclient import GET, POST, PUT, DELETE
urlBase = 'http://127.0.0.1:8000/xml/device/'
def GetMac(macId):
response = GET(urlBase + macId.lower() + '/')
print response
def DelMac(macId):
response = DELETE(urlBase + macId.lower() + '/', async=False)
print response
def PostMac(macId):
response = POST(urlBase + macId.lower() + '/', async=False)
print response
def AirTiesPutMac(macId):
response = PUT(urlBase, async=False)
print response
if __name__ == '__main__':
def print_usage():
usage = "Usage: atclient -g MacAddress"
print usage; sys.exit()
try: opts, args = getopt.getopt(sys.argv[1:], 'a:d:g:u:')
except getopt.GetoptError: print_usage()
opts = dict(opts)
try: mode = opts.keys()[0]
except IndexError: print_usage()
if mode == '-g':
print "Getting MAC: " + opts['-g']
GetMac(opts['-g'])
elif mode == '-d':
print "Deleting MAC: " + opts['-d']
DelMac(opts['-d'])
elif mode == '-a':
print "Adding MAC: " + opts['-a']
PutMac(opts['-a'])
elif mode == '-u':
print "Updating MAC: " + opts['-u']
PostMac(opts['-u'])
I would appreciate any help you can give.
Thanks in Advance
--
Peter Nixon
http://peternixon.net/