5
5
'''
6
6
from StringIO import StringIO
7
7
from collections import defaultdict
8
+ from xml .etree import ElementTree
9
+ import re
10
+ from django .conf import ConfigurationError
8
11
from django .core .serializers .json import DateTimeAwareJSONEncoder
9
12
from django .http import HttpResponseBadRequest , HttpResponse
10
13
from django .utils import simplejson
11
14
from django .utils .encoding import smart_unicode
12
15
from django .utils .xmlutils import SimplerXMLGenerator
13
16
from restful .utils import serialize
14
- from xml .etree import ElementTree
15
- import re
16
17
17
18
18
19
class BaseRequestDecoder (object ):
19
- content_type = None
20
+ """ The base for request decoder mixins. Subclasses can be mixed together
21
+ to provide support for multiple content types.
22
+ """
20
23
21
24
def decode_postdata (self , request , * args , ** kwargs ):
22
- raise NotImplementedError ('This method must be implemented in subclasses' )
25
+ """ Subclasses must implement this method, by checking the compatibility
26
+ of the request's CONTENT_TYPE with the subclass supported one. In case
27
+ of a difference the subclass must delegate to another component.
28
+ """
29
+ raise NotImplementedError ('The method "decode_postdata()" must be implemented in subclasses' )
23
30
24
31
def dispatch (self , request , * args , ** kwargs ):
25
- """ If the request is POST or PUT, load the postdata into self.data
26
- after converting to an object. """
32
+ """ Load the request data into a dictionary, using any decoder
33
+ implemented by subclasses.
34
+ """
27
35
self .data = None
36
+ self .request_content_type = request .META .get ('CONTENT_TYPE' , '' ).split (';' )[0 ].strip ()
28
37
if request .method .lower () in ('put' , 'post' ) and request .POST :
29
38
try :
30
39
self .data = self .decode_postdata (request )
31
40
request .POST = dict ()
32
41
except NotImplementedError :
33
- return HttpResponseBadRequest ('Cannot decode POST data of type %s.' % request .META .get ('CONTENT_TYPE' ))
34
- except Exception :
35
- return HttpResponseBadRequest ('Error parsing data of type %s.' % request .META .get ('CONTENT_TYPE' ))
42
+ return HttpResponseBadRequest ('Cannot decode POST data of type %s.' % self .request_content_type )
43
+ except Exception as e :
44
+ # Any other error is a parsing error
45
+ return HttpResponseBadRequest ('Error parsing data of type %s.' % self .request_content_type )
36
46
return super (BaseRequestDecoder , self ).dispatch (request , * args , ** kwargs )
37
47
38
48
39
49
class DefaultRequestDecoder (BaseRequestDecoder ):
40
- # By default, no content type will be accepted too
41
- content_type = 'application/x-www-form-urlencoded'
50
+ """ The default request decoder, if used, loads the POST data into
51
+ a dictionary when the Content-Type header is ot set or is set to
52
+ form-encoded.
53
+
54
+ The proper QueryDict object from django request is returned, so list-type
55
+ instances can be retrieved explicitly.
56
+ """
57
+ content_types = ['application/x-www-form-urlencoded' , '' ]
42
58
43
59
def decode_postdata (self , request , * args , ** kwargs ):
44
- if request . META . get ( 'CONTENT_TYPE' , 'application/x-www-form-urlencoded' ). startswith ( DefaultRequestDecoder .content_type ) :
60
+ if self . request_content_type in DefaultRequestDecoder .content_types :
45
61
return request .POST
46
62
else :
47
- return super (JSONRequestDecoder , self ).decode_postdata (request , * args , ** kwargs )
63
+ return super (DefaultRequestDecoder , self ).decode_postdata (request , * args , ** kwargs )
48
64
49
65
50
66
class JSONRequestDecoder (BaseRequestDecoder ):
51
- content_type = 'application/json'
67
+ """ The JSON request decoder. JSON specifies that there must be a root
68
+ object, which is converted to a dictionary.
69
+ """
70
+ content_types = ['application/json' ]
52
71
53
72
def decode_postdata (self , request , * args , ** kwargs ):
54
- if request . META . get ( 'CONTENT_TYPE' , '' ). startswith ( JSONRequestDecoder .content_type ) :
73
+ if self . request_content_type in JSONRequestDecoder .content_types :
55
74
return simplejson .load (request )
56
75
else :
57
76
return super (JSONRequestDecoder , self ).decode_postdata (request , * args , ** kwargs )
58
77
59
78
60
79
class XMLRequestDecoder (BaseRequestDecoder ):
61
- content_type = 'application/xml'
80
+ # FIXME: This should retrun a dict-like object, not a ElemetTree root.
81
+ content_types = 'application/xml'
62
82
63
83
def decode_postdata (self , request , * args , ** kwargs ):
64
- if request . META . get ( 'CONTENT_TYPE' , '' ). startswith ( XMLRequestDecoder .content_type ) :
84
+ if self . request_content_type in XMLRequestDecoder .content_types :
65
85
root = ElementTree .parse (request ).getroot ()
66
86
return root
67
87
else :
@@ -71,24 +91,48 @@ def decode_postdata(self, request, *args, **kwargs):
71
91
72
92
73
93
class BaseResponseEncoder (object ):
94
+
95
+ # The mimetype to send along with the response.
74
96
mimetype = None
97
+
98
+ # The 'format' class attribute acts as default serialization format
99
+ # for instances. If only basic mixins are used, the first mixin determines
100
+ # the default format. Anyway the subclass can set its own default.
75
101
format = None
76
- field_spec_marker = None
77
- field_spec = defaultdict ()
78
- fields = ()
102
+
103
+ # A special request parameter which allows the requested resource to be
104
+ # serialized into several different forms.
105
+ fieldset_marker = None
106
+
107
+ # The field resolver object. Subclasses can use the FieldSpec class or a
108
+ # simpler defaultdict for fallback behaviour. If the consumer can't
109
+ # specify a serialization form, set it to just a tuple.
110
+ fields = tuple () # for fixed serialization
111
+ # fields = defaultdict(tuple) # for polymorphic serialization
112
+
113
+ def get_fields (self ):
114
+ if self .fieldset_marker and isinstance (self .fields , dict ):
115
+ return self .fields [self .request .GET .get (self .fieldset_marker )]
116
+ elif isinstance (self .fields , tuple ):
117
+ return self .fields
118
+ else :
119
+ raise ConfigurationError ("The 'fields' attribute must be either "
120
+ "a dict (when 'fieldset_marker' is set) or a "
121
+ "tuple." )
79
122
80
123
def render (self , response ):
81
- raise NotImplementedError ('This method must be implemented in subclasses' )
82
-
83
- def get_default_format ( self ):
84
- return None
85
-
124
+ raise NotImplementedError ('%s extends BaseResponseEncoder but does not '
125
+ 'implement the render() method. Extend one of '
126
+ 'BaseResponseEncoder \' s subclasses, setting also '
127
+ 'a default format, or implement render() '
128
+ 'directly.' % self . __class__ . __name__ )
86
129
87
130
def dispatch (self , request , * args , ** kwargs ):
88
- self .format = re .sub (r'^\.+' , '' , kwargs .get ('format' ) or self .get_default_format ())
89
- print "format set to %s" % self .format
131
+ # if the format has ben specified in the request, overwrite the
132
+ # the default.
133
+ if 'format' in kwargs :
134
+ self .format = kwargs ['format' ].lstrip ('.' )
90
135
response = super (BaseResponseEncoder , self ).dispatch (request , * args , ** kwargs )
91
- print "got response: %s" % response
92
136
if isinstance (response , basestring ):
93
137
return HttpResponse (response )
94
138
elif isinstance (response , HttpResponse ):
@@ -98,29 +142,15 @@ def dispatch(self, request, *args, **kwargs):
98
142
return self .render (data )
99
143
100
144
101
- def get_fields (self ):
102
- if self .field_spec_marker :
103
- return self .field_spec [self .request .GET .get (self .field_spec_marker )]
104
- else :
105
- return self .fields
106
-
107
-
108
- def get_format (self ):
109
- return self .format
110
-
111
-
112
145
class JSONResponseEncoder (BaseResponseEncoder ):
113
146
mimetype = 'application/json'
114
147
format = 'json'
115
148
116
149
def render (self , response ):
117
- if self .format == JSONResponseEncoder .format :
118
- print "rendering %s in json" % `response`
119
- content = simplejson .dumps (response , cls = DateTimeAwareJSONEncoder , ensure_ascii = False , indent = 2 )
120
- return HttpResponse (content , mimetype = JSONResponseEncoder .mimetype )
121
- else :
150
+ if self .format != JSONResponseEncoder .format :
122
151
return super (JSONResponseEncoder , self ).render (response )
123
-
152
+ content = simplejson .dumps (response , cls = DateTimeAwareJSONEncoder , ensure_ascii = False , indent = 2 )
153
+ return HttpResponse (content , mimetype = JSONResponseEncoder .mimetype )
124
154
125
155
126
156
@@ -129,23 +159,17 @@ class XMLResponseEncoder(BaseResponseEncoder):
129
159
format = 'xml'
130
160
131
161
def render (self , response ):
132
- if self .format == XMLResponseEncoder .format :
133
- stream = StringIO ()
134
-
135
- xml = SimplerXMLGenerator (stream , "utf-8" )
136
- xml .startDocument ()
137
- xml .startElement ("response" , {})
138
-
139
- self ._to_xml (xml , response )
140
-
141
- xml .endElement ("response" )
142
- xml .endDocument ()
143
-
144
- content = stream .getvalue ()
145
-
146
- return HttpResponse (content , mimetype = XMLResponseEncoder .mimetype )
147
- else :
162
+ if self .format != XMLResponseEncoder .format :
148
163
return super (XMLResponseEncoder , self ).render (response )
164
+ stream = StringIO ()
165
+ xml = SimplerXMLGenerator (stream , "utf-8" )
166
+ xml .startDocument ()
167
+ xml .startElement ("response" , {})
168
+ self ._to_xml (xml , response )
169
+ xml .endElement ("response" )
170
+ xml .endDocument ()
171
+ content = stream .getvalue ()
172
+ return HttpResponse (content , mimetype = XMLResponseEncoder .mimetype )
149
173
150
174
151
175
def _to_xml (self , xml , data ):
0 commit comments