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
15f9e7c5
Commit
15f9e7c5
authored
May 12, 2011
by
Tom Christie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactoring resource specfic stuff into ResourceMixin - validators now defunct
parent
4d126796
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
122 additions
and
128 deletions
+122
-128
djangorestframework/authentication.py
+2
-2
djangorestframework/mixins.py
+64
-62
djangorestframework/parsers.py
+7
-12
djangorestframework/renderers.py
+7
-7
djangorestframework/resource.py
+0
-0
djangorestframework/tests/content.py
+15
-15
djangorestframework/tests/files.py
+5
-1
djangorestframework/tests/methods.py
+2
-2
djangorestframework/tests/parsers.py
+11
-7
djangorestframework/views.py
+7
-18
examples/sandbox/views.py
+2
-2
No files found.
djangorestframework/authentication.py
View file @
15f9e7c5
...
@@ -85,9 +85,9 @@ class UserLoggedInAuthenticaton(BaseAuthenticaton):
...
@@ -85,9 +85,9 @@ class UserLoggedInAuthenticaton(BaseAuthenticaton):
if
getattr
(
request
,
'user'
,
None
)
and
request
.
user
.
is_active
:
if
getattr
(
request
,
'user'
,
None
)
and
request
.
user
.
is_active
:
# If this is a POST request we enforce CSRF validation.
# If this is a POST request we enforce CSRF validation.
if
request
.
method
.
upper
()
==
'POST'
:
if
request
.
method
.
upper
()
==
'POST'
:
# Temporarily replace request.POST with .
RAW_CONTENT
,
# Temporarily replace request.POST with .
DATA
,
# so that we use our more generic request parsing
# so that we use our more generic request parsing
request
.
_post
=
self
.
view
.
RAW_CONTENT
request
.
_post
=
self
.
view
.
DATA
resp
=
CsrfViewMiddleware
()
.
process_view
(
request
,
None
,
(),
{})
resp
=
CsrfViewMiddleware
()
.
process_view
(
request
,
None
,
(),
{})
del
(
request
.
_post
)
del
(
request
.
_post
)
if
resp
is
not
None
:
# csrf failed
if
resp
is
not
None
:
# csrf failed
...
...
djangorestframework/mixins.py
View file @
15f9e7c5
""""""
"""
The mixins module provides a set of reusable mixin classes that can be added to a ``View``.
"""
from
django.contrib.auth.models
import
AnonymousUser
from
django.contrib.auth.models
import
AnonymousUser
from
django.db.models.query
import
QuerySet
from
django.db.models.query
import
QuerySet
...
@@ -18,9 +20,12 @@ from StringIO import StringIO
...
@@ -18,9 +20,12 @@ from StringIO import StringIO
__all__
=
(
__all__
=
(
# Base behavior mixins
'RequestMixin'
,
'RequestMixin'
,
'ResponseMixin'
,
'ResponseMixin'
,
'AuthMixin'
,
'AuthMixin'
,
'ResourceMixin'
,
# Model behavior mixins
'ReadModelMixin'
,
'ReadModelMixin'
,
'CreateModelMixin'
,
'CreateModelMixin'
,
'UpdateModelMixin'
,
'UpdateModelMixin'
,
...
@@ -36,13 +41,12 @@ class RequestMixin(object):
...
@@ -36,13 +41,12 @@ class RequestMixin(object):
Mixin class to provide request parsing behavior.
Mixin class to provide request parsing behavior.
"""
"""
USE_FORM_OVERLOADING
=
True
_
USE_FORM_OVERLOADING
=
True
METHOD_PARAM
=
"_method"
_METHOD_PARAM
=
'_method'
CONTENTTYPE_PARAM
=
"_content_type"
_CONTENTTYPE_PARAM
=
'_content_type'
CONTENT_PARAM
=
"_content"
_CONTENT_PARAM
=
'_content'
parsers
=
()
parsers
=
()
validators
=
()
def
_get_method
(
self
):
def
_get_method
(
self
):
"""
"""
...
@@ -137,62 +141,58 @@ class RequestMixin(object):
...
@@ -137,62 +141,58 @@ class RequestMixin(object):
self
.
_stream
=
stream
self
.
_stream
=
stream
def
_get_raw_content
(
self
):
def
_load_data_and_files
(
self
):
"""
(
self
.
_data
,
self
.
_files
)
=
self
.
_parse
(
self
.
stream
,
self
.
content_type
)
Returns the parsed content of the request
"""
if
not
hasattr
(
self
,
'_raw_content'
):
self
.
_raw_content
=
self
.
parse
(
self
.
stream
,
self
.
content_type
)
return
self
.
_raw_content
def
_get_data
(
self
):
if
not
hasattr
(
self
,
'_data'
):
self
.
_load_data_and_files
()
return
self
.
_data
def
_get_content
(
self
):
def
_get_files
(
self
):
"""
if
not
hasattr
(
self
,
'_files'
):
Returns the parsed and validated content of the request
self
.
_load_data_and_files
()
"""
return
self
.
_files
if
not
hasattr
(
self
,
'_content'
):
self
.
_content
=
self
.
validate
(
self
.
RAW_CONTENT
)
return
self
.
_content
# TODO: Modify this so that it happens implictly, rather than being called explicitly
# TODO: Modify this so that it happens implictly, rather than being called explicitly
# ie accessing any of .DATA, .FILES, .content_type, .
stream or .
method will force
# ie accessing any of .DATA, .FILES, .content_type, .method will force
# form overloading.
# form overloading.
def
perform_form_overloading
(
self
):
def
_
perform_form_overloading
(
self
):
"""
"""
Check the request to see if it is using form POST '_method'/'_content'/'_content_type' overrides.
Check the request to see if it is using form POST '_method'/'_content'/'_content_type' overrides.
If it is then alter self.method, self.content_type, self.CONTENT to reflect that rather than simply
If it is then alter self.method, self.content_type, self.CONTENT to reflect that rather than simply
delegating them to the original request.
delegating them to the original request.
"""
"""
if
not
self
.
USE_FORM_OVERLOADING
or
self
.
method
!=
'POST'
or
not
is_form_media_type
(
self
.
content_type
):
if
not
self
.
_
USE_FORM_OVERLOADING
or
self
.
method
!=
'POST'
or
not
is_form_media_type
(
self
.
content_type
):
return
return
# Temporarily switch to using the form parsers, then parse the content
# Temporarily switch to using the form parsers, then parse the content
parsers
=
self
.
parsers
parsers
=
self
.
parsers
self
.
parsers
=
(
FormParser
,
MultiPartParser
)
self
.
parsers
=
(
FormParser
,
MultiPartParser
)
content
=
self
.
RAW_CONTENT
content
=
self
.
DATA
self
.
parsers
=
parsers
self
.
parsers
=
parsers
# Method overloading - change the method and remove the param from the content
# Method overloading - change the method and remove the param from the content
if
self
.
METHOD_PARAM
in
content
:
if
self
.
_
METHOD_PARAM
in
content
:
self
.
method
=
content
[
self
.
METHOD_PARAM
]
.
upper
()
self
.
method
=
content
[
self
.
_
METHOD_PARAM
]
.
upper
()
del
self
.
_
raw_content
[
self
.
METHOD_PARAM
]
del
self
.
_
data
[
self
.
_
METHOD_PARAM
]
# Content overloading - rewind the stream and modify the content type
# Content overloading - rewind the stream and modify the content type
if
self
.
CONTENT_PARAM
in
content
and
self
.
CONTENTTYPE_PARAM
in
content
:
if
self
.
_CONTENT_PARAM
in
content
and
self
.
_
CONTENTTYPE_PARAM
in
content
:
self
.
_content_type
=
content
[
self
.
CONTENTTYPE_PARAM
]
self
.
_content_type
=
content
[
self
.
_
CONTENTTYPE_PARAM
]
self
.
_stream
=
StringIO
(
content
[
self
.
CONTENT_PARAM
])
self
.
_stream
=
StringIO
(
content
[
self
.
_
CONTENT_PARAM
])
del
(
self
.
_
raw_content
)
del
(
self
.
_
data
)
def
parse
(
self
,
stream
,
content_type
):
def
_
parse
(
self
,
stream
,
content_type
):
"""
"""
Parse the request content.
Parse the request content.
May raise a 415 ErrorResponse (Unsupported Media Type), or a 400 ErrorResponse (Bad Request).
May raise a 415 ErrorResponse (Unsupported Media Type), or a 400 ErrorResponse (Bad Request).
"""
"""
if
stream
is
None
or
content_type
is
None
:
if
stream
is
None
or
content_type
is
None
:
return
None
return
(
None
,
None
)
parsers
=
as_tuple
(
self
.
parsers
)
parsers
=
as_tuple
(
self
.
parsers
)
...
@@ -206,48 +206,28 @@ class RequestMixin(object):
...
@@ -206,48 +206,28 @@ class RequestMixin(object):
content_type
})
content_type
})
# TODO: Acutally this needs to go into Resource
def
validate
(
self
,
content
):
"""
Validate, cleanup, and type-ify the request content.
"""
for
validator_cls
in
self
.
validators
:
validator
=
validator_cls
(
self
)
content
=
validator
.
validate
(
content
)
return
content
# TODO: Acutally this needs to go into Resource
def
get_bound_form
(
self
,
content
=
None
):
"""
Return a bound form instance for the given content,
if there is an appropriate form validator attached to the view.
"""
for
validator_cls
in
self
.
validators
:
if
hasattr
(
validator_cls
,
'get_bound_form'
):
validator
=
validator_cls
(
self
)
return
validator
.
get_bound_form
(
content
)
return
None
@property
@property
def
parsed_media_types
(
self
):
def
parsed_media_types
(
self
):
"""Return an list of all the media types that this view can parse."""
"""
Return an list of all the media types that this view can parse.
"""
return
[
parser
.
media_type
for
parser
in
self
.
parsers
]
return
[
parser
.
media_type
for
parser
in
self
.
parsers
]
@property
@property
def
default_parser
(
self
):
def
default_parser
(
self
):
"""Return the view's most preferred parser.
"""
(This has no behavioral effect, but is may be used by documenting renderers)"""
Return the view's most preferred parser.
(This has no behavioral effect, but is may be used by documenting renderers)
"""
return
self
.
parsers
[
0
]
return
self
.
parsers
[
0
]
method
=
property
(
_get_method
,
_set_method
)
method
=
property
(
_get_method
,
_set_method
)
content_type
=
property
(
_get_content_type
,
_set_content_type
)
content_type
=
property
(
_get_content_type
,
_set_content_type
)
stream
=
property
(
_get_stream
,
_set_stream
)
stream
=
property
(
_get_stream
,
_set_stream
)
RAW_CONTENT
=
property
(
_get_raw_content
)
DATA
=
property
(
_get_data
)
CONTENT
=
property
(
_get_content
)
FILES
=
property
(
_get_files
)
########## ResponseMixin ##########
########## ResponseMixin ##########
...
@@ -422,6 +402,28 @@ class AuthMixin(object):
...
@@ -422,6 +402,28 @@ class AuthMixin(object):
permission
.
check_permission
(
user
)
permission
.
check_permission
(
user
)
########## Resource Mixin ##########
class
ResourceMixin
(
object
):
@property
def
CONTENT
(
self
):
if
not
hasattr
(
self
,
'_content'
):
self
.
_content
=
self
.
_get_content
(
self
.
DATA
,
self
.
FILES
)
return
self
.
_content
def
_get_content
(
self
,
data
,
files
):
resource
=
self
.
resource
(
self
)
return
resource
.
validate
(
data
,
files
)
def
get_bound_form
(
self
,
content
=
None
):
resource
=
self
.
resource
(
self
)
return
resource
.
get_bound_form
(
content
)
def
object_to_data
(
self
,
obj
):
resource
=
self
.
resource
(
self
)
return
resource
.
object_to_data
(
obj
)
########## Model Mixins ##########
########## Model Mixins ##########
class
ReadModelMixin
(
object
):
class
ReadModelMixin
(
object
):
...
...
djangorestframework/parsers.py
View file @
15f9e7c5
...
@@ -41,7 +41,7 @@ class BaseParser(object):
...
@@ -41,7 +41,7 @@ class BaseParser(object):
"""
"""
self
.
view
=
view
self
.
view
=
view
def
can_handle_request
(
self
,
media
_type
):
def
can_handle_request
(
self
,
content
_type
):
"""
"""
Returns `True` if this parser is able to deal with the given media type.
Returns `True` if this parser is able to deal with the given media type.
...
@@ -52,12 +52,12 @@ class BaseParser(object):
...
@@ -52,12 +52,12 @@ class BaseParser(object):
This may be overridden to provide for other behavior, but typically you'll
This may be overridden to provide for other behavior, but typically you'll
instead want to just set the ``media_type`` attribute on the class.
instead want to just set the ``media_type`` attribute on the class.
"""
"""
return
media_type_matches
(
media
_type
,
self
.
media_type
)
return
media_type_matches
(
content
_type
,
self
.
media_type
)
def
parse
(
self
,
stream
):
def
parse
(
self
,
stream
):
"""
"""
Given a stream to read from, return the deserialized output.
Given a stream to read from, return the deserialized output.
The return value may be of any type, but for many parsers it might typically be a dict-like object
.
Should return a 2-tuple of (data, files)
.
"""
"""
raise
NotImplementedError
(
"BaseParser.parse() Must be overridden to be implemented."
)
raise
NotImplementedError
(
"BaseParser.parse() Must be overridden to be implemented."
)
...
@@ -67,7 +67,7 @@ class JSONParser(BaseParser):
...
@@ -67,7 +67,7 @@ class JSONParser(BaseParser):
def
parse
(
self
,
stream
):
def
parse
(
self
,
stream
):
try
:
try
:
return
json
.
load
(
stream
)
return
(
json
.
load
(
stream
),
None
)
except
ValueError
,
exc
:
except
ValueError
,
exc
:
raise
ErrorResponse
(
status
.
HTTP_400_BAD_REQUEST
,
raise
ErrorResponse
(
status
.
HTTP_400_BAD_REQUEST
,
{
'detail'
:
'JSON parse error -
%
s'
%
unicode
(
exc
)})
{
'detail'
:
'JSON parse error -
%
s'
%
unicode
(
exc
)})
...
@@ -107,7 +107,7 @@ class PlainTextParser(BaseParser):
...
@@ -107,7 +107,7 @@ class PlainTextParser(BaseParser):
media_type
=
'text/plain'
media_type
=
'text/plain'
def
parse
(
self
,
stream
):
def
parse
(
self
,
stream
):
return
stream
.
read
(
)
return
(
stream
.
read
(),
None
)
class
FormParser
(
BaseParser
,
DataFlatener
):
class
FormParser
(
BaseParser
,
DataFlatener
):
...
@@ -139,7 +139,7 @@ class FormParser(BaseParser, DataFlatener):
...
@@ -139,7 +139,7 @@ class FormParser(BaseParser, DataFlatener):
if
key
in
self
.
RESERVED_FORM_PARAMS
:
if
key
in
self
.
RESERVED_FORM_PARAMS
:
data
.
pop
(
key
)
data
.
pop
(
key
)
return
data
return
(
data
,
None
)
def
remove_empty_val
(
self
,
val_list
):
def
remove_empty_val
(
self
,
val_list
):
""" """
""" """
...
@@ -152,11 +152,6 @@ class FormParser(BaseParser, DataFlatener):
...
@@ -152,11 +152,6 @@ class FormParser(BaseParser, DataFlatener):
val_list
.
pop
(
ind
)
val_list
.
pop
(
ind
)
class
MultipartData
(
dict
):
def
__init__
(
self
,
data
,
files
):
dict
.
__init__
(
self
,
data
)
self
.
FILES
=
files
class
MultiPartParser
(
BaseParser
,
DataFlatener
):
class
MultiPartParser
(
BaseParser
,
DataFlatener
):
media_type
=
'multipart/form-data'
media_type
=
'multipart/form-data'
RESERVED_FORM_PARAMS
=
(
'csrfmiddlewaretoken'
,)
RESERVED_FORM_PARAMS
=
(
'csrfmiddlewaretoken'
,)
...
@@ -175,4 +170,4 @@ class MultiPartParser(BaseParser, DataFlatener):
...
@@ -175,4 +170,4 @@ class MultiPartParser(BaseParser, DataFlatener):
if
key
in
self
.
RESERVED_FORM_PARAMS
:
if
key
in
self
.
RESERVED_FORM_PARAMS
:
data
.
pop
(
key
)
data
.
pop
(
key
)
return
MultipartData
(
data
,
files
)
return
(
data
,
files
)
djangorestframework/renderers.py
View file @
15f9e7c5
...
@@ -150,7 +150,7 @@ class DocumentingTemplateRenderer(BaseRenderer):
...
@@ -150,7 +150,7 @@ class DocumentingTemplateRenderer(BaseRenderer):
# If we're not using content overloading there's no point in supplying a generic form,
# If we're not using content overloading there's no point in supplying a generic form,
# as the view won't treat the form's value as the content of the request.
# as the view won't treat the form's value as the content of the request.
if
not
getattr
(
view
,
'USE_FORM_OVERLOADING'
,
False
):
if
not
getattr
(
view
,
'
_
USE_FORM_OVERLOADING'
,
False
):
return
None
return
None
# NB. http://jacobian.org/writing/dynamic-form-generation/
# NB. http://jacobian.org/writing/dynamic-form-generation/
...
@@ -164,14 +164,14 @@ class DocumentingTemplateRenderer(BaseRenderer):
...
@@ -164,14 +164,14 @@ class DocumentingTemplateRenderer(BaseRenderer):
contenttype_choices
=
[(
media_type
,
media_type
)
for
media_type
in
view
.
parsed_media_types
]
contenttype_choices
=
[(
media_type
,
media_type
)
for
media_type
in
view
.
parsed_media_types
]
initial_contenttype
=
view
.
default_parser
.
media_type
initial_contenttype
=
view
.
default_parser
.
media_type
self
.
fields
[
view
.
CONTENTTYPE_PARAM
]
=
forms
.
ChoiceField
(
label
=
'Content Type'
,
self
.
fields
[
view
.
_
CONTENTTYPE_PARAM
]
=
forms
.
ChoiceField
(
label
=
'Content Type'
,
choices
=
contenttype_choices
,
choices
=
contenttype_choices
,
initial
=
initial_contenttype
)
initial
=
initial_contenttype
)
self
.
fields
[
view
.
CONTENT_PARAM
]
=
forms
.
CharField
(
label
=
'Content'
,
self
.
fields
[
view
.
_
CONTENT_PARAM
]
=
forms
.
CharField
(
label
=
'Content'
,
widget
=
forms
.
Textarea
)
widget
=
forms
.
Textarea
)
# If either of these reserved parameters are turned off then content tunneling is not possible
# If either of these reserved parameters are turned off then content tunneling is not possible
if
self
.
view
.
CONTENTTYPE_PARAM
is
None
or
self
.
view
.
CONTENT_PARAM
is
None
:
if
self
.
view
.
_CONTENTTYPE_PARAM
is
None
or
self
.
view
.
_
CONTENT_PARAM
is
None
:
return
None
return
None
# Okey doke, let's do it
# Okey doke, let's do it
...
...
djangorestframework/resource.py
View file @
15f9e7c5
This diff is collapsed.
Click to expand it.
djangorestframework/tests/content.py
View file @
15f9e7c5
...
@@ -14,14 +14,14 @@ class TestContentParsing(TestCase):
...
@@ -14,14 +14,14 @@ class TestContentParsing(TestCase):
def
ensure_determines_no_content_GET
(
self
,
view
):
def
ensure_determines_no_content_GET
(
self
,
view
):
"""Ensure view.RAW_CONTENT returns None for GET request with no content."""
"""Ensure view.RAW_CONTENT returns None for GET request with no content."""
view
.
request
=
self
.
req
.
get
(
'/'
)
view
.
request
=
self
.
req
.
get
(
'/'
)
self
.
assertEqual
(
view
.
RAW_CONTENT
,
None
)
self
.
assertEqual
(
view
.
DATA
,
None
)
def
ensure_determines_form_content_POST
(
self
,
view
):
def
ensure_determines_form_content_POST
(
self
,
view
):
"""Ensure view.RAW_CONTENT returns content for POST request with form content."""
"""Ensure view.RAW_CONTENT returns content for POST request with form content."""
form_data
=
{
'qwerty'
:
'uiop'
}
form_data
=
{
'qwerty'
:
'uiop'
}
view
.
parsers
=
(
FormParser
,
MultiPartParser
)
view
.
parsers
=
(
FormParser
,
MultiPartParser
)
view
.
request
=
self
.
req
.
post
(
'/'
,
data
=
form_data
)
view
.
request
=
self
.
req
.
post
(
'/'
,
data
=
form_data
)
self
.
assertEqual
(
view
.
RAW_CONTENT
,
form_data
)
self
.
assertEqual
(
view
.
DATA
,
form_data
)
def
ensure_determines_non_form_content_POST
(
self
,
view
):
def
ensure_determines_non_form_content_POST
(
self
,
view
):
"""Ensure view.RAW_CONTENT returns content for POST request with non-form content."""
"""Ensure view.RAW_CONTENT returns content for POST request with non-form content."""
...
@@ -29,14 +29,14 @@ class TestContentParsing(TestCase):
...
@@ -29,14 +29,14 @@ class TestContentParsing(TestCase):
content_type
=
'text/plain'
content_type
=
'text/plain'
view
.
parsers
=
(
PlainTextParser
,)
view
.
parsers
=
(
PlainTextParser
,)
view
.
request
=
self
.
req
.
post
(
'/'
,
content
,
content_type
=
content_type
)
view
.
request
=
self
.
req
.
post
(
'/'
,
content
,
content_type
=
content_type
)
self
.
assertEqual
(
view
.
RAW_CONTENT
,
content
)
self
.
assertEqual
(
view
.
DATA
,
content
)
def
ensure_determines_form_content_PUT
(
self
,
view
):
def
ensure_determines_form_content_PUT
(
self
,
view
):
"""Ensure view.RAW_CONTENT returns content for PUT request with form content."""
"""Ensure view.RAW_CONTENT returns content for PUT request with form content."""
form_data
=
{
'qwerty'
:
'uiop'
}
form_data
=
{
'qwerty'
:
'uiop'
}
view
.
parsers
=
(
FormParser
,
MultiPartParser
)
view
.
parsers
=
(
FormParser
,
MultiPartParser
)
view
.
request
=
self
.
req
.
put
(
'/'
,
data
=
form_data
)
view
.
request
=
self
.
req
.
put
(
'/'
,
data
=
form_data
)
self
.
assertEqual
(
view
.
RAW_CONTENT
,
form_data
)
self
.
assertEqual
(
view
.
DATA
,
form_data
)
def
ensure_determines_non_form_content_PUT
(
self
,
view
):
def
ensure_determines_non_form_content_PUT
(
self
,
view
):
"""Ensure view.RAW_CONTENT returns content for PUT request with non-form content."""
"""Ensure view.RAW_CONTENT returns content for PUT request with non-form content."""
...
@@ -44,36 +44,36 @@ class TestContentParsing(TestCase):
...
@@ -44,36 +44,36 @@ class TestContentParsing(TestCase):
content_type
=
'text/plain'
content_type
=
'text/plain'
view
.
parsers
=
(
PlainTextParser
,)
view
.
parsers
=
(
PlainTextParser
,)
view
.
request
=
self
.
req
.
post
(
'/'
,
content
,
content_type
=
content_type
)
view
.
request
=
self
.
req
.
post
(
'/'
,
content
,
content_type
=
content_type
)
self
.
assertEqual
(
view
.
RAW_CONTENT
,
content
)
self
.
assertEqual
(
view
.
DATA
,
content
)
def
test_standard_behaviour_determines_no_content_GET
(
self
):
def
test_standard_behaviour_determines_no_content_GET
(
self
):
"""Ensure view.
RAW_CONTENT
returns None for GET request with no content."""
"""Ensure view.
DATA
returns None for GET request with no content."""
self
.
ensure_determines_no_content_GET
(
RequestMixin
())
self
.
ensure_determines_no_content_GET
(
RequestMixin
())
def
test_standard_behaviour_determines_form_content_POST
(
self
):
def
test_standard_behaviour_determines_form_content_POST
(
self
):
"""Ensure view.
RAW_CONTENT
returns content for POST request with form content."""
"""Ensure view.
DATA
returns content for POST request with form content."""
self
.
ensure_determines_form_content_POST
(
RequestMixin
())
self
.
ensure_determines_form_content_POST
(
RequestMixin
())
def
test_standard_behaviour_determines_non_form_content_POST
(
self
):
def
test_standard_behaviour_determines_non_form_content_POST
(
self
):
"""Ensure view.
RAW_CONTENT
returns content for POST request with non-form content."""
"""Ensure view.
DATA
returns content for POST request with non-form content."""
self
.
ensure_determines_non_form_content_POST
(
RequestMixin
())
self
.
ensure_determines_non_form_content_POST
(
RequestMixin
())
def
test_standard_behaviour_determines_form_content_PUT
(
self
):
def
test_standard_behaviour_determines_form_content_PUT
(
self
):
"""Ensure view.
RAW_CONTENT
returns content for PUT request with form content."""
"""Ensure view.
DATA
returns content for PUT request with form content."""
self
.
ensure_determines_form_content_PUT
(
RequestMixin
())
self
.
ensure_determines_form_content_PUT
(
RequestMixin
())
def
test_standard_behaviour_determines_non_form_content_PUT
(
self
):
def
test_standard_behaviour_determines_non_form_content_PUT
(
self
):
"""Ensure view.
RAW_CONTENT
returns content for PUT request with non-form content."""
"""Ensure view.
DATA
returns content for PUT request with non-form content."""
self
.
ensure_determines_non_form_content_PUT
(
RequestMixin
())
self
.
ensure_determines_non_form_content_PUT
(
RequestMixin
())
def
test_overloaded_behaviour_allows_content_tunnelling
(
self
):
def
test_overloaded_behaviour_allows_content_tunnelling
(
self
):
"""Ensure request.
RAW_CONTENT
returns content for overloaded POST request"""
"""Ensure request.
DATA
returns content for overloaded POST request"""
content
=
'qwerty'
content
=
'qwerty'
content_type
=
'text/plain'
content_type
=
'text/plain'
view
=
RequestMixin
()
view
=
RequestMixin
()
form_data
=
{
view
.
CONTENT_PARAM
:
content
,
form_data
=
{
view
.
_
CONTENT_PARAM
:
content
,
view
.
CONTENTTYPE_PARAM
:
content_type
}
view
.
_
CONTENTTYPE_PARAM
:
content_type
}
view
.
request
=
self
.
req
.
post
(
'/'
,
form_data
)
view
.
request
=
self
.
req
.
post
(
'/'
,
form_data
)
view
.
parsers
=
(
PlainTextParser
,)
view
.
parsers
=
(
PlainTextParser
,)
view
.
perform_form_overloading
()
view
.
_
perform_form_overloading
()
self
.
assertEqual
(
view
.
RAW_CONTENT
,
content
)
self
.
assertEqual
(
view
.
DATA
,
content
)
djangorestframework/tests/files.py
View file @
15f9e7c5
...
@@ -2,6 +2,7 @@ from django.test import TestCase
...
@@ -2,6 +2,7 @@ from django.test import TestCase
from
django
import
forms
from
django
import
forms
from
djangorestframework.compat
import
RequestFactory
from
djangorestframework.compat
import
RequestFactory
from
djangorestframework.views
import
BaseView
from
djangorestframework.views
import
BaseView
from
djangorestframework.resource
import
FormResource
import
StringIO
import
StringIO
class
UploadFilesTests
(
TestCase
):
class
UploadFilesTests
(
TestCase
):
...
@@ -15,9 +16,12 @@ class UploadFilesTests(TestCase):
...
@@ -15,9 +16,12 @@ class UploadFilesTests(TestCase):
class
FileForm
(
forms
.
Form
):
class
FileForm
(
forms
.
Form
):
file
=
forms
.
FileField
file
=
forms
.
FileField
class
MockResource
(
FormResource
):
form
=
FileForm
class
MockView
(
BaseView
):
class
MockView
(
BaseView
):
permissions
=
()
permissions
=
()
form
=
FileForm
resource
=
MockResource
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
return
{
'FILE_NAME'
:
self
.
CONTENT
[
'file'
]
.
name
,
return
{
'FILE_NAME'
:
self
.
CONTENT
[
'file'
]
.
name
,
...
...
djangorestframework/tests/methods.py
View file @
15f9e7c5
...
@@ -22,6 +22,6 @@ class TestMethodOverloading(TestCase):
...
@@ -22,6 +22,6 @@ class TestMethodOverloading(TestCase):
def
test_overloaded_POST_behaviour_determines_overloaded_method
(
self
):
def
test_overloaded_POST_behaviour_determines_overloaded_method
(
self
):
"""POST requests can be overloaded to another method by setting a reserved form field"""
"""POST requests can be overloaded to another method by setting a reserved form field"""
view
=
RequestMixin
()
view
=
RequestMixin
()
view
.
request
=
self
.
req
.
post
(
'/'
,
{
view
.
METHOD_PARAM
:
'DELETE'
})
view
.
request
=
self
.
req
.
post
(
'/'
,
{
view
.
_
METHOD_PARAM
:
'DELETE'
})
view
.
perform_form_overloading
()
view
.
_
perform_form_overloading
()
self
.
assertEqual
(
view
.
method
,
'DELETE'
)
self
.
assertEqual
(
view
.
method
,
'DELETE'
)
djangorestframework/tests/parsers.py
View file @
15f9e7c5
...
@@ -24,7 +24,8 @@ Here is some example data, which would eventually be sent along with a post requ
...
@@ -24,7 +24,8 @@ Here is some example data, which would eventually be sent along with a post requ
Default behaviour for :class:`parsers.FormParser`, is to return a single value for each parameter :
Default behaviour for :class:`parsers.FormParser`, is to return a single value for each parameter :
>>> FormParser(some_view).parse(StringIO(inpt)) == {'key1': 'bla1', 'key2': 'blo1'}
>>> (data, files) = FormParser(some_view).parse(StringIO(inpt))
>>> data == {'key1': 'bla1', 'key2': 'blo1'}
True
True
However, you can customize this behaviour by subclassing :class:`parsers.FormParser`, and overriding :meth:`parsers.FormParser.is_a_list` :
However, you can customize this behaviour by subclassing :class:`parsers.FormParser`, and overriding :meth:`parsers.FormParser.is_a_list` :
...
@@ -36,7 +37,8 @@ However, you can customize this behaviour by subclassing :class:`parsers.FormPar
...
@@ -36,7 +37,8 @@ However, you can customize this behaviour by subclassing :class:`parsers.FormPar
This new parser only flattens the lists of parameters that contain a single value.
This new parser only flattens the lists of parameters that contain a single value.
>>> MyFormParser(some_view).parse(StringIO(inpt)) == {'key1': 'bla1', 'key2': ['blo1', 'blo2']}
>>> (data, files) = MyFormParser(some_view).parse(StringIO(inpt))
>>> data == {'key1': 'bla1', 'key2': ['blo1', 'blo2']}
True
True
.. note:: The same functionality is available for :class:`parsers.MultiPartParser`.
.. note:: The same functionality is available for :class:`parsers.MultiPartParser`.
...
@@ -61,7 +63,8 @@ The browsers usually strip the parameter completely. A hack to avoid this, and t
...
@@ -61,7 +63,8 @@ The browsers usually strip the parameter completely. A hack to avoid this, and t
:class:`parsers.FormParser` strips the values ``_empty`` from all the lists.
:class:`parsers.FormParser` strips the values ``_empty`` from all the lists.
>>> MyFormParser(some_view).parse(StringIO(inpt)) == {'key1': 'blo1'}
>>> (data, files) = MyFormParser(some_view).parse(StringIO(inpt))
>>> data == {'key1': 'blo1'}
True
True
Oh ... but wait a second, the parameter ``key2`` isn't even supposed to be a list, so the parser just stripped it.
Oh ... but wait a second, the parameter ``key2`` isn't even supposed to be a list, so the parser just stripped it.
...
@@ -71,7 +74,8 @@ Oh ... but wait a second, the parameter ``key2`` isn't even supposed to be a lis
...
@@ -71,7 +74,8 @@ Oh ... but wait a second, the parameter ``key2`` isn't even supposed to be a lis
... def is_a_list(self, key, val_list):
... def is_a_list(self, key, val_list):
... return key == 'key2'
... return key == 'key2'
...
...
>>> MyFormParser(some_view).parse(StringIO(inpt)) == {'key1': 'blo1', 'key2': []}
>>> (data, files) = MyFormParser(some_view).parse(StringIO(inpt))
>>> data == {'key1': 'blo1', 'key2': []}
True
True
Better like that. Note that you can configure something else than ``_empty`` for the empty value by setting :attr:`parsers.FormParser.EMPTY_VALUE`.
Better like that. Note that you can configure something else than ``_empty`` for the empty value by setting :attr:`parsers.FormParser.EMPTY_VALUE`.
...
@@ -123,7 +127,7 @@ class TestMultiPartParser(TestCase):
...
@@ -123,7 +127,7 @@ class TestMultiPartParser(TestCase):
post_req
=
RequestFactory
()
.
post
(
'/'
,
self
.
body
,
content_type
=
self
.
content_type
)
post_req
=
RequestFactory
()
.
post
(
'/'
,
self
.
body
,
content_type
=
self
.
content_type
)
view
=
BaseView
()
view
=
BaseView
()
view
.
request
=
post_req
view
.
request
=
post_req
parsed
=
MultiPartParser
(
view
)
.
parse
(
StringIO
(
self
.
body
))
(
data
,
files
)
=
MultiPartParser
(
view
)
.
parse
(
StringIO
(
self
.
body
))
self
.
assertEqual
(
parsed
[
'key1'
],
'val1'
)
self
.
assertEqual
(
data
[
'key1'
],
'val1'
)
self
.
assertEqual
(
parsed
.
FILES
[
'file1'
]
.
read
(),
'blablabla'
)
self
.
assertEqual
(
files
[
'file1'
]
.
read
(),
'blablabla'
)
djangorestframework/views.py
View file @
15f9e7c5
...
@@ -17,7 +17,7 @@ __all__ = (
...
@@ -17,7 +17,7 @@ __all__ = (
class
BaseView
(
RequestMixin
,
ResponseMixin
,
AuthMixin
,
View
):
class
BaseView
(
Re
sourceMixin
,
Re
questMixin
,
ResponseMixin
,
AuthMixin
,
View
):
"""Handles incoming requests and maps them to REST operations.
"""Handles incoming requests and maps them to REST operations.
Performs request deserialization, response serialization, authentication and input validation."""
Performs request deserialization, response serialization, authentication and input validation."""
...
@@ -46,9 +46,6 @@ class BaseView(RequestMixin, ResponseMixin, AuthMixin, View):
...
@@ -46,9 +46,6 @@ class BaseView(RequestMixin, ResponseMixin, AuthMixin, View):
# List of all permissions that must be checked.
# List of all permissions that must be checked.
permissions
=
(
permissions
.
FullAnonAccess
,
)
permissions
=
(
permissions
.
FullAnonAccess
,
)
# Optional form for input validation and presentation of HTML formatted responses.
form
=
None
# Allow name and description for the Resource to be set explicitly,
# Allow name and description for the Resource to be set explicitly,
# overiding the default classname/docstring behaviour.
# overiding the default classname/docstring behaviour.
# These are used for documentation in the standard html and text renderers.
# These are used for documentation in the standard html and text renderers.
...
@@ -60,22 +57,13 @@ class BaseView(RequestMixin, ResponseMixin, AuthMixin, View):
...
@@ -60,22 +57,13 @@ class BaseView(RequestMixin, ResponseMixin, AuthMixin, View):
return
[
method
.
upper
()
for
method
in
self
.
http_method_names
if
hasattr
(
self
,
method
)]
return
[
method
.
upper
()
for
method
in
self
.
http_method_names
if
hasattr
(
self
,
method
)]
def
http_method_not_allowed
(
self
,
request
,
*
args
,
**
kwargs
):
def
http_method_not_allowed
(
self
,
request
,
*
args
,
**
kwargs
):
"""Return an HTTP 405 error if an operation is called which does not have a handler method."""
"""
Return an HTTP 405 error if an operation is called which does not have a handler method.
"""
raise
ErrorResponse
(
status
.
HTTP_405_METHOD_NOT_ALLOWED
,
raise
ErrorResponse
(
status
.
HTTP_405_METHOD_NOT_ALLOWED
,
{
'detail'
:
'Method
\'
%
s
\'
not allowed on this resource.'
%
self
.
method
})
{
'detail'
:
'Method
\'
%
s
\'
not allowed on this resource.'
%
self
.
method
})
def
cleanup_response
(
self
,
data
):
"""Perform any resource-specific data filtering prior to the standard HTTP
content-type serialization.
Eg filter complex objects that cannot be serialized by json/xml/etc into basic objects that can.
TODO: This is going to be removed. I think that the 'fields' behaviour is going to move into
the RendererMixin and Renderer classes."""
return
data
# Note: session based authentication is explicitly CSRF validated,
# Note: session based authentication is explicitly CSRF validated,
# all other authentication is CSRF exempt.
# all other authentication is CSRF exempt.
@csrf_exempt
@csrf_exempt
...
@@ -92,7 +80,7 @@ class BaseView(RequestMixin, ResponseMixin, AuthMixin, View):
...
@@ -92,7 +80,7 @@ class BaseView(RequestMixin, ResponseMixin, AuthMixin, View):
try
:
try
:
# If using a form POST with '_method'/'_content'/'_content_type' overrides, then alter
# If using a form POST with '_method'/'_content'/'_content_type' overrides, then alter
# self.method, self.content_type, self.RAW_CONTENT & self.CONTENT appropriately.
# self.method, self.content_type, self.RAW_CONTENT & self.CONTENT appropriately.
self
.
perform_form_overloading
()
self
.
_
perform_form_overloading
()
# Authenticate and check request is has the relevant permissions
# Authenticate and check request is has the relevant permissions
self
.
_check_permissions
()
self
.
_check_permissions
()
...
@@ -114,13 +102,14 @@ class BaseView(RequestMixin, ResponseMixin, AuthMixin, View):
...
@@ -114,13 +102,14 @@ class BaseView(RequestMixin, ResponseMixin, AuthMixin, View):
response
=
Response
(
status
.
HTTP_204_NO_CONTENT
)
response
=
Response
(
status
.
HTTP_204_NO_CONTENT
)
# Pre-serialize filtering (eg filter complex objects into natively serializable types)
# Pre-serialize filtering (eg filter complex objects into natively serializable types)
response
.
cleaned_content
=
self
.
resource
.
object_to_serializable
(
response
.
raw_content
)
response
.
cleaned_content
=
self
.
object_to_data
(
response
.
raw_content
)
except
ErrorResponse
,
exc
:
except
ErrorResponse
,
exc
:
response
=
exc
.
response
response
=
exc
.
response
except
:
except
:
import
traceback
import
traceback
traceback
.
print_exc
()
traceback
.
print_exc
()
raise
# Always add these headers.
# Always add these headers.
#
#
...
...
examples/sandbox/views.py
View file @
15f9e7c5
"""The root view for the examples provided with Django REST framework"""
"""The root view for the examples provided with Django REST framework"""
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
djangorestframework.
resource
import
Resource
from
djangorestframework.
views
import
BaseView
class
Sandbox
(
Resource
):
class
Sandbox
(
BaseView
):
"""This is the sandbox for the examples provided with [Django REST framework](http://django-rest-framework.org).
"""This is the sandbox for the examples provided with [Django REST framework](http://django-rest-framework.org).
These examples are provided to help you get a better idea of the some of the features of RESTful APIs created using the framework.
These examples are provided to help you get a better idea of the some of the features of RESTful APIs created using the framework.
...
...
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