aboutsummaryrefslogtreecommitdiffstats
path: root/flywheel
diff options
context:
space:
mode:
authortom christie tom@tomchristie.com2011-01-26 08:58:09 +0000
committertom christie tom@tomchristie.com2011-01-26 08:58:09 +0000
commit6807cf014cb0fde611f63c64bc352038206176cc (patch)
treeb5ebd4414852bf39efdf5380c57875c91e798ee2 /flywheel
parenteff54c00d514e1edd74fbc789f9064d09db40b02 (diff)
downloaddjango-rest-framework-6807cf014cb0fde611f63c64bc352038206176cc.tar.bz2
Added pygments_api example
Diffstat (limited to 'flywheel')
-rw-r--r--flywheel/emitters.py15
-rw-r--r--flywheel/parsers.py11
-rw-r--r--flywheel/resource.py49
-rw-r--r--flywheel/templates/emitter.html2
-rw-r--r--flywheel/templates/emitter.xhtml3
5 files changed, 53 insertions, 27 deletions
diff --git a/flywheel/emitters.py b/flywheel/emitters.py
index 7492b07b..33375200 100644
--- a/flywheel/emitters.py
+++ b/flywheel/emitters.py
@@ -17,8 +17,21 @@ class BaseEmitter(object):
self.resource = resource
def emit(self, output=NoContent, verbose=False):
- raise Exception('emit() function on a subclass of BaseEmitter must be implemented')
+ if output is NoContent:
+ return ''
+
+ return output
+
+class TemplateEmitter(BaseEmitter):
+ media_type = None
+ template = None
+ def emit(self, output=NoContent, verbose=False):
+ if output is NoContent:
+ return ''
+
+ return self.template.render(Context(output))
+
from django import forms
class JSONForm(forms.Form):
diff --git a/flywheel/parsers.py b/flywheel/parsers.py
index f0684612..57218cc6 100644
--- a/flywheel/parsers.py
+++ b/flywheel/parsers.py
@@ -8,13 +8,21 @@ except ImportError:
# TODO: Make all parsers only list a single media_type, rather than a list
class BaseParser(object):
+ """All parsers should extend BaseParser, specifing a media_type attribute,
+ and overriding the parse() method."""
+
media_types = ()
def __init__(self, resource):
+ """Initialise the parser with the Resource instance as state,
+ in case the parser needs to access any metadata on the Resource object."""
self.resource = resource
def parse(self, input):
- return {}
+ """Given some serialized input, return the deserialized output.
+ The input will be the raw request content body. The return value may be of
+ any type, but for many parsers/inputs it might typically be a dict."""
+ return input
class JSONParser(BaseParser):
@@ -26,6 +34,7 @@ class JSONParser(BaseParser):
except ValueError, exc:
raise ResponseException(status.HTTP_400_BAD_REQUEST, {'detail': 'JSON parse error - %s' % str(exc)})
+
class XMLParser(BaseParser):
media_types = ('application/xml',)
diff --git a/flywheel/resource.py b/flywheel/resource.py
index 8c20e14f..509b102c 100644
--- a/flywheel/resource.py
+++ b/flywheel/resource.py
@@ -9,7 +9,6 @@ from decimal import Decimal
import re
from itertools import chain
-# TODO: Authentication
# TODO: Display user login in top panel: http://stackoverflow.com/questions/806835/django-redirect-to-previous-page-after-login
# TODO: Figure how out references and named urls need to work nicely
# TODO: POST on existing 404 URL, PUT on existing 404 URL
@@ -73,6 +72,7 @@ class Resource(object):
"""Make the class callable so it can be used as a Django view."""
self = object.__new__(cls)
self.__init__(request)
+ # TODO: Remove this debugging code
try:
return self._handle_request(request, *args, **kwargs)
except:
@@ -120,22 +120,16 @@ class Resource(object):
(This emitter is used if the client does not send and Accept: header, or sends Accept: */*)"""
return self.emitters[0]
- # TODO:
-
+ #@property
#def parsed_media_types(self):
# """Return an list of all the media types that this resource can emit."""
# return [parser.media_type for parser in self.parsers]
-
+ #
+ #@property
#def default_parser(self):
# return self.parsers[0]
- def reverse(self, view, *args, **kwargs):
- """Return a fully qualified URI for a given view or resource.
- Add the domain using the Sites framework if possible, otherwise fallback to using the current request."""
- return self.add_domain(reverse(view, args=args, kwargs=kwargs))
-
-
def get(self, request, auth, *args, **kwargs):
"""Must be subclassed to be implemented."""
self.not_implemented('GET')
@@ -156,6 +150,12 @@ class Resource(object):
self.not_implemented('DELETE')
+ def reverse(self, view, *args, **kwargs):
+ """Return a fully qualified URI for a given view or resource.
+ Add the domain using the Sites framework if possible, otherwise fallback to using the current request."""
+ return self.add_domain(reverse(view, args=args, kwargs=kwargs))
+
+
def not_implemented(self, operation):
"""Return an HTTP 500 server error if an operation is called which has been allowed by
allowed_methods, but which has not been implemented."""
@@ -191,22 +191,21 @@ class Resource(object):
def authenticate(self, request):
- """Attempt to authenticate the request, returning an authentication context or None"""
+ """Attempt to authenticate the request, returning an authentication context or None.
+ An authentication context may be any object, although in many cases it will be a User instance."""
+
+ # Attempt authentication against each authenticator in turn,
+ # and return None if no authenticators succeed in authenticating the request.
for authenticator in self.authenticators:
auth_context = authenticator(self).authenticate(request)
if auth_context:
return auth_context
+
return None
def check_method_allowed(self, method, auth):
- """Ensure the request method is acceptable for this resource."""
-
- # If anonoymous check permissions and bail with no further info if disallowed
- if auth is None and not method in self.anon_allowed_methods:
- raise ResponseException(status.HTTP_403_FORBIDDEN,
- {'detail': 'You do not have permission to access this resource. ' +
- 'You may need to login or otherwise authenticate the request.'})
+ """Ensure the request method is permitted for this resource, raising a ResourceException if it is not."""
if not method in self.callmap.keys():
raise ResponseException(status.HTTP_501_NOT_IMPLEMENTED,
@@ -216,13 +215,17 @@ class Resource(object):
raise ResponseException(status.HTTP_405_METHOD_NOT_ALLOWED,
{'detail': 'Method \'%s\' not allowed on this resource.' % method})
+ if auth is None and not method in self.anon_allowed_methods:
+ raise ResponseException(status.HTTP_403_FORBIDDEN,
+ {'detail': 'You do not have permission to access this resource. ' +
+ 'You may need to login or otherwise authenticate the request.'})
def get_form(self, data=None):
"""Optionally return a Django Form instance, which may be used for validation
and/or rendered by an HTML/XHTML emitter.
- If data is not None the form will be bound to data. is_response indicates if data should be
- treated as the input data (bind to client input) or the response data (bind to an existing object)."""
+ If data is not None the form will be bound to data."""
+
if self.form:
if data:
return self.form(data)
@@ -238,6 +241,7 @@ class Resource(object):
Returns a tuple containing the cleaned up data, and optionally a form bound to that data.
By default this uses form validation to filter the basic input into the required types."""
+
if form_instance is None:
return data
@@ -253,7 +257,7 @@ class Resource(object):
else:
# Add standard field errors
- details = dict((key, map(unicode, val)) for (key, val) in form_instance.errors.iteritems())
+ details = dict((key, map(unicode, val)) for (key, val) in form_instance.errors.iteritems() if key != '__all__')
# Add any non-field errors
if form_instance.non_field_errors():
@@ -363,7 +367,8 @@ class Resource(object):
def _handle_request(self, request, *args, **kwargs):
- """
+ """This method is the core of Resource, through which all requests are passed.
+
Broadly this consists of the following procedure:
0. ensure the operation is permitted
diff --git a/flywheel/templates/emitter.html b/flywheel/templates/emitter.html
index fea5126a..93f4cb49 100644
--- a/flywheel/templates/emitter.html
+++ b/flywheel/templates/emitter.html
@@ -48,6 +48,7 @@
<div class='action'>
<form action="{{ request.path }}" method="post">
{% csrf_token %}
+ {{ form.non_field_errors }}
{% for field in form %}
<div>
{{ field.label_tag }}:
@@ -67,6 +68,7 @@
<form action="{{ request.path }}" method="post">
<input type="hidden" name="{{ resource.METHOD_PARAM }}" value="PUT" />
{% csrf_token %}
+ {{ form.non_field_errors }}
{% for field in form %}
<div>
{{ field.label_tag }}:
diff --git a/flywheel/templates/emitter.xhtml b/flywheel/templates/emitter.xhtml
deleted file mode 100644
index d9fb3ce9..00000000
--- a/flywheel/templates/emitter.xhtml
+++ /dev/null
@@ -1,3 +0,0 @@
-HTML:
-
-{{ content }} \ No newline at end of file