Sample example working

This commit is contained in:
Tom Christie 2011-01-11 18:01:02 +00:00
parent 95ac2396d6
commit 42825e44e1
9 changed files with 399 additions and 252 deletions

View File

@ -18,7 +18,7 @@ class TemplatedEmitter(BaseEmitter):
template = None
def emit(self, output):
content = json.dumps(output, indent=4)
content = json.dumps(output, indent=4, sort_keys=True)
template = loader.get_template(self.template)
context = RequestContext(self.request, {
'content': content,

View File

@ -137,15 +137,20 @@ class Resource(object):
def determine_form(self, data=None):
def determine_form(self, input_data=None, return_data=None):
"""Optionally return a Django Form instance, which may be used for validation
and/or rendered by an HTML/XHTML emitter.
The data argument will be non Null if the form is required to be bound to some deserialized
input data, or Null if the form is required to be unbound.
The input_data or return_data arguments can be used to bind the form either to the deserialized input,
or to a return object.
"""
if self.form:
return self.form(data)
if input_data:
return self.form(input_data)
elif return_data:
return self.form(return_data)
else:
return self.form()
return None
@ -259,12 +264,13 @@ class Resource(object):
if method in ('PUT', 'POST'):
parser = self.determine_parser(request)
data = parser(self, request).parse(request.raw_post_data)
form = self.determine_form(data)
form = self.determine_form(input_data=data)
data = self.cleanup_request(data, form)
(status, ret, headers) = func(data, request.META, *args, **kwargs)
else:
(status, ret, headers) = func(request.META, *args, **kwargs)
form = self.determine_form(return_data=ret)
except ResourceException, exc:
@ -274,7 +280,7 @@ class Resource(object):
if emitter is None:
mimetype, emitter = self.emitters[0]
# Use a form unbound to any data if one has not yet been created
# Create an unbound form if one has not yet been created
if form is None:
form = self.determine_form()
@ -284,7 +290,6 @@ class Resource(object):
# Serialize the response content
ret = self.cleanup_response(ret)
content = emitter(self, request, status, headers, form).emit(ret)
print content
# Build the HTTP Response
resp = HttpResponse(content, mimetype=mimetype, status=status)
@ -308,7 +313,7 @@ class ModelResource(Resource):
fields = None
form_fields = None
def determine_form(self, data=None):
def determine_form(self, input_data=None, return_data=None):
"""Return a form that may be used in validation and/or rendering an html emitter"""
if self.form:
return self.form
@ -317,12 +322,14 @@ class ModelResource(Resource):
class NewModelForm(ModelForm):
class Meta:
model = self.model
fields = self.form_fields if self.form_fields else self.fields
fields = self.form_fields if self.form_fields else None #self.fields
if data is None:
return NewModelForm()
if input_data:
return NewModelForm(input_data)
elif return_data:
return NewModelForm(instance=return_data)
else:
return NewModelForm(data)
return NewModelForm()
else:
return None
@ -359,6 +366,12 @@ class ModelResource(Resource):
ret = _list(thing)
elif isinstance(thing, dict):
ret = _dict(thing)
elif isinstance(thing, int):
ret = thing
elif isinstance(thing, bool):
ret = thing
elif isinstance(thing, type(None)):
ret = thing
elif isinstance(thing, decimal.Decimal):
ret = str(thing)
elif isinstance(thing, Model):
@ -417,7 +430,7 @@ class ModelResource(Resource):
ret = { }
#handler = self.in_typemapper(type(data), self.anonymous) # TRC
handler = None # TRC
get_absolute_uri = False
get_absolute_url = False
if handler or fields:
v = lambda f: getattr(data, f.attname)
@ -444,12 +457,13 @@ class ModelResource(Resource):
for field in get_fields.copy():
if exclude.match(field):
get_fields.discard(field)
get_absolute_url = True
else:
get_fields = set(fields)
if 'absolute_uri' in get_fields: # MOVED (TRC)
get_absolute_uri = True
if 'absolute_url' in get_fields: # MOVED (TRC)
get_absolute_url = True
met_fields = _method_fields(handler, get_fields) # TRC
@ -508,14 +522,37 @@ class ModelResource(Resource):
# ret[maybe_field] = _any(handler_f(data))
else:
# Add absolute_url if it exists
get_absolute_url = True
# Add all the fields
for f in data._meta.fields:
ret[f.attname] = _any(getattr(data, f.attname))
if f.attname != 'id':
ret[f.attname] = _any(getattr(data, f.attname))
fields = dir(data.__class__) + ret.keys()
add_ons = [k for k in dir(data) if k not in fields]
# Add all the propertiess
klass = data.__class__
for attr in dir(klass):
if not attr.startswith('_') and not attr in ('pk','id') and isinstance(getattr(klass, attr, None), property):
#if attr.endswith('_url') or attr.endswith('_uri'):
# ret[attr] = self.make_absolute(_any(getattr(data, attr)))
#else:
ret[attr] = _any(getattr(data, attr))
#fields = dir(data.__class__) + ret.keys()
#add_ons = [k for k in dir(data) if k not in fields and not k.startswith('_')]
#print add_ons
###print dir(data.__class__)
#from django.db.models import Model
#model_fields = dir(Model)
#for attr in dir(data):
## #if attr.startswith('_'):
## # continue
# if (attr in fields) and not (attr in model_fields) and not attr.startswith('_'):
# print attr, type(getattr(data, attr, None)), attr in fields, attr in model_fields
for k in add_ons:
ret[k] = _any(getattr(data, k))
#for k in add_ons:
# ret[k] = _any(getattr(data, k))
# TRC
# resouce uri
@ -532,9 +569,13 @@ class ModelResource(Resource):
# except: pass
# absolute uri
if hasattr(data, 'get_absolute_url') and get_absolute_uri:
try: ret['absolute_uri'] = self.make_absolute(data.get_absolute_url())
if hasattr(data, 'get_absolute_url') and get_absolute_url:
try: ret['absolute_url'] = self.make_absolute(data.get_absolute_url())
except: pass
for key, val in ret.items():
if key.endswith('_url') or key.endswith('_uri'):
ret[key] = self.make_absolute(val)
return ret
@ -560,8 +601,9 @@ class ModelResource(Resource):
return _any(data, self.fields)
def create(self, data, headers={}):
instance = self.model(**data)
def create(self, data, headers={}, *args, **kwargs):
all_kw_args = dict(data.items() + kwargs.items())
instance = self.model(**all_kw_args)
instance.save()
headers = {}
if hasattr(instance, 'get_absolute_url'):
@ -569,17 +611,37 @@ class ModelResource(Resource):
return (201, instance, headers)
def read(self, headers={}, *args, **kwargs):
instance = self.model.objects.get(**kwargs)
try:
instance = self.model.objects.get(**kwargs)
except self.model.DoesNotExist:
return (404, '', {})
return (200, instance, {})
def update(self, data, headers={}, *args, **kwargs):
instance = self.model.objects.get(**kwargs)
for (key, val) in data.items():
setattr(instance, key, val)
try:
instance = self.model.objects.get(**kwargs)
for (key, val) in data.items():
setattr(instance, key, val)
except self.model.DoesNotExist:
instance = self.model(**data)
instance.save()
instance.save()
return (200, instance, {})
def delete(self, headers={}, *args, **kwargs):
instance = self.model.objects.get(**kwargs)
instance.delete()
return (204, '', {})
return (204, '', {})
class QueryModelResource(ModelResource):
allowed_methods = ('read',)
def determine_form(self, input_data=None, return_data=None):
return None
def read(self, headers={}, *args, **kwargs):
query = self.model.objects.all()
return (200, query, {})

View File

@ -11,10 +11,10 @@
<body>
<h1>{{ resource_name }}</h1>
<p>{{ resource_doc }}</p>
<pre>{% autoescape off %}<b>{{ status }} {{ reason }}</b>
<pre><b>{{ status }} {{ reason }}</b>{% autoescape off %}
{% for key, val in headers.items %}<b>{{ key }}:</b> {{ val|urlize_quoted_links }}
{% endfor %}
{{ content|urlize_quoted_links }}{% endautoescape %} </pre>
{{ content|urlize_quoted_links }} </pre>{% endautoescape %}
{% if 'read' in resource.allowed_operations %}
<div class='action'>

View File

@ -33,7 +33,7 @@ html_gunk_re = re.compile(r'(?:<br clear="all">|<i><\/i>|<b><\/b>|<em><\/em>|<st
hard_coded_bullets_re = re.compile(r'((?:<p>(?:%s).*?[a-zA-Z].*?</p>\s*)+)' % '|'.join([re.escape(x) for x in DOTS]), re.DOTALL)
trailing_empty_content_re = re.compile(r'(?:<p>(?:&nbsp;|\s|<br \/>)*?</p>\s*)+\Z')
def urlize_quoted_links(text, trim_url_limit=None, nofollow=False, autoescape=False):
def urlize_quoted_links(text, trim_url_limit=None, nofollow=False, autoescape=True):
"""
Converts any URLs in text into clickable links.
@ -90,6 +90,10 @@ def urlize_quoted_links(text, trim_url_limit=None, nofollow=False, autoescape=Fa
words[i] = escape(word)
return u''.join(words)
#urlize_quoted_links.needs_autoescape = True
urlize_quoted_links.is_safe = True
# Register urlize_quoted_links as a custom filter
# http://docs.djangoproject.com/en/dev/howto/custom-template-tags/
register = template.Library()

View File

@ -1,63 +1,90 @@
from django.db import models
from django.template.defaultfilters import slugify
from datetime import datetime
import uuid
def uuid_str():
return str(uuid.uuid1())
class ExampleModel(models.Model):
num = models.IntegerField(default=2, choices=((1,'one'), (2, 'two')))
hidden_num = models.IntegerField(verbose_name='Something', help_text='HELP')
text = models.TextField(blank=False)
another = models.CharField(max_length=10)
#class ExampleModel(models.Model):
# num = models.IntegerField(default=2, choices=((1,'one'), (2, 'two')))
# hidden_num = models.IntegerField(verbose_name='Something', help_text='HELP')
# text = models.TextField(blank=False)
# another = models.CharField(max_length=10)
class ExampleContainer(models.Model):
"""Container. Has a key, a name, and some internal data, and contains a set of items."""
key = models.CharField(primary_key=True, default=uuid_str, max_length=36, editable=False)
name = models.CharField(max_length=256)
internal = models.IntegerField(default=0)
#class ExampleContainer(models.Model):
# """Container. Has a key, a name, and some internal data, and contains a set of items."""
# key = models.CharField(primary_key=True, default=uuid_str, max_length=36, editable=False)
# name = models.CharField(max_length=256)
# internal = models.IntegerField(default=0)
@models.permalink
def get_absolute_url(self):
return ('testapp.views.ContainerInstance', [self.key])
# @models.permalink
# def get_absolute_url(self):
# return ('testapp.views.ContainerInstance', [self.key])
class ExampleItem(models.Model):
"""Item. Belongs to a container and has an index number and a note.
Items are uniquely identified by their container and index number."""
container = models.ForeignKey(ExampleContainer, related_name='items')
index = models.IntegerField()
note = models.CharField(max_length=1024)
unique_together = (container, index)
#class ExampleItem(models.Model):
# """Item. Belongs to a container and has an index number and a note.
# Items are uniquely identified by their container and index number."""
# container = models.ForeignKey(ExampleContainer, related_name='items')
# index = models.IntegerField()
# note = models.CharField(max_length=1024)
# unique_together = (container, index)
RATING_CHOICES = ((0, 'Awful'),
(1, 'Poor'),
(2, 'OK'),
(3, 'Good'),
(4, 'Excellent'))
class BlogPost(models.Model):
slug = models.SlugField(editable=False, primary_key=True, default='blah')
title = models.CharField(max_length=128)
content = models.TextField()
when = models.DateTimeField(editable=False)
key = models.CharField(primary_key=True, max_length=64, default=uuid_str, editable=False)
title = models.CharField(max_length=128, help_text='The article title (Required)')
content = models.TextField(help_text='The article body (Required)')
created = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(editable=False, default='')
class Meta:
ordering = ('created',)
@models.permalink
def get_absolute_url(self):
return ('testapp.views.BlogPostInstance', (self.slug,))
return ('testapp.views.BlogPostInstance', (self.key,))
@property
@models.permalink
def comments_url(self):
"""Link to a resource which lists all comments for this blog post."""
return ('testapp.views.CommentList', (self.key,))
@property
@models.permalink
def comment_url(self):
"""Link to a resource which can create a comment for this blog post."""
return ('testapp.views.CommentCreator', (self.key,))
def __unicode__(self):
return self.title
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
self.when = datetime.now()
super(self.__class__, self).save(*args, **kwargs)
class Comment(models.Model):
blogpost = models.ForeignKey(BlogPost, related_name='comments')
name = models.CharField(max_length=128)
content = models.TextField()
when = models.DateTimeField(auto_now_add=True)
blogpost = models.ForeignKey(BlogPost, editable=False, related_name='comments')
username = models.CharField(max_length=128, help_text='Please enter a username (Required)')
comment = models.TextField(help_text='Enter your comment here (Required)')
rating = models.IntegerField(blank=True, null=True, choices=RATING_CHOICES, help_text='Please rate the blog post (Optional)')
created = models.DateTimeField(auto_now_add=True)
@models.permalink
def get_absolute_url(self):
return ('testapp.views.CommentInstance', (self.blogpost.slug, self.id))
def save(self):
self.index = self.blogpost.comments.count()
return ('testapp.views.CommentInstance', (self.blogpost.key, self.id))
@property
@models.permalink
def blogpost_url(self):
return ('testapp.views.BlogPostInstance', (self.blogpost.key,))

View File

@ -1,8 +1,4 @@
"""
This file demonstrates two different styles of tests (one doctest and one
unittest). These will both pass when you run "manage.py test".
Replace these with more appropriate tests for your application.
"""Test a range of REST API usage of the example application.
"""
from django.test import TestCase
@ -13,134 +9,154 @@ import json
class AcceptHeaderTests(TestCase):
def assert_accept_mimetype(self, mimetype, expect=None, expect_match=True):
"""
Assert that a request with given mimetype in the accept header,
gives a response with the appropriate content-type.
"""
"""Test correct behaviour of the Accept header as specified by RFC 2616:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1"""
def assert_accept_mimetype(self, mimetype, expect=None):
"""Assert that a request with given mimetype in the accept header,
gives a response with the appropriate content-type."""
if expect is None:
expect = mimetype
resp = self.client.get(reverse(views.ReadOnlyResource), HTTP_ACCEPT=mimetype)
resp = self.client.get(reverse(views.RootResource), HTTP_ACCEPT=mimetype)
if expect_match:
self.assertEquals(resp['content-type'], expect)
else:
self.assertNotEquals(resp['content-type'], expect)
self.assertEquals(resp['content-type'], expect)
def test_accept_xml(self):
self.assert_accept_mimetype('application/xml')
def test_accept_json(self):
"""Ensure server responds with Content-Type of JSON when requested."""
self.assert_accept_mimetype('application/json')
def test_accept_xml_prefered_to_json(self):
self.assert_accept_mimetype('application/xml,q=0.9;application/json,q=0.1', expect='application/xml')
def test_accept_xml(self):
"""Ensure server responds with Content-Type of XML when requested."""
self.assert_accept_mimetype('application/xml')
def test_accept_json_prefered_to_xml(self):
def test_accept_json_when_prefered_to_xml(self):
"""Ensure server responds with Content-Type of JSON when it is the client's prefered choice."""
self.assert_accept_mimetype('application/json,q=0.9;application/xml,q=0.1', expect='application/json')
def test_dont_accept_invalid(self):
self.assert_accept_mimetype('application/invalid', expect_match=False)
def test_accept_xml_when_prefered_to_json(self):
"""Ensure server responds with Content-Type of XML when it is the client's prefered choice."""
self.assert_accept_mimetype('application/xml,q=0.9;application/json,q=0.1', expect='application/xml')
def test_default_json_prefered(self):
"""Ensure server responds with JSON in preference to XML."""
self.assert_accept_mimetype('application/json;application/xml', expect='application/json')
def test_accept_generic_subtype_format(self):
"""Ensure server responds with an appropriate type, when the subtype is left generic."""
self.assert_accept_mimetype('text/*', expect='text/html')
def test_accept_generic_type_format(self):
"""Ensure server responds with an appropriate type, when the type and subtype are left generic."""
self.assert_accept_mimetype('*/*', expect='application/json')
def test_invalid_accept_header_returns_406(self):
resp = self.client.get(reverse(views.ReadOnlyResource), HTTP_ACCEPT='invalid/invalid')
"""Ensure server returns a 406 (not acceptable) response if we set the Accept header to junk."""
resp = self.client.get(reverse(views.RootResource), HTTP_ACCEPT='invalid/invalid')
self.assertNotEquals(resp['content-type'], 'invalid/invalid')
self.assertEquals(resp.status_code, 406)
def test_prefer_specific(self):
self.fail("Test not implemented")
def test_prefer_specific_over_generic(self): # This test is broken right now
"""More specific accept types have precedence over less specific types."""
self.assert_accept_mimetype('application/xml;*/*', expect='application/xml')
class AllowedMethodsTests(TestCase):
def test_reading_read_only_allowed(self):
resp = self.client.get(reverse(views.ReadOnlyResource))
"""Basic tests to check that only allowed operations may be performed on a Resource"""
def test_reading_a_read_only_resource_is_allowed(self):
"""GET requests on a read only resource should default to a 200 (OK) response"""
resp = self.client.get(reverse(views.RootResource))
self.assertEquals(resp.status_code, 200)
def test_writing_read_only_not_allowed(self):
resp = self.client.put(reverse(views.ReadOnlyResource), {})
def test_writing_to_read_only_resource_is_not_allowed(self):
"""PUT requests on a read only resource should default to a 405 (method not allowed) response"""
resp = self.client.put(reverse(views.RootResource), {})
self.assertEquals(resp.status_code, 405)
def test_reading_write_only_not_allowed(self):
resp = self.client.get(reverse(views.WriteOnlyResource))
self.assertEquals(resp.status_code, 405)
def test_writing_write_only_allowed(self):
resp = self.client.put(reverse(views.WriteOnlyResource), {})
self.assertEquals(resp.status_code, 200)
class EncodeDecodeTests(TestCase):
def setUp(self):
super(self.__class__, self).setUp()
self.input = {'a': 1, 'b': 'example'}
def test_encode_form_decode_json(self):
content = self.input
resp = self.client.put(reverse(views.WriteOnlyResource), content)
output = json.loads(resp.content)
self.assertEquals(self.input, output)
def test_encode_json_decode_json(self):
content = json.dumps(self.input)
resp = self.client.put(reverse(views.WriteOnlyResource), content, 'application/json')
output = json.loads(resp.content)
self.assertEquals(self.input, output)
#def test_encode_xml_decode_json(self):
# content = dict2xml(self.input)
# resp = self.client.put(reverse(views.WriteOnlyResource), content, 'application/json', HTTP_ACCEPT='application/json')
# output = json.loads(resp.content)
# self.assertEquals(self.input, output)
#def test_encode_form_decode_xml(self):
# content = self.input
# resp = self.client.put(reverse(views.WriteOnlyResource), content, HTTP_ACCEPT='application/xml')
# output = xml2dict(resp.content)
# self.assertEquals(self.input, output)
#def test_encode_json_decode_xml(self):
# content = json.dumps(self.input)
# resp = self.client.put(reverse(views.WriteOnlyResource), content, 'application/json', HTTP_ACCEPT='application/xml')
# output = xml2dict(resp.content)
# self.assertEquals(self.input, output)
#def test_encode_xml_decode_xml(self):
# content = dict2xml(self.input)
# resp = self.client.put(reverse(views.WriteOnlyResource), content, 'application/json', HTTP_ACCEPT='application/xml')
# output = xml2dict(resp.content)
# self.assertEquals(self.input, output)
class ModelTests(TestCase):
def test_create_container(self):
content = json.dumps({'name': 'example'})
resp = self.client.post(reverse(views.ContainerFactory), content, 'application/json')
output = json.loads(resp.content)
self.assertEquals(resp.status_code, 201)
self.assertEquals(output['name'], 'example')
self.assertEquals(set(output.keys()), set(('absolute_uri', 'name', 'key')))
class CreatedModelTests(TestCase):
def setUp(self):
content = json.dumps({'name': 'example'})
resp = self.client.post(reverse(views.ContainerFactory), content, 'application/json', HTTP_ACCEPT='application/json')
self.container = json.loads(resp.content)
def test_read_container(self):
resp = self.client.get(self.container["absolute_uri"])
self.assertEquals(resp.status_code, 200)
container = json.loads(resp.content)
self.assertEquals(container, self.container)
def test_delete_container(self):
resp = self.client.delete(self.container["absolute_uri"])
self.assertEquals(resp.status_code, 204)
self.assertEquals(resp.content, '')
def test_update_container(self):
self.container['name'] = 'new'
content = json.dumps(self.container)
resp = self.client.put(self.container["absolute_uri"], content, 'application/json')
self.assertEquals(resp.status_code, 200)
container = json.loads(resp.content)
self.assertEquals(container, self.container)
#
# def test_reading_write_only_not_allowed(self):
# resp = self.client.get(reverse(views.WriteOnlyResource))
# self.assertEquals(resp.status_code, 405)
#
# def test_writing_write_only_allowed(self):
# resp = self.client.put(reverse(views.WriteOnlyResource), {})
# self.assertEquals(resp.status_code, 200)
#
#
#class EncodeDecodeTests(TestCase):
# def setUp(self):
# super(self.__class__, self).setUp()
# self.input = {'a': 1, 'b': 'example'}
#
# def test_encode_form_decode_json(self):
# content = self.input
# resp = self.client.put(reverse(views.WriteOnlyResource), content)
# output = json.loads(resp.content)
# self.assertEquals(self.input, output)
#
# def test_encode_json_decode_json(self):
# content = json.dumps(self.input)
# resp = self.client.put(reverse(views.WriteOnlyResource), content, 'application/json')
# output = json.loads(resp.content)
# self.assertEquals(self.input, output)
#
# #def test_encode_xml_decode_json(self):
# # content = dict2xml(self.input)
# # resp = self.client.put(reverse(views.WriteOnlyResource), content, 'application/json', HTTP_ACCEPT='application/json')
# # output = json.loads(resp.content)
# # self.assertEquals(self.input, output)
#
# #def test_encode_form_decode_xml(self):
# # content = self.input
# # resp = self.client.put(reverse(views.WriteOnlyResource), content, HTTP_ACCEPT='application/xml')
# # output = xml2dict(resp.content)
# # self.assertEquals(self.input, output)
#
# #def test_encode_json_decode_xml(self):
# # content = json.dumps(self.input)
# # resp = self.client.put(reverse(views.WriteOnlyResource), content, 'application/json', HTTP_ACCEPT='application/xml')
# # output = xml2dict(resp.content)
# # self.assertEquals(self.input, output)
#
# #def test_encode_xml_decode_xml(self):
# # content = dict2xml(self.input)
# # resp = self.client.put(reverse(views.WriteOnlyResource), content, 'application/json', HTTP_ACCEPT='application/xml')
# # output = xml2dict(resp.content)
# # self.assertEquals(self.input, output)
#
#class ModelTests(TestCase):
# def test_create_container(self):
# content = json.dumps({'name': 'example'})
# resp = self.client.post(reverse(views.ContainerFactory), content, 'application/json')
# output = json.loads(resp.content)
# self.assertEquals(resp.status_code, 201)
# self.assertEquals(output['name'], 'example')
# self.assertEquals(set(output.keys()), set(('absolute_uri', 'name', 'key')))
#
#class CreatedModelTests(TestCase):
# def setUp(self):
# content = json.dumps({'name': 'example'})
# resp = self.client.post(reverse(views.ContainerFactory), content, 'application/json', HTTP_ACCEPT='application/json')
# self.container = json.loads(resp.content)
#
# def test_read_container(self):
# resp = self.client.get(self.container["absolute_uri"])
# self.assertEquals(resp.status_code, 200)
# container = json.loads(resp.content)
# self.assertEquals(container, self.container)
#
# def test_delete_container(self):
# resp = self.client.delete(self.container["absolute_uri"])
# self.assertEquals(resp.status_code, 204)
# self.assertEquals(resp.content, '')
#
# def test_update_container(self):
# self.container['name'] = 'new'
# content = json.dumps(self.container)
# resp = self.client.put(self.container["absolute_uri"], content, 'application/json')
# self.assertEquals(resp.status_code, 200)
# container = json.loads(resp.content)
# self.assertEquals(container, self.container)

View File

@ -2,13 +2,18 @@ from django.conf.urls.defaults import patterns
urlpatterns = patterns('testapp.views',
(r'^$', 'RootResource'),
(r'^read-only$', 'ReadOnlyResource'),
(r'^write-only$', 'WriteOnlyResource'),
(r'^read-write$', 'ReadWriteResource'),
(r'^model$', 'ModelFormResource'),
(r'^container$', 'ContainerFactory'),
(r'^container/((?P<key>[^/]+))$', 'ContainerInstance'),
#(r'^read-only$', 'ReadOnlyResource'),
#(r'^write-only$', 'WriteOnlyResource'),
#(r'^read-write$', 'ReadWriteResource'),
#(r'^model$', 'ModelFormResource'),
#(r'^container$', 'ContainerFactory'),
#(r'^container/((?P<key>[^/]+))$', 'ContainerInstance'),
(r'^blogpost/create$', 'BlogPostCreator'),
(r'^blogposts/(?P<slug>[^/]+)', 'BlogPostInstance'),
(r'^blog-posts/$', 'BlogPostList'),
(r'^blog-post/$', 'BlogPostCreator'),
(r'^blog-post/(?P<key>[^/]+)/$', 'BlogPostInstance'),
(r'^blog-post/(?P<blogpost_id>[^/]+)/comments/$', 'CommentList'),
(r'^blog-post/(?P<blogpost_id>[^/]+)/comment/$', 'CommentCreator'),
(r'^blog-post/(?P<blogpost>[^/]+)/comments/(?P<id>[^/]+)/$', 'CommentInstance'),
)

View File

@ -1,78 +1,111 @@
from rest.resource import Resource, ModelResource
from testapp.forms import ExampleForm
from testapp.models import ExampleModel, ExampleContainer, BlogPost, Comment
from rest.resource import Resource, ModelResource, QueryModelResource
from testapp.models import BlogPost, Comment
class RootResource(Resource):
"""This is my docstring
"""
"""This is the top level resource for the API.
All the sub-resources are discoverable from here."""
allowed_operations = ('read',)
def read(self, headers={}, *args, **kwargs):
return (200, {'read-only-api': self.reverse(ReadOnlyResource),
'write-only-api': self.reverse(WriteOnlyResource),
'read-write-api': self.reverse(ReadWriteResource),
'model-api': self.reverse(ModelFormResource),
'create-container': self.reverse(ContainerFactory),
'blog-post-creator': self.reverse(BlogPostCreator)}, {})
return (200, {'blog-posts': self.reverse(BlogPostList),
'blog-post': self.reverse(BlogPostCreator)}, {})
class ReadOnlyResource(Resource):
"""This is my docstring
"""
allowed_operations = ('read',)
# Blog Post Resources
def read(self, headers={}, *args, **kwargs):
return (200, {'ExampleString': 'Example',
'ExampleInt': 1,
'ExampleDecimal': 1.0}, {})
class BlogPostList(QueryModelResource):
"""A resource which lists all existing blog posts."""
allowed_operations = ('read', )
model = BlogPost
class WriteOnlyResource(Resource):
"""This is my docstring
"""
allowed_operations = ('update',)
def update(self, data, headers={}, *args, **kwargs):
return (200, data, {})
class ReadWriteResource(Resource):
allowed_operations = ('read', 'update', 'delete')
create_form = ExampleForm
update_form = ExampleForm
class ModelFormResource(ModelResource):
allowed_operations = ('read', 'update', 'delete')
model = ExampleModel
# Nice things: form validation is applied to any input type
# html forms for output
# output always serialized nicely
class ContainerFactory(ModelResource):
class BlogPostCreator(ModelResource):
"""A resource with which blog posts may be created."""
allowed_operations = ('create',)
model = ExampleContainer
fields = ('absolute_uri', 'name', 'key')
form_fields = ('name',)
model = BlogPost
fields = ('created', 'title', 'slug', 'content', 'absolute_url', 'comment_url', 'comments_url')
class ContainerInstance(ModelResource):
class BlogPostInstance(ModelResource):
"""A resource which represents a single blog post."""
allowed_operations = ('read', 'update', 'delete')
model = ExampleContainer
fields = ('absolute_uri', 'name', 'key')
form_fields = ('name',)
model = BlogPost
fields = ('created', 'title', 'slug', 'content', 'absolute_url', 'comment_url', 'comments_url')
# Comment Resources
class CommentList(QueryModelResource):
"""A resource which lists all existing comments for a given blog post."""
allowed_operations = ('read', )
model = Comment
class CommentCreator(ModelResource):
"""A resource with which blog comments may be created for a given blog post."""
allowed_operations = ('create',)
model = Comment
fields = ('username', 'comment', 'created', 'rating', 'absolute_url', 'blogpost_url')
class CommentInstance(ModelResource):
"""A resource which represents a single comment."""
allowed_operations = ('read', 'update', 'delete')
model = Comment
fields = ('username', 'comment', 'created', 'rating', 'absolute_url', 'blogpost_url')
#
#'read-only-api': self.reverse(ReadOnlyResource),
# 'write-only-api': self.reverse(WriteOnlyResource),
# 'read-write-api': self.reverse(ReadWriteResource),
# 'model-api': self.reverse(ModelFormResource),
# 'create-container': self.reverse(ContainerFactory),
#
#class ReadOnlyResource(Resource):
# """This is my docstring
# """
# allowed_operations = ('read',)
#
# def read(self, headers={}, *args, **kwargs):
# return (200, {'ExampleString': 'Example',
# 'ExampleInt': 1,
# 'ExampleDecimal': 1.0}, {})
#
#
#class WriteOnlyResource(Resource):
# """This is my docstring
# """
# allowed_operations = ('update',)
#
# def update(self, data, headers={}, *args, **kwargs):
# return (200, data, {})
#
#
#class ReadWriteResource(Resource):
# allowed_operations = ('read', 'update', 'delete')
# create_form = ExampleForm
# update_form = ExampleForm
#
#
#class ModelFormResource(ModelResource):
# allowed_operations = ('read', 'update', 'delete')
# model = ExampleModel
#
## Nice things: form validation is applied to any input type
## html forms for output
## output always serialized nicely
#class ContainerFactory(ModelResource):
# allowed_operations = ('create',)
# model = ExampleContainer
# fields = ('absolute_uri', 'name', 'key')
# form_fields = ('name',)
#
#
#class ContainerInstance(ModelResource):
# allowed_operations = ('read', 'update', 'delete')
# model = ExampleContainer
# fields = ('absolute_uri', 'name', 'key')
# form_fields = ('name',)
#######################
class BlogPostCreator(ModelResource):
"""A Resource with which blog posts may be created.
This is distinct from blog post instance so that it is discoverable by the client.
(ie the client doens't need to know how to form a blog post url in order to create a blog post)"""
allowed_operations = ('create',)
model = BlogPost
class BlogPostInstance(ModelResource):
"""Represents a single Blog Post."""
allowed_operations = ('read', 'update', 'delete')
model = BlogPost