Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
D
django-rest-framework
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
django-rest-framework
Commits
5557dfb5
Commit
5557dfb5
authored
Jan 12, 2011
by
Tom Christie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Various cleanups
parent
42825e44
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
89 additions
and
78 deletions
+89
-78
src/rest/emitters.py
+23
-12
src/rest/parsers.py
+11
-12
src/rest/resource.py
+55
-54
No files found.
src/rest/emitters.py
View file @
5557dfb5
...
...
@@ -4,12 +4,8 @@ import json
from
utils
import
dict2xml
class
BaseEmitter
(
object
):
def
__init__
(
self
,
resource
,
request
,
status
,
headers
,
form
):
self
.
request
=
request
def
__init__
(
self
,
resource
):
self
.
resource
=
resource
self
.
status
=
status
self
.
headers
=
headers
self
.
form
=
form
def
emit
(
self
,
output
):
return
output
...
...
@@ -18,28 +14,43 @@ class TemplatedEmitter(BaseEmitter):
template
=
None
def
emit
(
self
,
output
):
if
output
is
None
:
content
=
''
else
:
content
=
json
.
dumps
(
output
,
indent
=
4
,
sort_keys
=
True
)
template
=
loader
.
get_template
(
self
.
template
)
context
=
RequestContext
(
self
.
request
,
{
context
=
RequestContext
(
self
.
re
source
.
re
quest
,
{
'content'
:
content
,
'status'
:
self
.
status
,
'reason'
:
STATUS_CODE_TEXT
.
get
(
self
.
status
,
''
),
'headers'
:
self
.
headers
,
'status'
:
self
.
resource
.
resp_
status
,
'reason'
:
STATUS_CODE_TEXT
.
get
(
self
.
resource
.
resp_
status
,
''
),
'headers'
:
self
.
resource
.
resp_
headers
,
'resource_name'
:
self
.
resource
.
__class__
.
__name__
,
'resource_doc'
:
self
.
resource
.
__doc__
,
'create_form'
:
self
.
form
,
'update_form'
:
self
.
form
,
'request'
:
self
.
request
,
'create_form'
:
self
.
resource
.
form
,
'update_form'
:
self
.
resource
.
form
,
'request'
:
self
.
re
source
.
re
quest
,
'resource'
:
self
.
resource
,
})
# Munge DELETE Response code to allow us to return content
if
self
.
resource
.
resp_status
==
204
:
self
.
resource
.
resp_status
=
200
return
template
.
render
(
context
)
class
JSONEmitter
(
BaseEmitter
):
def
emit
(
self
,
output
):
if
output
is
None
:
# Treat None as no message body, rather than serializing
return
''
return
json
.
dumps
(
output
)
class
XMLEmitter
(
BaseEmitter
):
def
emit
(
self
,
output
):
if
output
is
None
:
# Treat None as no message body, rather than serializing
return
''
return
dict2xml
(
output
)
class
HTMLEmitter
(
TemplatedEmitter
):
...
...
src/rest/parsers.py
View file @
5557dfb5
import
json
class
BaseParser
(
object
):
def
__init__
(
self
,
resource
,
request
):
def
__init__
(
self
,
resource
):
self
.
resource
=
resource
self
.
request
=
request
def
parse
(
self
,
input
):
return
{}
...
...
@@ -20,9 +19,9 @@ class FormParser(BaseParser):
"""The default parser for form data.
Return a dict containing a single value for each non-reserved parameter
"""
def
__init__
(
self
,
resource
,
request
):
def
__init__
(
self
,
resource
):
if
request
.
method
==
'PUT'
:
if
re
source
.
re
quest
.
method
==
'PUT'
:
# Fix from piston to force Django to give PUT requests the same
# form processing that POST requests get...
#
...
...
@@ -36,22 +35,22 @@ class FormParser(BaseParser):
# the first time _load_post_and_files is called (both by wsgi.py and
# modpython.py). If it's set, the request has to be 'reset' to redo
# the query value parsing in POST mode.
if
hasattr
(
request
,
'_post'
):
if
hasattr
(
re
source
.
re
quest
,
'_post'
):
del
request
.
_post
del
request
.
_files
try
:
request
.
method
=
"POST"
request
.
_load_post_and_files
()
request
.
method
=
"PUT"
re
source
.
re
quest
.
method
=
"POST"
re
source
.
re
quest
.
_load_post_and_files
()
re
source
.
re
quest
.
method
=
"PUT"
except
AttributeError
:
request
.
META
[
'REQUEST_METHOD'
]
=
'POST'
request
.
_load_post_and_files
()
request
.
META
[
'REQUEST_METHOD'
]
=
'PUT'
re
source
.
re
quest
.
META
[
'REQUEST_METHOD'
]
=
'POST'
re
source
.
re
quest
.
_load_post_and_files
()
re
source
.
re
quest
.
META
[
'REQUEST_METHOD'
]
=
'PUT'
#
self
.
data
=
{}
for
(
key
,
val
)
in
request
.
POST
.
items
():
for
(
key
,
val
)
in
re
source
.
re
quest
.
POST
.
items
():
if
key
not
in
resource
.
RESERVED_PARAMS
:
self
.
data
[
key
]
=
val
...
...
src/rest/resource.py
View file @
5557dfb5
...
...
@@ -57,7 +57,7 @@ class Resource(object):
"""Make the class callable so it can be used as a Django view."""
self
=
object
.
__new__
(
cls
)
self
.
__init__
()
self
.
_
request
=
request
self
.
request
=
request
try
:
return
self
.
_handle_request
(
request
,
*
args
,
**
kwargs
)
except
:
...
...
@@ -76,7 +76,7 @@ class Resource(object):
TODO: Add SITEMAP option.
Provided for convienience."""
return
self
.
_
request
.
build_absolute_uri
(
reverse
(
view
,
*
args
,
**
kwargs
))
return
self
.
request
.
build_absolute_uri
(
reverse
(
view
,
*
args
,
**
kwargs
))
def
make_absolute
(
self
,
uri
):
...
...
@@ -84,7 +84,7 @@ class Resource(object):
TODO: Add SITEMAP option.
Provided for convienience."""
return
self
.
_
request
.
build_absolute_uri
(
uri
)
return
self
.
request
.
build_absolute_uri
(
uri
)
def
read
(
self
,
headers
=
{},
*
args
,
**
kwargs
):
...
...
@@ -137,36 +137,41 @@ class Resource(object):
def
determine_form
(
self
,
input_data
=
None
,
return_data
=
Non
e
):
def
determine_form
(
self
,
data
=
None
,
is_response
=
Fals
e
):
"""Optionally return a Django Form instance, which may be used for validation
and/or rendered by an HTML/XHTML emitter.
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 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
self
.
form
:
if
input_data
:
return
self
.
form
(
input_data
)
elif
return_data
:
return
self
.
form
(
return_data
)
if
data
:
return
self
.
form
(
data
)
else
:
return
self
.
form
()
return
None
def
cleanup_request
(
self
,
data
,
form
=
None
):
def
cleanup_request
(
self
,
data
):
"""Perform any resource-specific data deserialization and/or validation
after the initial HTTP content-type deserialization has taken place.
Optionally this may use a Django Form which will have been bound to the data,
rather than using the data directly.
"""
By default this uses form validation to filter the basic input into the required types."""
if
self
.
form
is
None
:
return
data
if
not
self
.
form
.
is_valid
():
details
=
dict
((
key
,
map
(
unicode
,
val
))
for
(
key
,
val
)
in
self
.
form
.
errors
.
iteritems
())
raise
ResourceException
(
STATUS_400_BAD_REQUEST
,
{
'detail'
:
details
})
return
self
.
form
.
cleaned_data
def
cleanup_response
(
self
,
data
):
"""Perform any resource-specific data filtering prior to the standard HTTP
content-type serialization."""
content-type serialization.
Eg filter complex objects that cannot be serialized by json/xml/etc into basic objects that can."""
return
data
...
...
@@ -247,53 +252,57 @@ class Resource(object):
4. cleanup the response data
5. serialize response data into response content, using standard HTTP content negotiation
"""
method
=
self
.
determine_method
(
request
)
emitter
=
None
form
=
None
# We make these attributes to allow for a certain amount of munging,
# eg The HTML emitter needs to render this information
self
.
method
=
self
.
determine_method
(
request
)
self
.
form
=
None
self
.
resp_status
=
None
self
.
resp_content
=
None
self
.
resp_headers
=
{}
try
:
# Before we attempt anything else determine what format to emit our response data with.
mimetype
,
emitter
=
self
.
determine_emitter
(
request
)
# Ensure the requested operation is permitted on this resource
self
.
check_method_allowed
(
method
)
self
.
check_method_allowed
(
self
.
method
)
# Get the appropriate create/read/update/delete function
func
=
getattr
(
self
,
self
.
CALLMAP
.
get
(
method
,
''
))
func
=
getattr
(
self
,
self
.
CALLMAP
.
get
(
self
.
method
,
''
))
# Either generate the response data, deserializing and validating any request data
if
method
in
(
'PUT'
,
'POST'
):
if
self
.
method
in
(
'PUT'
,
'POST'
):
parser
=
self
.
determine_parser
(
request
)
data
=
parser
(
self
,
request
)
.
parse
(
request
.
raw_post_data
)
form
=
self
.
determine_form
(
input_data
=
data
)
data
=
self
.
cleanup_request
(
data
,
form
)
(
s
tatus
,
ret
,
headers
)
=
func
(
data
,
request
.
META
,
*
args
,
**
kwargs
)
data
=
parser
(
self
)
.
parse
(
request
.
raw_post_data
)
self
.
form
=
self
.
determine_form
(
data
)
data
=
self
.
cleanup_request
(
data
)
(
s
elf
.
resp_status
,
ret
,
self
.
resp_
headers
)
=
func
(
data
,
request
.
META
,
*
args
,
**
kwargs
)
else
:
(
s
tatus
,
ret
,
headers
)
=
func
(
request
.
META
,
*
args
,
**
kwargs
)
form
=
self
.
determine_form
(
return_data
=
ret
)
(
s
elf
.
resp_status
,
ret
,
self
.
resp_
headers
)
=
func
(
request
.
META
,
*
args
,
**
kwargs
)
self
.
form
=
self
.
determine_form
(
ret
,
is_response
=
True
)
except
ResourceException
,
exc
:
(
status
,
ret
,
headers
)
=
(
exc
.
status
,
exc
.
content
,
exc
.
headers
)
# Use a default emitter if request failed without being able to determine an acceptable emitter
(
self
.
resp_status
,
ret
,
self
.
resp_headers
)
=
(
exc
.
status
,
exc
.
content
,
exc
.
headers
)
if
emitter
is
None
:
mimetype
,
emitter
=
self
.
emitters
[
0
]
if
self
.
form
is
None
:
self
.
form
=
self
.
determine_form
()
# Create an unbound form if one has not yet been created
if
form
is
None
:
form
=
self
.
determine_form
()
# Always add the allow header
headers
[
'Allow'
]
=
', '
.
join
([
self
.
REVERSE_CALLMAP
[
operation
]
for
operation
in
self
.
allowed_operations
])
self
.
resp_
headers
[
'Allow'
]
=
', '
.
join
([
self
.
REVERSE_CALLMAP
[
operation
]
for
operation
in
self
.
allowed_operations
])
# Serialize the response content
ret
=
self
.
cleanup_response
(
ret
)
content
=
emitter
(
self
,
request
,
status
,
headers
,
form
)
.
emit
(
ret
)
content
=
emitter
(
self
)
.
emit
(
ret
)
# Build the HTTP Response
resp
=
HttpResponse
(
content
,
mimetype
=
mimetype
,
status
=
status
)
for
(
key
,
val
)
in
headers
.
items
():
resp
=
HttpResponse
(
content
,
mimetype
=
mimetype
,
status
=
s
elf
.
resp_s
tatus
)
for
(
key
,
val
)
in
self
.
resp_
headers
.
items
():
resp
[
key
]
=
val
return
resp
...
...
@@ -313,10 +322,10 @@ class ModelResource(Resource):
fields
=
None
form_fields
=
None
def
determine_form
(
self
,
input_data
=
None
,
return_data
=
Non
e
):
def
determine_form
(
self
,
data
=
None
,
is_response
=
Fals
e
):
"""Return a form that may be used in validation and/or rendering an html emitter"""
if
self
.
form
:
return
s
elf
.
form
return
s
uper
(
self
.
__class__
,
self
)
.
determine_form
(
data
,
is_response
=
is_response
)
elif
self
.
model
:
class
NewModelForm
(
ModelForm
):
...
...
@@ -324,26 +333,17 @@ class ModelResource(Resource):
model
=
self
.
model
fields
=
self
.
form_fields
if
self
.
form_fields
else
None
#self.fields
if
input_data
:
return
NewModelForm
(
input_
data
)
elif
return_data
:
return
NewModelForm
(
instance
=
return_
data
)
if
data
and
not
is_response
:
return
NewModelForm
(
data
)
elif
data
and
is_response
:
return
NewModelForm
(
instance
=
data
)
else
:
return
NewModelForm
()
else
:
return
None
def
cleanup_request
(
self
,
data
,
form
=
None
):
"""Filter data into form-cleaned data, performing validation and type coercion."""
if
form
is
None
:
return
data
if
not
form
.
is_valid
():
details
=
dict
((
key
,
map
(
unicode
,
val
))
for
(
key
,
val
)
in
form
.
errors
.
iteritems
())
raise
ResourceException
(
STATUS_400_BAD_REQUEST
,
{
'detail'
:
details
})
return
form
.
cleaned_data
def
cleanup_response
(
self
,
data
):
"""
...
...
@@ -614,7 +614,7 @@ class ModelResource(Resource):
try
:
instance
=
self
.
model
.
objects
.
get
(
**
kwargs
)
except
self
.
model
.
DoesNotExist
:
return
(
404
,
''
,
{})
return
(
404
,
None
,
{})
return
(
200
,
instance
,
{})
...
...
@@ -633,13 +633,14 @@ class ModelResource(Resource):
def
delete
(
self
,
headers
=
{},
*
args
,
**
kwargs
):
instance
=
self
.
model
.
objects
.
get
(
**
kwargs
)
instance
.
delete
()
return
(
204
,
''
,
{})
return
(
204
,
None
,
{})
class
QueryModelResource
(
ModelResource
):
allowed_methods
=
(
'read'
,)
def
determine_form
(
self
,
input_data
=
None
,
return_data
=
Non
e
):
def
determine_form
(
self
,
data
=
None
,
is_response
=
Fals
e
):
return
None
def
read
(
self
,
headers
=
{},
*
args
,
**
kwargs
):
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment