Permalink
Browse files

initial commit

  • Loading branch information...
0 parents commit 281eaf45f529706c26b1cfea77a76f80cf4bfcf0 @aehlke committed Mar 1, 2011
Showing with 673 additions and 0 deletions.
  1. +2 −0 .gitignore
  2. +30 −0 LICENSE.txt
  3. +32 −0 catnap/__init__.py
  4. +50 −0 catnap/middleware.py
  5. +46 −0 catnap/restresources.py
  6. +293 −0 catnap/restviews.py
  7. +188 −0 catnap/serializers.py
  8. +32 −0 setup.py
@@ -0,0 +1,2 @@
+*.swp
+*.pyc
@@ -0,0 +1,30 @@
+Copyright (c) 2011, Alex Ehlke
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Fez Consulting Limited nor the names
+ of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,32 @@
+'''
+
+catnap is a lightweight REST framework for Django.
+
+It's for developers who want to conform to the proper definition of REST,
+but still want to be somewhat lazy about it.
+
+It's not very opinionated or magical. It's mostly a collection of
+convenience classes and mixins that you can subclass for your views,
+and some middleware for HTTP verbs and content negotiation.
+
+It'll help you make an API which can be explored via hypertext.
+
+It won't help you document your API, though, because it doesn't make
+you define your resource models. You should document the API you want
+first, then make your views behave accordingly. This isn't far off from
+how you write Python code -- you document the behavior, and implement
+that behavior, without predefined static type checking making sure
+you implemented it right.
+
+If your REST resources correspond very closely to your Django ORM models
+in a CRUD-like, 1:1 fashion, you're probably better off with another
+library, like django-piston, since catnap doesn't help you generate
+views from Django models.
+
+catnap recognizes that your HTTP resources are not necessarily identical
+to your data models.
+
+
+Licensed under BSD: http://www.opensource.org/licenses/bsd-license.php
+
+'''
@@ -0,0 +1,50 @@
+# Some code adapted from the WebOb project: http://bitbucket.org/ianb/webob/
+
+from webob.acceptparse import accept_property, Accept, MIMEAccept, NilAccept, MIMENilAccept, NoAccept
+
+ALL_HTTP_METHODS = ['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE']
+
+class HttpAcceptMiddleware(object):
+ '''
+ Adds an `accept` property to `request`, which is a MIMEAccept instance,
+ from the WebOb library. This lets you easily check whether the given
+ `request.META['HTTP_ACCEPT']` header accepts various content types.
+ It's intelligent enough to understand the various forms allowed by
+ the HTTP RFC.
+
+ See: http://pythonpaste.org/webob/reference.html#accept-headers
+ for usage examples.
+ '''
+
+ def process_request(self, request):
+ '''
+ Add an `accept` property and set its value, which parses it
+ See HTTP RFC section 14.1
+ '''
+ accept_val = request.META.get('HTTP_ACCEPT', None)
+ if accept_val:
+ request.accept = MIMEAccept('Accept', accept_val)
+ else:
+ request.accept = MIMENilAccept('Accept')
+ return None
+
+
+class HttpMethodsFallbackMiddleware(object):
+ '''
+ Looks for `_method` param in POST requests, to change
+ `request.method` to its value, if it's valid.
+
+ Don't put this too early in the middleware chain, since
+ it could break middleware that depends on POST being POST.
+ '''
+ FALLBACK_PARAM = '_method'
+
+ def process_request(self, request):
+ if request.POST and request.POST.has_key(self.FALLBACK_PARAM):
+ method = request.POST[self.FALLBACK_PARAM]
+ if method in ALL_HTTP_METHODS:
+ request.method = request.POST[self.FALLBACK_PARAM]
+ else:
+ return HttpResponseNotAllowed(ALL_HTTP_METHODS)
+ return None
+
@@ -0,0 +1,46 @@
+
+
+
+class RestResource(object):
+ def get_data(self):
+ data = {}
+ if hasattr(self, 'get_url'):
+ data['url'] = self.get_url()
+ return data
+
+
+class RestModelResource(RestResource):
+ '''
+ Represents a single object, in a queryset.
+ '''
+ fields = None
+
+ def __init__(self, obj):
+ self.obj = obj
+
+ def _model_to_dict(self, obj):
+ fields = self.fields
+
+ ret = {}
+ # If we only have a model, we only want to encode the fields.
+ for f in obj._meta.fields:
+ if f.attname in self.fields:
+ #ret[f.attname] = _any(getattr(obj, f.attname))
+ ret[f.attname] = getattr(obj, f.attname)
+ # And additionally encode arbitrary properties
+ # that had been added.
+ fields = dir(obj.__class__) + ret.keys()
+ add_ons = [k for k in dir(obj) if k not in fields]
+ for k in add_ons:
+ if f.attname in self.fields:
+ #ret[k] = _any(getattr(obj, k))
+ ret[k] = getattr(obj, k)
+ return ret
+
+ def get_data(self):
+ data = super(RestModelResource, self).get_data()
+
+ data.update(self._model_to_dict(self.obj))
+
+ return data
+
Oops, something went wrong.

0 comments on commit 281eaf4

Please sign in to comment.