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
f144b769
Commit
f144b769
authored
Jan 04, 2011
by
Tom Christie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Lots of good form validation and default actions
parent
48c7171a
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
176 additions
and
40 deletions
+176
-40
src/rest/emitters.py
+4
-4
src/rest/parsers.py
+39
-1
src/rest/resource.py
+0
-0
src/rest/templates/emitter.html
+5
-5
src/rest/templatetags/__init__.pyc
+0
-0
src/rest/templatetags/urlize_quoted_links.pyc
+0
-0
src/testapp/models.py
+30
-1
src/testapp/tests.py
+64
-23
src/testapp/urls.py
+3
-0
src/testapp/views.py
+31
-6
No files found.
src/rest/emitters.py
View file @
f144b769
...
@@ -4,11 +4,12 @@ import json
...
@@ -4,11 +4,12 @@ import json
from
utils
import
dict2xml
from
utils
import
dict2xml
class
BaseEmitter
(
object
):
class
BaseEmitter
(
object
):
def
__init__
(
self
,
resource
,
request
,
status
,
headers
):
def
__init__
(
self
,
resource
,
request
,
status
,
headers
,
form
):
self
.
request
=
request
self
.
request
=
request
self
.
resource
=
resource
self
.
resource
=
resource
self
.
status
=
status
self
.
status
=
status
self
.
headers
=
headers
self
.
headers
=
headers
self
.
form
=
form
def
emit
(
self
,
output
):
def
emit
(
self
,
output
):
return
output
return
output
...
@@ -26,9 +27,8 @@ class TemplatedEmitter(BaseEmitter):
...
@@ -26,9 +27,8 @@ class TemplatedEmitter(BaseEmitter):
'headers'
:
self
.
headers
,
'headers'
:
self
.
headers
,
'resource_name'
:
self
.
resource
.
__class__
.
__name__
,
'resource_name'
:
self
.
resource
.
__class__
.
__name__
,
'resource_doc'
:
self
.
resource
.
__doc__
,
'resource_doc'
:
self
.
resource
.
__doc__
,
'create_form'
:
self
.
resource
.
create_form
and
self
.
resource
.
create_form
()
or
None
,
'create_form'
:
self
.
form
,
'update_form'
:
self
.
resource
.
update_form
and
self
.
resource
.
update_form
()
or
None
,
'update_form'
:
self
.
form
,
'allowed_methods'
:
self
.
resource
.
allowed_methods
,
'request'
:
self
.
request
,
'request'
:
self
.
request
,
'resource'
:
self
.
resource
,
'resource'
:
self
.
resource
,
})
})
...
...
src/rest/parsers.py
View file @
f144b769
...
@@ -17,6 +17,44 @@ class XMLParser(BaseParser):
...
@@ -17,6 +17,44 @@ class XMLParser(BaseParser):
pass
pass
class
FormParser
(
BaseParser
):
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
):
if
request
.
method
==
'PUT'
:
# Fix from piston to force Django to give PUT requests the same
# form processing that POST requests get...
#
# Bug fix: if _load_post_and_files has already been called, for
# example by middleware accessing request.POST, the below code to
# pretend the request is a POST instead of a PUT will be too late
# to make a difference. Also calling _load_post_and_files will result
# in the following exception:
# AttributeError: You cannot set the upload handlers after the upload has been processed.
# The fix is to check for the presence of the _post field which is set
# 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'
):
del
request
.
_post
del
request
.
_files
try
:
request
.
method
=
"POST"
request
.
_load_post_and_files
()
request
.
method
=
"PUT"
except
AttributeError
:
request
.
META
[
'REQUEST_METHOD'
]
=
'POST'
request
.
_load_post_and_files
()
request
.
META
[
'REQUEST_METHOD'
]
=
'PUT'
#
self
.
data
=
{}
for
(
key
,
val
)
in
request
.
POST
.
items
():
if
key
not
in
resource
.
RESERVED_PARAMS
:
self
.
data
[
key
]
=
val
def
parse
(
self
,
input
):
def
parse
(
self
,
input
):
return
self
.
request
.
POST
return
self
.
data
src/rest/resource.py
View file @
f144b769
This diff is collapsed.
Click to expand it.
src/rest/templates/emitter.html
View file @
f144b769
...
@@ -12,17 +12,17 @@
...
@@ -12,17 +12,17 @@
<h1>
{{ resource_name }}
</h1>
<h1>
{{ resource_name }}
</h1>
<p>
{{ resource_doc }}
</p>
<p>
{{ resource_doc }}
</p>
<pre>
{% autoescape off %}
<b>
{{ status }} {{ reason }}
</b>
<pre>
{% autoescape off %}
<b>
{{ status }} {{ reason }}
</b>
{% for key, val in headers.items %}
<b>
{{ key }}:
</b>
{{ val }}
{% for key, val in headers.items %}
<b>
{{ key }}:
</b>
{{ val
|urlize_quoted_links
}}
{% endfor %}
{% endfor %}
{{ content|urlize_quoted_links }}{% endautoescape %}
</pre>
{{ content|urlize_quoted_links }}{% endautoescape %}
</pre>
{% if '
GET' in allowed_method
s %}
{% if '
read' in resource.allowed_operation
s %}
<div
class=
'action'
>
<div
class=
'action'
>
<a
href=
'{{ request.path }}'
>
Read
</a>
<a
href=
'{{ request.path }}'
>
Read
</a>
</div>
</div>
{% endif %}
{% endif %}
{% if '
POST' in resource.allowed_method
s %}
{% if '
create' in resource.allowed_operation
s %}
<div
class=
'action'
>
<div
class=
'action'
>
<form
action=
"{{ request.path }}"
method=
"POST"
>
<form
action=
"{{ request.path }}"
method=
"POST"
>
{% csrf_token %}
{% csrf_token %}
...
@@ -32,7 +32,7 @@
...
@@ -32,7 +32,7 @@
</div>
</div>
{% endif %}
{% endif %}
{% if '
PUT' in resource.allowed_method
s %}
{% if '
update' in resource.allowed_operation
s %}
<div
class=
'action'
>
<div
class=
'action'
>
<form
action=
"{{ request.path }}"
method=
"POST"
>
<form
action=
"{{ request.path }}"
method=
"POST"
>
<input
type=
"hidden"
name=
"{{ resource.METHOD_PARAM}}"
value=
"PUT"
/>
<input
type=
"hidden"
name=
"{{ resource.METHOD_PARAM}}"
value=
"PUT"
/>
...
@@ -43,7 +43,7 @@
...
@@ -43,7 +43,7 @@
</div>
</div>
{% endif %}
{% endif %}
{% if '
DELETE' in resource.allowed_method
s %}
{% if '
delete' in resource.allowed_operation
s %}
<div
class=
'action'
>
<div
class=
'action'
>
<form
action=
"{{ request.path }}"
method=
"POST"
>
<form
action=
"{{ request.path }}"
method=
"POST"
>
{% csrf_token %}
{% csrf_token %}
...
...
src/rest/templatetags/__init__.pyc
View file @
f144b769
No preview for this file type
src/rest/templatetags/urlize_quoted_links.pyc
View file @
f144b769
No preview for this file type
src/testapp/models.py
View file @
f144b769
from
django.db
import
models
from
django.db
import
models
import
uuid
# Create your models here.
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
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
])
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
)
\ No newline at end of file
src/testapp/tests.py
View file @
f144b769
...
@@ -9,7 +9,8 @@ from django.test import TestCase
...
@@ -9,7 +9,8 @@ from django.test import TestCase
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
testapp
import
views
from
testapp
import
views
import
json
import
json
from
rest.utils
import
xml2dict
,
dict2xml
#from rest.utils import xml2dict, dict2xml
class
AcceptHeaderTests
(
TestCase
):
class
AcceptHeaderTests
(
TestCase
):
def
assert_accept_mimetype
(
self
,
mimetype
,
expect
=
None
,
expect_match
=
True
):
def
assert_accept_mimetype
(
self
,
mimetype
,
expect
=
None
,
expect_match
=
True
):
...
@@ -46,6 +47,10 @@ class AcceptHeaderTests(TestCase):
...
@@ -46,6 +47,10 @@ class AcceptHeaderTests(TestCase):
resp
=
self
.
client
.
get
(
reverse
(
views
.
ReadOnlyResource
),
HTTP_ACCEPT
=
'invalid/invalid'
)
resp
=
self
.
client
.
get
(
reverse
(
views
.
ReadOnlyResource
),
HTTP_ACCEPT
=
'invalid/invalid'
)
self
.
assertEquals
(
resp
.
status_code
,
406
)
self
.
assertEquals
(
resp
.
status_code
,
406
)
def
test_prefer_specific
(
self
):
self
.
fail
(
"Test not implemented"
)
class
AllowedMethodsTests
(
TestCase
):
class
AllowedMethodsTests
(
TestCase
):
def
test_reading_read_only_allowed
(
self
):
def
test_reading_read_only_allowed
(
self
):
resp
=
self
.
client
.
get
(
reverse
(
views
.
ReadOnlyResource
))
resp
=
self
.
client
.
get
(
reverse
(
views
.
ReadOnlyResource
))
...
@@ -63,6 +68,7 @@ class AllowedMethodsTests(TestCase):
...
@@ -63,6 +68,7 @@ class AllowedMethodsTests(TestCase):
resp
=
self
.
client
.
put
(
reverse
(
views
.
WriteOnlyResource
),
{})
resp
=
self
.
client
.
put
(
reverse
(
views
.
WriteOnlyResource
),
{})
self
.
assertEquals
(
resp
.
status_code
,
200
)
self
.
assertEquals
(
resp
.
status_code
,
200
)
class
EncodeDecodeTests
(
TestCase
):
class
EncodeDecodeTests
(
TestCase
):
def
setUp
(
self
):
def
setUp
(
self
):
super
(
self
.
__class__
,
self
)
.
setUp
()
super
(
self
.
__class__
,
self
)
.
setUp
()
...
@@ -70,36 +76,71 @@ class EncodeDecodeTests(TestCase):
...
@@ -70,36 +76,71 @@ class EncodeDecodeTests(TestCase):
def
test_encode_form_decode_json
(
self
):
def
test_encode_form_decode_json
(
self
):
content
=
self
.
input
content
=
self
.
input
resp
=
self
.
client
.
put
(
reverse
(
views
.
WriteOnlyResource
),
content
,
HTTP_ACCEPT
=
'application/json'
)
resp
=
self
.
client
.
put
(
reverse
(
views
.
WriteOnlyResource
),
content
)
output
=
json
.
loads
(
resp
.
content
)
output
=
json
.
loads
(
resp
.
content
)
self
.
assertEquals
(
self
.
input
,
output
)
self
.
assertEquals
(
self
.
input
,
output
)
def
test_encode_json_decode_json
(
self
):
def
test_encode_json_decode_json
(
self
):
content
=
json
.
dumps
(
self
.
input
)
content
=
json
.
dumps
(
self
.
input
)
resp
=
self
.
client
.
put
(
reverse
(
views
.
WriteOnlyResource
),
content
,
'application/json'
,
HTTP_ACCEPT
=
'application/json'
)
resp
=
self
.
client
.
put
(
reverse
(
views
.
WriteOnlyResource
),
content
,
'application/json'
)
output
=
json
.
loads
(
resp
.
content
)
output
=
json
.
loads
(
resp
.
content
)
self
.
assertEquals
(
self
.
input
,
output
)
self
.
assertEquals
(
self
.
input
,
output
)
def
test_encode_xml_decode_json
(
self
):
#def test_encode_xml_decode_json(self):
content
=
dict2xml
(
self
.
input
)
# content = dict2xml(self.input)
resp
=
self
.
client
.
put
(
reverse
(
views
.
WriteOnlyResource
),
content
,
'application/json'
,
HTTP_ACCEPT
=
'application/json'
)
# 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
)
output
=
json
.
loads
(
resp
.
content
)
self
.
assertEquals
(
self
.
input
,
output
)
self
.
assertEquals
(
resp
.
status_code
,
201
)
self
.
assertEquals
(
output
[
'name'
],
'example'
)
self
.
assertEquals
(
set
(
output
.
keys
()),
set
((
'absolute_uri'
,
'name'
,
'key'
)))
def
test_encode_form_decode_xml
(
self
):
class
CreatedModelTests
(
TestCase
):
content
=
self
.
input
def
setUp
(
self
):
resp
=
self
.
client
.
put
(
reverse
(
views
.
WriteOnlyResource
),
content
,
HTTP_ACCEPT
=
'application/xml'
)
content
=
json
.
dumps
({
'name'
:
'example'
}
)
output
=
xml2dict
(
resp
.
content
)
resp
=
self
.
client
.
post
(
reverse
(
views
.
ContainerFactory
),
content
,
'application/json'
,
HTTP_ACCEPT
=
'application/json'
)
self
.
assertEquals
(
self
.
input
,
outpu
t
)
self
.
container
=
json
.
loads
(
resp
.
conten
t
)
def
test_encode_json_decode_xml
(
self
):
def
test_read_container
(
self
):
content
=
json
.
dumps
(
self
.
input
)
resp
=
self
.
client
.
get
(
self
.
container
[
"absolute_uri"
])
resp
=
self
.
client
.
put
(
reverse
(
views
.
WriteOnlyResource
),
content
,
'application/json'
,
HTTP_ACCEPT
=
'application/xml'
)
self
.
assertEquals
(
resp
.
status_code
,
200
)
output
=
xml2dict
(
resp
.
content
)
container
=
json
.
loads
(
resp
.
content
)
self
.
assertEquals
(
self
.
input
,
output
)
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
)
\ No newline at end of file
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
)
\ No newline at end of file
src/testapp/urls.py
View file @
f144b769
...
@@ -5,4 +5,7 @@ urlpatterns = patterns('testapp.views',
...
@@ -5,4 +5,7 @@ urlpatterns = patterns('testapp.views',
(
r'^read-only$'
,
'ReadOnlyResource'
),
(
r'^read-only$'
,
'ReadOnlyResource'
),
(
r'^write-only$'
,
'WriteOnlyResource'
),
(
r'^write-only$'
,
'WriteOnlyResource'
),
(
r'^read-write$'
,
'ReadWriteResource'
),
(
r'^read-write$'
,
'ReadWriteResource'
),
(
r'^model$'
,
'ModelFormResource'
),
(
r'^container$'
,
'ContainerFactory'
),
(
r'^container/((?P<key>[^/]+))$'
,
'ContainerInstance'
),
)
)
src/testapp/views.py
View file @
f144b769
from
rest.resource
import
Resource
from
rest.resource
import
Resource
,
ModelResource
from
testapp.forms
import
ExampleForm
from
testapp.forms
import
ExampleForm
from
testapp.models
import
ExampleModel
,
ExampleContainer
class
RootResource
(
Resource
):
class
RootResource
(
Resource
):
"""This is my docstring
"""This is my docstring
"""
"""
allowed_
methods
=
(
'GET
'
,)
allowed_
operations
=
(
'read
'
,)
def
read
(
self
,
headers
=
{},
*
args
,
**
kwargs
):
def
read
(
self
,
headers
=
{},
*
args
,
**
kwargs
):
return
(
200
,
{
'read-only-api'
:
self
.
reverse
(
ReadOnlyResource
),
return
(
200
,
{
'read-only-api'
:
self
.
reverse
(
ReadOnlyResource
),
'write-only-api'
:
self
.
reverse
(
WriteOnlyResource
),
'write-only-api'
:
self
.
reverse
(
WriteOnlyResource
),
'read-write-api'
:
self
.
reverse
(
ReadWriteResource
)},
{})
'read-write-api'
:
self
.
reverse
(
ReadWriteResource
),
'model-api'
:
self
.
reverse
(
ModelFormResource
),
'create-container'
:
self
.
reverse
(
ContainerFactory
)},
{})
class
ReadOnlyResource
(
Resource
):
class
ReadOnlyResource
(
Resource
):
"""This is my docstring
"""This is my docstring
"""
"""
allowed_
methods
=
(
'GET
'
,)
allowed_
operations
=
(
'read
'
,)
def
read
(
self
,
headers
=
{},
*
args
,
**
kwargs
):
def
read
(
self
,
headers
=
{},
*
args
,
**
kwargs
):
return
(
200
,
{
'ExampleString'
:
'Example'
,
return
(
200
,
{
'ExampleString'
:
'Example'
,
...
@@ -26,13 +29,35 @@ class ReadOnlyResource(Resource):
...
@@ -26,13 +29,35 @@ class ReadOnlyResource(Resource):
class
WriteOnlyResource
(
Resource
):
class
WriteOnlyResource
(
Resource
):
"""This is my docstring
"""This is my docstring
"""
"""
allowed_
methods
=
(
'PUT
'
,)
allowed_
operations
=
(
'update
'
,)
def
update
(
self
,
data
,
headers
=
{},
*
args
,
**
kwargs
):
def
update
(
self
,
data
,
headers
=
{},
*
args
,
**
kwargs
):
return
(
200
,
data
,
{})
return
(
200
,
data
,
{})
class
ReadWriteResource
(
Resource
):
class
ReadWriteResource
(
Resource
):
allowed_
methods
=
(
'GET'
,
'PUT'
,
'DELETE
'
)
allowed_
operations
=
(
'read'
,
'update'
,
'delete
'
)
create_form
=
ExampleForm
create_form
=
ExampleForm
update_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'
,)
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