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
1cde31c8
Commit
1cde31c8
authored
Feb 25, 2012
by
Tom Christie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Massive merge
parent
5fd4c639
Hide whitespace changes
Inline
Side-by-side
Showing
45 changed files
with
859 additions
and
855 deletions
+859
-855
AUTHORS
+1
-0
djangorestframework/__init__.py
+1
-1
djangorestframework/authentication.py
+0
-2
djangorestframework/compat.py
+10
-14
djangorestframework/mixins.py
+26
-83
djangorestframework/parsers.py
+37
-40
djangorestframework/renderers.py
+28
-41
djangorestframework/request.py
+84
-75
djangorestframework/resources.py
+10
-54
djangorestframework/response.py
+53
-57
djangorestframework/reverse.py
+9
-12
djangorestframework/runtests/settings.py
+0
-5
djangorestframework/templates/djangorestframework/base.html
+9
-2
djangorestframework/templates/djangorestframework/login.html
+1
-1
djangorestframework/tests/__init__.py
+0
-1
djangorestframework/tests/accept.py
+13
-2
djangorestframework/tests/modelviews.py
+4
-1
djangorestframework/tests/oauthentication.py
+1
-1
djangorestframework/tests/parsers.py
+15
-13
djangorestframework/tests/renderers.py
+176
-32
djangorestframework/tests/request.py
+131
-120
djangorestframework/tests/response.py
+24
-31
djangorestframework/tests/reverse.py
+2
-1
djangorestframework/tests/views.py
+26
-28
djangorestframework/urls.py
+7
-4
djangorestframework/utils/__init__.py
+41
-69
djangorestframework/utils/staticviews.py
+0
-61
djangorestframework/views.py
+34
-34
docs/howto/setup.rst
+9
-9
docs/index.rst
+6
-0
examples/blogpost/models.py
+2
-1
examples/blogpost/resources.py
+11
-2
examples/mixin/urls.py
+3
-3
examples/modelresourceexample/models.py
+1
-2
examples/modelresourceexample/resources.py
+7
-0
examples/modelresourceexample/urls.py
+5
-2
examples/objectstore/views.py
+36
-16
examples/pygments_api/forms.py
+1
-2
examples/pygments_api/tests.py
+2
-5
examples/pygments_api/views.py
+14
-9
examples/requestexample/views.py
+1
-2
examples/resourceexample/forms.py
+1
-0
examples/resourceexample/views.py
+5
-3
examples/sandbox/views.py
+10
-10
examples/urls.py
+2
-4
No files found.
AUTHORS
View file @
1cde31c8
...
...
@@ -33,6 +33,7 @@ Camille Harang <mammique>
Paul Oswald <poswald>
Sean C. Farley <scfarley>
Daniel Izquierdo <izquierdo>
Can Yavuz <tschan>
THANKS TO:
...
...
djangorestframework/__init__.py
View file @
1cde31c8
__version__
=
'0.
3.3
'
__version__
=
'0.
4.0-dev
'
VERSION
=
__version__
# synonym
djangorestframework/authentication.py
View file @
1cde31c8
...
...
@@ -87,8 +87,6 @@ class UserLoggedInAuthentication(BaseAuthentication):
Returns a :obj:`User` if the request session currently has a logged in user.
Otherwise returns :const:`None`.
"""
request
.
DATA
# Make sure our generic parsing runs first
if
getattr
(
request
,
'user'
,
None
)
and
request
.
user
.
is_active
:
# Enforce CSRF validation for session based authentication.
resp
=
CsrfViewMiddleware
()
.
process_view
(
request
,
None
,
(),
{})
...
...
djangorestframework/compat.py
View file @
1cde31c8
...
...
@@ -214,18 +214,15 @@ else:
REASON_NO_CSRF_COOKIE
=
"CSRF cookie not set."
REASON_BAD_TOKEN
=
"CSRF token missing or incorrect."
def
_get_failure_view
():
"""
Returns the view to be used for CSRF rejections
"""
return
get_callable
(
settings
.
CSRF_FAILURE_VIEW
)
def
_get_new_csrf_key
():
return
hashlib
.
md5
(
"
%
s
%
s"
%
(
randrange
(
0
,
_MAX_CSRF_KEY
),
settings
.
SECRET_KEY
))
.
hexdigest
()
def
get_token
(
request
):
"""
Returns the the CSRF token required for a POST form. The token is an
...
...
@@ -239,7 +236,6 @@ else:
request
.
META
[
"CSRF_COOKIE_USED"
]
=
True
return
request
.
META
.
get
(
"CSRF_COOKIE"
,
None
)
def
_sanitize_token
(
token
):
# Allow only alphanum, and ensure we return a 'str' for the sake of the post
# processing middleware.
...
...
@@ -432,12 +428,13 @@ try:
except
ImportError
:
yaml
=
None
import
unittest
try
:
import
unittest.skip
except
ImportError
:
# python < 2.7
except
ImportError
:
# python < 2.7
from
unittest
import
TestCase
import
functools
import
functools
def
skip
(
reason
):
# Pasted from py27/lib/unittest/case.py
...
...
@@ -448,20 +445,19 @@ except ImportError: # python < 2.7
if
not
(
isinstance
(
test_item
,
type
)
and
issubclass
(
test_item
,
TestCase
)):
@functools.wraps
(
test_item
)
def
skip_wrapper
(
*
args
,
**
kwargs
):
pass
pass
test_item
=
skip_wrapper
test_item
.
__unittest_skip__
=
True
test_item
.
__unittest_skip_why__
=
reason
return
test_item
return
decorator
unittest
.
skip
=
skip
# reverse_lazy (Django 1.4 onwards)
# xml.etree.parse only throws ParseError for python >= 2.7
try
:
from
django.core.urlresolvers
import
reverse_lazy
except
:
from
django.core.urlresolvers
import
reverse
from
django.utils.functional
import
lazy
reverse_lazy
=
lazy
(
reverse
,
str
)
from
xml.etree
import
ParseError
as
ETParseError
except
ImportError
:
# python < 2.7
ETParseError
=
None
djangorestframework/mixins.py
View file @
1cde31c8
...
...
@@ -21,14 +21,13 @@ __all__ = (
'ResponseMixin'
,
'AuthMixin'
,
'ResourceMixin'
,
# Reverse URL lookup behavior
'InstanceMixin'
,
# Model behavior mixins
'ReadModelMixin'
,
'CreateModelMixin'
,
'UpdateModelMixin'
,
'DeleteModelMixin'
,
'ListModelMixin'
'ListModelMixin'
,
'PaginatorMixin'
)
...
...
@@ -39,39 +38,33 @@ class RequestMixin(object):
`Mixin` class enabling the use of :class:`request.Request` in your views.
"""
parser_classes
=
()
"""
The set of parsers that the view can handle.
Should be a tuple/list of classes as described in the :mod:`parsers` module.
"""
request_class
=
Request
"""
The class to use as a wrapper for the original request object.
"""
def
get_parsers
(
self
):
"""
Instantiates and returns the list of parsers the request will use.
"""
return
[
p
(
self
)
for
p
in
self
.
parser_classes
]
def
create_request
(
self
,
request
):
"""
Creates and returns an instance of :class:`request.Request`.
This new instance wraps the `request` passed as a parameter, and use
the
parsers set on the view.
This new instance wraps the `request` passed as a parameter, and use
the
parsers set on the view.
"""
parsers
=
self
.
get_parsers
()
return
self
.
request_class
(
request
,
parsers
=
parsers
)
return
self
.
request_class
(
request
,
parsers
=
self
.
parsers
)
@property
def
_parsed_media_types
(
self
):
"""
Returns a list of all the media types that this view can parse.
Return a list of all the media types that this view can parse.
"""
return
[
parser
.
media_type
for
parser
in
self
.
parsers
]
@property
def
_default_parser
(
self
):
"""
Return the view's default parser class.
"""
return
[
p
.
media_type
for
p
in
self
.
parser_classes
]
return
self
.
parsers
[
0
]
########## ResponseMixin ##########
...
...
@@ -80,58 +73,32 @@ class ResponseMixin(object):
`Mixin` class enabling the use of :class:`response.Response` in your views.
"""
renderer
_classe
s
=
()
renderers
=
()
"""
The set of response renderers that the view can handle.
Should be a tuple/list of classes as described in the :mod:`renderers` module.
"""
def
get_renderers
(
self
):
"""
Instantiates and returns the list of renderers the response will use.
"""
return
[
r
(
self
)
for
r
in
self
.
renderer_classes
]
def
prepare_response
(
self
,
response
):
"""
Prepares and returns `response`.
This has no effect if the response is not an instance of :class:`response.Response`.
"""
if
hasattr
(
response
,
'request'
)
and
response
.
request
is
None
:
response
.
request
=
self
.
request
# set all the cached headers
for
name
,
value
in
self
.
headers
.
items
():
response
[
name
]
=
value
# set the views renderers on the response
response
.
renderers
=
self
.
get_renderers
()
return
response
@property
def
header
s
(
self
):
def
_rendered_media_type
s
(
self
):
"""
Dictionary of headers to set on the response.
This is useful when the response doesn't exist yet, but you
want to memorize some headers to set on it when it will exist.
Return an list of all the media types that this response can render.
"""
if
not
hasattr
(
self
,
'_headers'
):
self
.
_headers
=
{}
return
self
.
_headers
return
[
renderer
.
media_type
for
renderer
in
self
.
renderers
]
@property
def
_rendered_
media_type
s
(
self
):
def
_rendered_
format
s
(
self
):
"""
Return a
n list of all the media types that this view
can render.
Return a
list of all the formats that this response
can render.
"""
return
[
renderer
.
media_type
for
renderer
in
self
.
get_renderers
()
]
return
[
renderer
.
format
for
renderer
in
self
.
renderers
]
@property
def
_
rendered_formats
(
self
):
def
_
default_renderer
(
self
):
"""
Return
a list of all the formats that this view can render
.
Return
the response's default renderer class
.
"""
return
[
renderer
.
format
for
renderer
in
self
.
get_renderers
()
]
return
self
.
renderers
[
0
]
########## Auth Mixin ##########
...
...
@@ -254,30 +221,6 @@ class ResourceMixin(object):
else
:
return
None
##########
class
InstanceMixin
(
object
):
"""
`Mixin` class that is used to identify a `View` class as being the canonical identifier
for the resources it is mapped to.
"""
@classmethod
def
as_view
(
cls
,
**
initkwargs
):
"""
Store the callable object on the resource class that has been associated with this view.
"""
view
=
super
(
InstanceMixin
,
cls
)
.
as_view
(
**
initkwargs
)
resource
=
getattr
(
cls
(
**
initkwargs
),
'resource'
,
None
)
if
resource
:
# We do a little dance when we store the view callable...
# we need to store it wrapped in a 1-tuple, so that inspect will treat it
# as a function when we later look it up (rather than turning it into a method).
# This makes sure our URL reversing works ok.
resource
.
view_callable
=
(
view
,)
return
view
########## Model Mixins ##########
...
...
@@ -411,7 +354,7 @@ class CreateModelMixin(ModelMixin):
response
=
Response
(
instance
,
status
=
status
.
HTTP_201_CREATED
)
# Set headers
if
hasattr
(
instance
,
'get_absolute_
url'
):
if
hasattr
(
self
.
resource
,
'
url'
):
response
[
'Location'
]
=
self
.
resource
(
self
)
.
url
(
instance
)
return
response
...
...
djangorestframework/parsers.py
View file @
1cde31c8
...
...
@@ -20,6 +20,8 @@ from djangorestframework.compat import yaml
from
djangorestframework.response
import
ImmediateResponse
from
djangorestframework.utils.mediatypes
import
media_type_matches
from
xml.etree
import
ElementTree
as
ET
from
djangorestframework.compat
import
ETParseError
from
xml.parsers.expat
import
ExpatError
import
datetime
import
decimal
...
...
@@ -43,13 +45,6 @@ class BaseParser(object):
media_type
=
None
def
__init__
(
self
,
view
=
None
):
"""
Initialize the parser with the ``View`` instance as state,
in case the parser needs to access any metadata on the :obj:`View` object.
"""
self
.
view
=
view
def
can_handle_request
(
self
,
content_type
):
"""
Returns :const:`True` if this parser is able to deal with the given *content_type*.
...
...
@@ -63,12 +58,12 @@ class BaseParser(object):
"""
return
media_type_matches
(
self
.
media_type
,
content_type
)
def
parse
(
self
,
stream
):
def
parse
(
self
,
stream
,
meta
,
upload_handlers
):
"""
Given a *stream* to read from, return the deserialized output.
Should return a 2-tuple of (data, files).
"""
raise
NotImplementedError
(
"
BaseParser
.parse() Must be overridden to be implemented."
)
raise
NotImplementedError
(
".parse() Must be overridden to be implemented."
)
class
JSONParser
(
BaseParser
):
...
...
@@ -78,7 +73,7 @@ class JSONParser(BaseParser):
media_type
=
'application/json'
def
parse
(
self
,
stream
):
def
parse
(
self
,
stream
,
meta
,
upload_handlers
):
"""
Returns a 2-tuple of `(data, files)`.
...
...
@@ -93,29 +88,26 @@ class JSONParser(BaseParser):
status
=
status
.
HTTP_400_BAD_REQUEST
)
if
yaml
:
class
YAMLParser
(
BaseParser
):
"""
Parses YAML-serialized data.
"""
class
YAMLParser
(
BaseParser
):
"""
Parses YAML-serialized data.
"""
media_type
=
'application/yaml'
media_type
=
'application/yaml'
def
parse
(
self
,
stream
):
"""
Returns a 2-tuple of `(data, files)`.
def
parse
(
self
,
stream
,
meta
,
upload_handlers
):
"""
Returns a 2-tuple of `(data, files)`.
`data` will be an object which is the parsed content of the response.
`files` will always be `None`.
"""
try
:
return
(
yaml
.
safe_load
(
stream
),
None
)
except
ValueError
,
exc
:
raise
ImmediateResponse
(
{
'detail'
:
'YAML parse error -
%
s'
%
unicode
(
exc
)},
status
=
status
.
HTTP_400_BAD_REQUEST
)
else
:
YAMLParser
=
None
`data` will be an object which is the parsed content of the response.
`files` will always be `None`.
"""
try
:
return
(
yaml
.
safe_load
(
stream
),
None
)
except
ValueError
,
exc
:
raise
ImmediateResponse
(
{
'detail'
:
'YAML parse error -
%
s'
%
unicode
(
exc
)},
status
=
status
.
HTTP_400_BAD_REQUEST
)
class
PlainTextParser
(
BaseParser
):
...
...
@@ -125,7 +117,7 @@ class PlainTextParser(BaseParser):
media_type
=
'text/plain'
def
parse
(
self
,
stream
):
def
parse
(
self
,
stream
,
meta
,
upload_handlers
):
"""
Returns a 2-tuple of `(data, files)`.
...
...
@@ -142,7 +134,7 @@ class FormParser(BaseParser):
media_type
=
'application/x-www-form-urlencoded'
def
parse
(
self
,
stream
):
def
parse
(
self
,
stream
,
meta
,
upload_handlers
):
"""
Returns a 2-tuple of `(data, files)`.
...
...
@@ -160,21 +152,20 @@ class MultiPartParser(BaseParser):
media_type
=
'multipart/form-data'
def
parse
(
self
,
stream
):
def
parse
(
self
,
stream
,
meta
,
upload_handlers
):
"""
Returns a 2-tuple of `(data, files)`.
`data` will be a :class:`QueryDict` containing all the form parameters.
`files` will be a :class:`QueryDict` containing all the form files.
"""
upload_handlers
=
self
.
view
.
request
.
_get_upload_handlers
()
try
:
django_parser
=
DjangoMultiPartParser
(
self
.
view
.
request
.
META
,
stream
,
upload_handlers
)
parser
=
DjangoMultiPartParser
(
meta
,
stream
,
upload_handlers
)
return
parser
.
parse
()
except
MultiPartParserError
,
exc
:
raise
ImmediateResponse
(
{
'detail'
:
'multipart parse error -
%
s'
%
unicode
(
exc
)},
status
=
status
.
HTTP_400_BAD_REQUEST
)
return
django_parser
.
parse
()
class
XMLParser
(
BaseParser
):
...
...
@@ -184,14 +175,18 @@ class XMLParser(BaseParser):
media_type
=
'application/xml'
def
parse
(
self
,
stream
):
def
parse
(
self
,
stream
,
meta
,
upload_handlers
):
"""
Returns a 2-tuple of `(data, files)`.
`data` will simply be a string representing the body of the request.
`files` will always be `None`.
"""
tree
=
ET
.
parse
(
stream
)
try
:
tree
=
ET
.
parse
(
stream
)
except
(
ExpatError
,
ETParseError
,
ValueError
),
exc
:
content
=
{
'detail'
:
'XML parse error -
%
s'
%
unicode
(
exc
)}
raise
ImmediateResponse
(
content
,
status
=
status
.
HTTP_400_BAD_REQUEST
)
data
=
self
.
_xml_convert
(
tree
.
getroot
())
return
(
data
,
None
)
...
...
@@ -251,5 +246,7 @@ DEFAULT_PARSERS = (
XMLParser
)
if
YAMLParser
:
DEFAULT_PARSERS
+=
(
YAMLParser
,)
if
yaml
:
DEFAULT_PARSERS
+=
(
YAMLParser
,
)
else
:
YAMLParser
=
None
djangorestframework/renderers.py
View file @
1cde31c8
...
...
@@ -6,20 +6,18 @@ by serializing the output along with documentation regarding the View, output st
and providing forms and links depending on the allowed methods, renderers and parsers on the View.
"""
from
django
import
forms
from
django.conf
import
settings
from
django.core.serializers.json
import
DateTimeAwareJSONEncoder
from
django.template
import
RequestContext
,
loader
from
django.utils
import
simplejson
as
json
from
djangorestframework.compat
import
yaml
from
djangorestframework.utils
import
dict2xml
,
url_resolves
,
allowed_methods
from
djangorestframework.utils
import
dict2xml
from
djangorestframework.utils.breadcrumbs
import
get_breadcrumbs
from
djangorestframework.utils.mediatypes
import
get_media_type_params
,
add_media_type_param
,
media_type_matches
from
djangorestframework
import
VERSION
import
string
from
urllib
import
quote_plus
__all__
=
(
'BaseRenderer'
,
...
...
@@ -156,25 +154,22 @@ class XMLRenderer(BaseRenderer):
return
dict2xml
(
obj
)
if
yaml
:
class
YAMLRenderer
(
BaseRenderer
):
"""
Renderer which serializes to YAML.
"""
class
YAMLRenderer
(
BaseRenderer
):
"""
Renderer which serializes to YAML.
"""
media_type
=
'application/yaml'
format
=
'yaml'
media_type
=
'application/yaml'
format
=
'yaml'
def
render
(
self
,
obj
=
None
,
media_type
=
None
):
"""
Renders *obj* into serialized YAML.
"""
if
obj
is
None
:
return
''
def
render
(
self
,
obj
=
None
,
media_type
=
None
):
"""
Renders *obj* into serialized YAML.
"""
if
obj
is
None
:
return
''
return
yaml
.
safe_dump
(
obj
)
else
:
YAMLRenderer
=
None
return
yaml
.
safe_dump
(
obj
)
class
TemplateRenderer
(
BaseRenderer
):
...
...
@@ -218,8 +213,8 @@ class DocumentingTemplateRenderer(BaseRenderer):
"""
# Find the first valid renderer and render the content. (Don't use another documenting renderer.)
renderers
=
[
renderer
for
renderer
in
view
.
renderer
_classes
if
not
issubclass
(
renderer
,
DocumentingTemplateRenderer
)]
renderers
=
[
renderer
for
renderer
in
view
.
renderer
s
if
not
issubclass
(
renderer
,
DocumentingTemplateRenderer
)]
if
not
renderers
:
return
'[No renderers were found]'
...
...
@@ -278,14 +273,14 @@ class DocumentingTemplateRenderer(BaseRenderer):
# NB. http://jacobian.org/writing/dynamic-form-generation/
class
GenericContentForm
(
forms
.
Form
):
def
__init__
(
self
,
request
):
def
__init__
(
self
,
view
,
request
):
"""We don't know the names of the fields we want to set until the point the form is instantiated,
as they are determined by the Resource the form is being created against.
Add the fields dynamically."""
super
(
GenericContentForm
,
self
)
.
__init__
()
contenttype_choices
=
[(
media_type
,
media_type
)
for
media_type
in
request
.
_parsed_media_types
]
initial_contenttype
=
request
.
_default_parser
.
media_type
contenttype_choices
=
[(
media_type
,
media_type
)
for
media_type
in
view
.
_parsed_media_types
]
initial_contenttype
=
view
.
_default_parser
.
media_type
self
.
fields
[
request
.
_CONTENTTYPE_PARAM
]
=
forms
.
ChoiceField
(
label
=
'Content Type'
,
choices
=
contenttype_choices
,
...
...
@@ -298,7 +293,7 @@ class DocumentingTemplateRenderer(BaseRenderer):
return
None
# Okey doke, let's do it
return
GenericContentForm
(
view
.
request
)
return
GenericContentForm
(
view
,
view
.
request
)
def
get_name
(
self
):
try
:
...
...
@@ -327,13 +322,6 @@ class DocumentingTemplateRenderer(BaseRenderer):
put_form_instance
=
self
.
_get_form_instance
(
self
.
view
,
'put'
)
post_form_instance
=
self
.
_get_form_instance
(
self
.
view
,
'post'
)
if
url_resolves
(
settings
.
LOGIN_URL
)
and
url_resolves
(
settings
.
LOGOUT_URL
):
login_url
=
"
%
s?next=
%
s"
%
(
settings
.
LOGIN_URL
,
quote_plus
(
self
.
view
.
request
.
path
))
logout_url
=
"
%
s?next=
%
s"
%
(
settings
.
LOGOUT_URL
,
quote_plus
(
self
.
view
.
request
.
path
))
else
:
login_url
=
None
logout_url
=
None
name
=
self
.
get_name
()
description
=
self
.
get_description
()
...
...
@@ -343,21 +331,18 @@ class DocumentingTemplateRenderer(BaseRenderer):
context
=
RequestContext
(
self
.
view
.
request
,
{
'content'
:
content
,
'view'
:
self
.
view
,
'request'
:
self
.
view
.
request
,
# TODO: remove
'request'
:
self
.
view
.
request
,
'response'
:
self
.
view
.
response
,
'description'
:
description
,
'name'
:
name
,
'version'
:
VERSION
,
'breadcrumblist'
:
breadcrumb_list
,
'allowed_methods'
:
allowed_methods
(
self
.
view
)
,
'allowed_methods'
:
self
.
view
.
allowed_methods
,
'available_formats'
:
self
.
view
.
_rendered_formats
,
'put_form'
:
put_form_instance
,
'post_form'
:
post_form_instance
,
'login_url'
:
login_url
,
'logout_url'
:
logout_url
,
'FORMAT_PARAM'
:
self
.
_FORMAT_QUERY_PARAM
,
'METHOD_PARAM'
:
getattr
(
self
.
view
.
request
,
'_METHOD_PARAM'
,
None
),
'ADMIN_MEDIA_PREFIX'
:
getattr
(
settings
,
'ADMIN_MEDIA_PREFIX'
,
None
),
'METHOD_PARAM'
:
getattr
(
self
.
view
,
'_METHOD_PARAM'
,
None
),
})
ret
=
template
.
render
(
context
)
...
...
@@ -415,5 +400,7 @@ DEFAULT_RENDERERS = (
XMLRenderer
)
if
YAMLRenderer
:
DEFAULT_RENDERERS
+=
(
YAMLRenderer
,)
if
yaml
:
DEFAULT_RENDERERS
+=
(
YAMLRenderer
,
)
else
:
YAMLRenderer
=
None
djangorestframework/request.py
View file @
1cde31c8
...
...
@@ -4,15 +4,14 @@ object received in all the views.
The wrapped request then offers a richer API, in particular :
- content automatically parsed according to `Content-Type` header, and available as :meth:`.DATA<Request.DATA>`
- content automatically parsed according to `Content-Type` header,
and available as :meth:`.DATA<Request.DATA>`
- full support of PUT method, including support for file uploads
- form overloading of HTTP method, content type and content
"""
from
djangorestframework.response
import
ImmediateResponse
from
djangorestframework
import
status
from
djangorestframework.utils.mediatypes
import
is_form_media_type
from
djangorestframework.utils
import
as_tuple
from
StringIO
import
StringIO
...
...
@@ -20,6 +19,14 @@ from StringIO import StringIO
__all__
=
(
'Request'
,)
class
Empty
:
pass
def
_hasattr
(
obj
,
name
):
return
not
getattr
(
obj
,
name
)
is
Empty
class
Request
(
object
):
"""
Wrapper allowing to enhance a standard `HttpRequest` instance.
...
...
@@ -35,19 +42,29 @@ class Request(object):
_CONTENT_PARAM
=
'_content'
def
__init__
(
self
,
request
=
None
,
parsers
=
None
):
self
.
request
=
request
if
parsers
is
not
None
:
self
.
parsers
=
parsers
self
.
_request
=
request
self
.
parsers
=
parsers
or
()
self
.
_data
=
Empty
self
.
_files
=
Empty
self
.
_method
=
Empty
self
.
_content_type
=
Empty
self
.
_stream
=
Empty
def
get_parsers
(
self
):
"""
Instantiates and returns the list of parsers the request will use.
"""
return
[
parser
()
for
parser
in
self
.
parsers
]
@property
def
method
(
self
):
"""
Returns the HTTP method.
This allows the `method` to be overridden by using a hidden `form`
field
on a form POST request.
This allows the `method` to be overridden by using a hidden `form`
field
on a form POST request.
"""
if
not
hasattr
(
self
,
'_method'
):
if
not
_
hasattr
(
self
,
'_method'
):
self
.
_load_method_and_content_type
()
return
self
.
_method
...
...
@@ -60,11 +77,20 @@ class Request(object):
as it allows the content type to be overridden by using a hidden form
field on a form POST request.
"""
if
not
hasattr
(
self
,
'_content_type'
):
if
not
_
hasattr
(
self
,
'_content_type'
):
self
.
_load_method_and_content_type
()
return
self
.
_content_type
@property
def
stream
(
self
):
"""
Returns an object that may be used to stream the request content.
"""
if
not
_hasattr
(
self
,
'_stream'
):
self
.
_load_stream
()
return
self
.
_stream
@property
def
DATA
(
self
):
"""
Parses the request body and returns the data.
...
...
@@ -72,7 +98,7 @@ class Request(object):
Similar to ``request.POST``, except that it handles arbitrary parsers,
and also works on methods other than POST (eg PUT).
"""
if
not
hasattr
(
self
,
'_data'
):
if
not
_
hasattr
(
self
,
'_data'
):
self
.
_load_data_and_files
()
return
self
.
_data
...
...
@@ -83,7 +109,7 @@ class Request(object):
Similar to ``request.FILES``, except that it handles arbitrary parsers,
and also works on methods other than POST (eg PUT).
"""
if
not
hasattr
(
self
,
'_files'
):
if
not
_
hasattr
(
self
,
'_files'
):
self
.
_load_data_and_files
()
return
self
.
_files
...
...
@@ -91,11 +117,11 @@ class Request(object):
"""
Parses the request content into self.DATA and self.FILES.
"""
if
not
hasattr
(
self
,
'_content_type'
):
if
not
_
hasattr
(
self
,
'_content_type'
):
self
.
_load_method_and_content_type
()
if
not
hasattr
(
self
,
'_data'
):
(
self
.
_data
,
self
.
_files
)
=
self
.
_parse
(
self
.
_get_stream
(),
self
.
_content_type
)
if
not
_
hasattr
(
self
,
'_data'
):
(
self
.
_data
,
self
.
_files
)
=
self
.
_parse
()
def
_load_method_and_content_type
(
self
):
"""
...
...
@@ -104,100 +130,83 @@ class Request(object):
self
.
_content_type
=
self
.
META
.
get
(
'HTTP_CONTENT_TYPE'
,
self
.
META
.
get
(
'CONTENT_TYPE'
,
''
))
self
.
_perform_form_overloading
()
# if the HTTP method was not overloaded, we take the raw HTTP method
if
not
hasattr
(
self
,
'_method'
):
self
.
_method
=
self
.
request
.
method
def
_get_stream
(
self
):
"""
Returns an object that may be used to stream the request content.
"""
if
not
_hasattr
(
self
,
'_method'
):
self
.
_method
=
self
.
_request
.
method
def
_load_stream
(
self
):
try
:
content_length
=
int
(
self
.
META
.
get
(
'CONTENT_LENGTH'
,
self
.
META
.
get
(
'HTTP_CONTENT_LENGTH'
)))
content_length
=
int
(
self
.
META
.
get
(
'CONTENT_LENGTH'
,
self
.
META
.
get
(
'HTTP_CONTENT_LENGTH'
)))
except
(
ValueError
,
TypeError
):
content_length
=
0
# TODO: Add 1.3's LimitedStream to compat and use that.
# NOTE: Currently only supports parsing request body as a stream with 1.3
if
content_length
==
0
:
return
None
elif
hasattr
(
self
,
'read'
):
return
self
return
StringIO
(
self
.
raw_post_data
)
self
.
_stream
=
None
elif
hasattr
(
self
.
_request
,
'read'
):
self
.
_stream
=
self
.
_request
else
:
self
.
_stream
=
StringIO
(
self
.
raw_post_data
)
def
_perform_form_overloading
(
self
):
"""
If this is a form POST request, then we need to check if the method and content/content_type have been
overridden by setting them in hidden form fields or not.
If this is a form POST request, then we need to check if the method and
content/content_type have been overridden by setting them in hidden
form fields or not.
"""
# We only need to use form overloading on form POST requests.
if
(
not
self
.
_USE_FORM_OVERLOADING
or
self
.
request
.
method
!=
'POST'
or
not
is_form_media_type
(
self
.
_content_type
)):
if
(
not
self
.
_USE_FORM_OVERLOADING
or
self
.
_request
.
method
!=
'POST'
or
not
is_form_media_type
(
self
.
_content_type
)):
return
# At this point we're committed to parsing the request as form data.
self
.
_data
=
data
=
self
.
POST
.
copy
()
self
.
_files
=
self
.
FILES
self
.
_data
=
self
.
_request
.
POST
self
.
_files
=
self
.
_request
.
FILES
# Method overloading - change the method and remove the param from the content.
if
self
.
_METHOD_PARAM
in
data
:
# NOTE:
unlike `get`, `pop` on a `QueryDict` seems to return
a list of values.
if
self
.
_METHOD_PARAM
in
self
.
_
data
:
# NOTE:
`pop` on a `QueryDict` returns
a list of values.
self
.
_method
=
self
.
_data
.
pop
(
self
.
_METHOD_PARAM
)[
0
]
.
upper
()
# Content overloading - modify the content type, and re-parse.
if
self
.
_CONTENT_PARAM
in
data
and
self
.
_CONTENTTYPE_PARAM
in
data
:
if
(
self
.
_CONTENT_PARAM
in
self
.
_data
and
self
.
_CONTENTTYPE_PARAM
in
self
.
_data
):
self
.
_content_type
=
self
.
_data
.
pop
(
self
.
_CONTENTTYPE_PARAM
)[
0
]
stream
=
StringIO
(
self
.
_data
.
pop
(
self
.
_CONTENT_PARAM
)[
0
])
(
self
.
_data
,
self
.
_files
)
=
self
.
_parse
(
stream
,
self
.
_content_type
)
s
elf
.
_s
tream
=
StringIO
(
self
.
_data
.
pop
(
self
.
_CONTENT_PARAM
)[
0
])
(
self
.
_data
,
self
.
_files
)
=
self
.
_parse
()
def
_parse
(
self
,
stream
,
content_type
):
def
_parse
(
self
):
"""
Parse the request content.
May raise a 415 ImmediateResponse (Unsupported Media Type), or a 400 ImmediateResponse (Bad Request).
May raise a 415 ImmediateResponse (Unsupported Media Type), or a
400 ImmediateResponse (Bad Request).
"""
if
s
tream
is
None
or
content_type
is
None
:
if
s
elf
.
stream
is
None
or
self
.
content_type
is
None
:
return
(
None
,
None
)
for
parser
in
as_tuple
(
self
.
parsers
):
if
parser
.
can_handle_request
(
content_type
):
return
parser
.
parse
(
s
tream
)
for
parser
in
self
.
get_parsers
(
):
if
parser
.
can_handle_request
(
self
.
content_type
):
return
parser
.
parse
(
s
elf
.
stream
,
self
.
META
,
self
.
upload_handlers
)
raise
ImmediateResponse
({
'error'
:
'Unsupported media type in request
\'
%
s
\'
.'
%
content_type
},
status
=
status
.
HTTP_415_UNSUPPORTED_MEDIA_TYPE
)
self
.
_raise_415_response
(
self
.
_content_type
)
@property
def
_parsed_media_types
(
self
):
def
_raise_415_response
(
self
,
content_type
):
"""
R
eturn a list of all the media types that this view can pars
e.
R
aise a 415 response if we cannot parse the given content typ
e.
"""
return
[
parser
.
media_type
for
parser
in
self
.
parsers
]
from
djangorestframework.response
import
ImmediateResponse
@property
def
_default_parser
(
self
):
"""
Return the view's default parser class.
"""
return
self
.
parsers
[
0
]
def
_get_parsers
(
self
):
if
hasattr
(
self
,
'_parsers'
):
return
self
.
_parsers
return
()
def
_set_parsers
(
self
,
value
):
self
.
_parsers
=
value
parsers
=
property
(
_get_parsers
,
_set_parsers
)
raise
ImmediateResponse
(
{
'error'
:
'Unsupported media type in request
\'
%
s
\'
.'
%
content_type
},
status
=
status
.
HTTP_415_UNSUPPORTED_MEDIA_TYPE
)
def
__getattr__
(
self
,
name
):
"""
When an attribute is not present on the calling instance, try to get it
from the original request.
Proxy other attributes to the underlying HttpRequest object.
"""
if
hasattr
(
self
.
request
,
name
):
return
getattr
(
self
.
request
,
name
)
else
:
return
super
(
Request
,
self
)
.
__getattribute__
(
name
)
return
getattr
(
self
.
_request
,
name
)
djangorestframework/resources.py
View file @
1cde31c8
from
django
import
forms
from
django.core.urlresolvers
import
get_urlconf
,
get_resolver
,
NoReverseMatch
from
django.db
import
models
from
djangorestframework.response
import
ImmediateResponse
from
djangorestframework.reverse
import
reverse
from
djangorestframework.serializer
import
Serializer
,
_SkipField
from
djangorestframework.utils
import
as_tuple
,
reverse
from
djangorestframework.serializer
import
Serializer
from
djangorestframework.utils
import
as_tuple
class
BaseResource
(
Serializer
):
"""
Base class for all Resource classes, which simply defines the interface they provide.
Base class for all Resource classes, which simply defines the interface
they provide.
"""
fields
=
None
include
=
None
...
...
@@ -19,11 +16,13 @@ class BaseResource(Serializer):
def
__init__
(
self
,
view
=
None
,
depth
=
None
,
stack
=
[],
**
kwargs
):
super
(
BaseResource
,
self
)
.
__init__
(
depth
,
stack
,
**
kwargs
)
self
.
view
=
view
self
.
request
=
getattr
(
view
,
'request'
,
None
)
def
validate_request
(
self
,
data
,
files
=
None
):
"""
Given the request content return the cleaned, validated content.
Typically raises a :exc:`response.ImmediateResponse` with status code 400 (Bad Request) on failure.
Typically raises a :exc:`response.ImmediateResponse` with status code
400 (Bad Request) on failure.
"""
return
data
...
...
@@ -37,7 +36,8 @@ class BaseResource(Serializer):
class
Resource
(
BaseResource
):
"""
A Resource determines how a python object maps to some serializable data.
Objects that a resource can act on include plain Python object instances, Django Models, and Django QuerySets.
Objects that a resource can act on include plain Python object instances,
Django Models, and Django QuerySets.
"""
# The model attribute refers to the Django Model which this Resource maps to.
...
...
@@ -220,9 +220,6 @@ class ModelResource(FormResource):
Also provides a :meth:`get_bound_form` method which may be used by some renderers.
"""
# Auto-register new ModelResource classes into _model_to_resource
#__metaclass__ = _RegisterModelResource
form
=
None
"""
The form class that should be used for request validation.
...
...
@@ -256,7 +253,7 @@ class ModelResource(FormResource):
The list of fields to exclude. This is only used if :attr:`fields` is not set.
"""
include
=
(
'url'
,
)
include
=
()
"""
The list of extra fields to include. This is only used if :attr:`fields` is not set.
"""
...
...
@@ -319,47 +316,6 @@ class ModelResource(FormResource):
return
form
()
def
url
(
self
,
instance
):
"""
Attempts to reverse resolve the url of the given model *instance* for this resource.
Requires a ``View`` with :class:`mixins.InstanceMixin` to have been created for this resource.
This method can be overridden if you need to set the resource url reversing explicitly.
"""
if
not
hasattr
(
self
,
'view_callable'
):
raise
_SkipField
# dis does teh magicks...
urlconf
=
get_urlconf
()
resolver
=
get_resolver
(
urlconf
)
possibilities
=
resolver
.
reverse_dict
.
getlist
(
self
.
view_callable
[
0
])
for
tuple_item
in
possibilities
:
possibility
=
tuple_item
[
0
]
# pattern = tuple_item[1]
# Note: defaults = tuple_item[2] for django >= 1.3
for
result
,
params
in
possibility
:
#instance_attrs = dict([ (param, getattr(instance, param)) for param in params if hasattr(instance, param) ])
instance_attrs
=
{}
for
param
in
params
:
if
not
hasattr
(
instance
,
param
):
continue
attr
=
getattr
(
instance
,
param
)
if
isinstance
(
attr
,
models
.
Model
):
instance_attrs
[
param
]
=
attr
.
pk
else
:
instance_attrs
[
param
]
=
attr
try
:
return
reverse
(
self
.
view_callable
[
0
],
self
.
view
.
request
,
kwargs
=
instance_attrs
)
except
NoReverseMatch
:
pass
raise
_SkipField
@property
def
_model_fields_set
(
self
):
"""
...
...
djangorestframework/response.py
View file @
1cde31c8
...
...
@@ -27,6 +27,10 @@ from djangorestframework import status
__all__
=
(
'Response'
,
'ImmediateResponse'
)
class
NotAcceptable
(
Exception
):
pass
class
Response
(
SimpleTemplateResponse
):
"""
An HttpResponse that may include content that hasn't yet been serialized.
...
...
@@ -40,25 +44,30 @@ class Response(SimpleTemplateResponse):
_ACCEPT_QUERY_PARAM
=
'_accept'
# Allow override of Accept header in URL query params
_IGNORE_IE_ACCEPT_HEADER
=
True
def
__init__
(
self
,
content
=
None
,
status
=
None
,
request
=
None
,
renderers
=
None
,
head
ers
=
None
):
def
__init__
(
self
,
content
=
None
,
status
=
None
,
headers
=
None
,
view
=
None
,
request
=
None
,
render
ers
=
None
):
# First argument taken by `SimpleTemplateResponse.__init__` is template_name,
# which we don't need
super
(
Response
,
self
)
.
__init__
(
None
,
status
=
status
)
# We need to store our content in raw content to avoid overriding HttpResponse's
# `content` property
self
.
raw_content
=
content
self
.
has_content_body
=
content
is
not
None
self
.
request
=
request
self
.
headers
=
headers
and
headers
[:]
or
[]
if
renderers
is
not
None
:
self
.
renderers
=
renderers
self
.
view
=
view
self
.
request
=
request
self
.
renderers
=
renderers
def
get_renderers
(
self
):
"""
Instantiates and returns the list of renderers the response will use.
"""
return
[
renderer
(
self
.
view
)
for
renderer
in
self
.
renderers
]
@property
def
rendered_content
(
self
):
"""
The final rendered content. Accessing this attribute triggers the complete rendering cycle :
selecting suitable renderer, setting response's actual content type, rendering data.
The final rendered content. Accessing this attribute triggers the
complete rendering cycle: selecting suitable renderer, setting
response's actual content type, rendering data.
"""
renderer
,
media_type
=
self
.
_determine_renderer
()
...
...
@@ -70,6 +79,13 @@ class Response(SimpleTemplateResponse):
return
renderer
.
render
(
self
.
raw_content
,
media_type
)
return
renderer
.
render
()
def
render
(
self
):
try
:
return
super
(
Response
,
self
)
.
render
()
except
NotAcceptable
:
response
=
self
.
_get_406_response
()
return
response
.
render
()
@property
def
status_text
(
self
):
"""
...
...
@@ -88,8 +104,6 @@ class Response(SimpleTemplateResponse):
If those are useless, a default value is returned instead.
"""
request
=
self
.
request
if
request
is
None
:
return
[
'*/*'
]
if
self
.
_ACCEPT_QUERY_PARAM
and
request
.
GET
.
get
(
self
.
_ACCEPT_QUERY_PARAM
,
None
):
# Use _accept parameter override
...
...
@@ -108,70 +122,52 @@ class Response(SimpleTemplateResponse):
def
_determine_renderer
(
self
):
"""
Determines the appropriate renderer for the output, given the list of
accepted media types,
and the :attr:`renderers` set on this class.
Determines the appropriate renderer for the output, given the list of
a
ccepted media types, a
nd the :attr:`renderers` set on this class.
Returns a 2-tuple of `(renderer, media_type)`
See: RFC 2616, Section 14 - http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
See: RFC 2616, Section 14
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
"""
renderers
=
self
.
get_renderers
()
accepts
=
self
.
_determine_accept_list
()
# Not acceptable response - Ignore accept header.
if
self
.
status_code
==
406
:
return
(
renderers
[
0
],
renderers
[
0
]
.
media_type
)
# Check the acceptable media types against each renderer,
# attempting more specific media types first
# NB. The inner loop here isn't as bad as it first looks :)
# Worst case is we're looping over len(accept_list) * len(self.renderers)
for
media_type_list
in
order_by_precedence
(
self
.
_determine_accept_list
()
):
for
renderer
in
self
.
renderers
:
for
media_type_list
in
order_by_precedence
(
accepts
):
for
renderer
in
renderers
:
for
media_type
in
media_type_list
:
if
renderer
.
can_handle_response
(
media_type
):
return
renderer
,
media_type
# No acceptable renderers were found
raise
ImmediateResponse
({
'detail'
:
'Could not satisfy the client
\'
s Accept header'
,
'available_types'
:
self
.
_rendered_media_types
},
status
=
status
.
HTTP_406_NOT_ACCEPTABLE
,
renderers
=
self
.
renderers
)
def
_get_renderers
(
self
):
if
hasattr
(
self
,
'_renderers'
):
return
self
.
_renderers
return
()
def
_set_renderers
(
self
,
value
):
self
.
_renderers
=
value
raise
NotAcceptable
renderers
=
property
(
_get_renderers
,
_set_renderers
)
@property
def
_rendered_media_types
(
self
):
"""
Return an list of all the media types that this response can render.
"""
return
[
renderer
.
media_type
for
renderer
in
self
.
renderers
]
@property
def
_rendered_formats
(
self
):
"""
Return a list of all the formats that this response can render.
"""
return
[
renderer
.
format
for
renderer
in
self
.
renderers
]
@property
def
_default_renderer
(
self
):
"""
Return the response's default renderer class.
"""
return
self
.
renderers
[
0
]
def
_get_406_response
(
self
):
renderer
=
self
.
renderers
[
0
]
return
Response
(
{
'detail'
:
'Could not satisfy the client
\'
s Accept header'
,
'available_types'
:
[
renderer
.
media_type
for
renderer
in
self
.
renderers
]
},
status
=
status
.
HTTP_406_NOT_ACCEPTABLE
,
view
=
self
.
view
,
request
=
self
.
request
,
renderers
=
[
renderer
])
class
ImmediateResponse
(
Response
,
Exception
):
"""
A subclass of :class:`Response` used to abort the current request handling.
An exception representing an Response that should be returned immediately.
Any content should be serialized as-is, without being filtered.
"""
def
__str__
(
self
):
"""
Since this class is also an exception it has to provide a sensible
representation for the cases when it is treated as an exception.
"""
return
(
'
%
s must be caught in try/except block, '
'and returned as a normal HttpResponse'
%
self
.
__class__
.
__name__
)
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
response
=
Response
(
*
args
,
**
kwargs
)
djangorestframework/reverse.py
View file @
1cde31c8
...
...
@@ -2,22 +2,19 @@
Provide reverse functions that return fully qualified URLs
"""
from
django.core.urlresolvers
import
reverse
as
django_reverse
from
django
restframework.compat
import
reverse_lazy
as
django_reverse_
lazy
from
django
.utils.functional
import
lazy
def
reverse
(
viewname
,
request
,
*
args
,
**
kwargs
):
def
reverse
(
viewname
,
*
args
,
**
kwargs
):
"""
Do the same as `django.core.urlresolvers.reverse` but using
*request* to build a fully qualified
URL.
Same as `django.core.urlresolvers.reverse`, but optionally takes a request
and returns a fully qualified URL, using the request to get the base
URL.
"""
request
=
kwargs
.
pop
(
'request'
,
None
)
url
=
django_reverse
(
viewname
,
*
args
,
**
kwargs
)
return
request
.
build_absolute_uri
(
url
)
if
request
:
return
request
.
build_absolute_uri
(
url
)
return
url
def
reverse_lazy
(
viewname
,
request
,
*
args
,
**
kwargs
):
"""
Do the same as `django.core.urlresolvers.reverse_lazy` but using
*request* to build a fully qualified URL.
"""
url
=
django_reverse_lazy
(
viewname
,
*
args
,
**
kwargs
)
return
request
.
build_absolute_uri
(
url
)
reverse_lazy
=
lazy
(
reverse
,
str
)
djangorestframework/runtests/settings.py
View file @
1cde31c8
...
...
@@ -53,11 +53,6 @@ MEDIA_ROOT = ''
# Examples: "http://media.lawrence.com", "http://example.com/media/"
MEDIA_URL
=
''
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
# Examples: "http://foo.com/media/", "/media/".
ADMIN_MEDIA_PREFIX
=
'/media/'
# Make this unique, and don't share it with anybody.
SECRET_KEY
=
'u@x-aj9(hoh#rb-^ymf#g2jx_hp0vj7u5#b@ag1n^seu9e!
%
cy'
...
...
djangorestframework/templates/djangorestframework/base.html
View file @
1cde31c8
...
...
@@ -20,8 +20,15 @@
<h1
id=
"site-name"
>
{% block branding %}
<a
href=
'http://django-rest-framework.org'
>
Django REST framework
</a>
<span
class=
"version"
>
v {{ version }}
</span>
{% endblock %}
</h1>
</div>
<div
id=
"user-tools"
>
{% if user.is_active %}Welcome, {{ user }}.{% if logout_url %}
<a
href=
'{{ logout_url }}'
>
Log out
</a>
{% endif %}{% else %}Anonymous {% if login_url %}
<a
href=
'{{ login_url }}'
>
Log in
</a>
{% endif %}{% endif %}
{% block userlinks %}{% endblock %}
{% block userlinks %}
{% if user.is_active %}
Welcome, {{ user }}.
<a
href=
'{% url djangorestframework:logout %}?next={{ request.path }}'
>
Log out
</a>
{% else %}
Anonymous
<a
href=
'{% url djangorestframework:login %}?next={{ request.path }}'
>
Log in
</a>
{% endif %}
{% endblock %}
</div>
{% block nav-global %}{% endblock %}
</div>
...
...
djangorestframework/templates/djangorestframework/login.html
View file @
1cde31c8
...
...
@@ -17,7 +17,7 @@
<div
id=
"content"
class=
"colM"
>
<div
id=
"content-main"
>
<form
method=
"post"
action=
"{% url djangorestframework
.utils.staticviews.api_
login %}"
id=
"login-form"
>
<form
method=
"post"
action=
"{% url djangorestframework
:
login %}"
id=
"login-form"
>
{% csrf_token %}
<div
class=
"form-row"
>
<label
for=
"id_username"
>
Username:
</label>
{{ form.username }}
...
...
djangorestframework/tests/__init__.py
View file @
1cde31c8
...
...
@@ -10,4 +10,3 @@ for module in modules:
exec
(
"from djangorestframework.tests.
%
s import __doc__ as module_doc"
%
module
)
exec
(
"from djangorestframework.tests.
%
s import *"
%
module
)
__test__
[
module
]
=
module_doc
or
""
djangorestframework/tests/accept.py
View file @
1cde31c8
from
django.conf.urls.defaults
import
patterns
,
url
,
include
from
django.test
import
TestCase
from
djangorestframework.compat
import
RequestFactory
...
...
@@ -15,9 +16,19 @@ SAFARI_5_0_USER_AGENT = 'Mozilla/5.0 (X11; U; Linux x86_64; en-ca) AppleWebKit/5
OPERA_11_0_MSIE_USER_AGENT
=
'Mozilla/4.0 (compatible; MSIE 8.0; X11; Linux x86_64; pl) Opera 11.00'
OPERA_11_0_OPERA_USER_AGENT
=
'Opera/9.80 (X11; Linux x86_64; U; pl) Presto/2.7.62 Version/11.00'
urlpatterns
=
patterns
(
''
,
url
(
r'^api'
,
include
(
'djangorestframework.urls'
,
namespace
=
'djangorestframework'
))
)
class
UserAgentMungingTest
(
TestCase
):
"""We need to fake up the accept headers when we deal with MSIE. Blergh.
http://www.gethifi.com/blog/browser-rest-http-accept-headers"""
"""
We need to fake up the accept headers when we deal with MSIE. Blergh.
http://www.gethifi.com/blog/browser-rest-http-accept-headers
"""
urls
=
'djangorestframework.tests.accept'
def
setUp
(
self
):
...
...
djangorestframework/tests/modelviews.py
View file @
1cde31c8
from
django.conf.urls.defaults
import
patterns
,
url
from
django.test
import
TestCase
from
django.forms
import
ModelForm
from
django.contrib.auth.models
import
Group
,
User
from
djangorestframework.resources
import
ModelResource
...
...
@@ -7,18 +6,22 @@ from djangorestframework.views import ListOrCreateModelView, InstanceModelView
from
djangorestframework.tests.models
import
CustomUser
from
djangorestframework.tests.testcases
import
TestModelsTestCase
class
GroupResource
(
ModelResource
):
model
=
Group
class
UserForm
(
ModelForm
):
class
Meta
:
model
=
User
exclude
=
(
'last_login'
,
'date_joined'
)
class
UserResource
(
ModelResource
):
model
=
User
form
=
UserForm
class
CustomUserResource
(
ModelResource
):
model
=
CustomUser
...
...
djangorestframework/tests/oauthentication.py
View file @
1cde31c8
...
...
@@ -27,7 +27,7 @@ else:
urlpatterns
=
patterns
(
''
,
url
(
r'^$'
,
oauth_required
(
ClientView
.
as_view
())),
url
(
r'^oauth/'
,
include
(
'oauth_provider.urls'
)),
url
(
r'^
accounts/login/$'
,
'djangorestframework.utils.staticviews.api_login'
),
url
(
r'^
restframework/'
,
include
(
'djangorestframework.urls'
,
namespace
=
'djangorestframework'
)
),
)
...
...
djangorestframework/tests/parsers.py
View file @
1cde31c8
...
...
@@ -132,17 +132,18 @@
# self.assertEqual(files['file1'].read(), 'blablabla')
from
StringIO
import
StringIO
from
cgi
import
parse_qs
from
django
import
forms
from
django.test
import
TestCase
from
djangorestframework.parsers
import
FormParser
from
djangorestframework.parsers
import
XMLParser
import
datetime
class
Form
(
forms
.
Form
):
field1
=
forms
.
CharField
(
max_length
=
3
)
field2
=
forms
.
CharField
()
class
TestFormParser
(
TestCase
):
def
setUp
(
self
):
self
.
string
=
"field1=abc&field2=defghijk"
...
...
@@ -152,10 +153,11 @@ class TestFormParser(TestCase):
parser
=
FormParser
(
None
)
stream
=
StringIO
(
self
.
string
)
(
data
,
files
)
=
parser
.
parse
(
stream
)
(
data
,
files
)
=
parser
.
parse
(
stream
,
{},
[]
)
self
.
assertEqual
(
Form
(
data
)
.
is_valid
(),
True
)
class
TestXMLParser
(
TestCase
):
def
setUp
(
self
):
self
.
_input
=
StringIO
(
...
...
@@ -163,13 +165,13 @@ class TestXMLParser(TestCase):
'<root>'
'<field_a>121.0</field_a>'
'<field_b>dasd</field_b>'
'<field_c></field_c>'
'<field_c></field_c>'
'<field_d>2011-12-25 12:45:00</field_d>'
'</root>'
)
self
.
_data
=
{
)
self
.
_data
=
{
'field_a'
:
121
,
'field_b'
:
'dasd'
,
'field_b'
:
'dasd'
,
'field_c'
:
None
,
'field_d'
:
datetime
.
datetime
(
2011
,
12
,
25
,
12
,
45
,
00
)
}
...
...
@@ -183,21 +185,21 @@ class TestXMLParser(TestCase):
'</sub_data_list>'
'<name>name</name>'
'</root>'
)
)
self
.
_complex_data
=
{
"creation_date"
:
datetime
.
datetime
(
2011
,
12
,
25
,
12
,
45
,
00
),
"name"
:
"name"
,
"creation_date"
:
datetime
.
datetime
(
2011
,
12
,
25
,
12
,
45
,
00
),
"name"
:
"name"
,
"sub_data_list"
:
[
{
"sub_id"
:
1
,
"sub_id"
:
1
,
"sub_name"
:
"first"
},
},
{
"sub_id"
:
2
,
"sub_id"
:
2
,
"sub_name"
:
"second"
}
]
}
}
def
test_parse
(
self
):
parser
=
XMLParser
(
None
)
...
...
djangorestframework/tests/renderers.py
View file @
1cde31c8
import
re
from
django.conf.urls.defaults
import
patterns
,
url
,
include
from
django.test
import
TestCase
from
django.conf.urls.defaults
import
patterns
,
url
from
django.test
import
TestCase
from
djangorestframework
import
status
from
djangorestframework.compat
import
View
as
DjangoView
from
djangorestframework.response
import
Response
from
djangorestframework.mixins
import
ResponseMixin
from
djangorestframework.views
import
View
from
djangorestframework.renderers
import
BaseRenderer
,
JSONRenderer
,
YAMLRenderer
,
\
XMLRenderer
,
JSONPRenderer
,
DocumentingHTMLRenderer
from
djangorestframework.parsers
import
JSONParser
,
YAMLParser
,
XMLParser
from
djangorestframework.parsers
import
YAMLParser
,
XMLParser
from
StringIO
import
StringIO
import
datetime
from
decimal
import
Decimal
DUMMYSTATUS
=
status
.
HTTP_200_OK
DUMMYCONTENT
=
'dummycontent'
RENDERER_A_SERIALIZER
=
lambda
x
:
'Renderer A:
%
s'
%
x
RENDERER_B_SERIALIZER
=
lambda
x
:
'Renderer B:
%
s'
%
x
class
RendererA
(
BaseRenderer
):
media_type
=
'mock/renderera'
format
=
"formata"
def
render
(
self
,
obj
=
None
,
media_type
=
None
):
return
RENDERER_A_SERIALIZER
(
obj
)
class
RendererB
(
BaseRenderer
):
media_type
=
'mock/rendererb'
format
=
"formatb"
def
render
(
self
,
obj
=
None
,
media_type
=
None
):
return
RENDERER_B_SERIALIZER
(
obj
)
class
MockView
(
ResponseMixin
,
DjangoView
):
renderers
=
(
RendererA
,
RendererB
)
def
get
(
self
,
request
,
**
kwargs
):
response
=
Response
(
DUMMYSTATUS
,
DUMMYCONTENT
)
return
self
.
render
(
response
)
class
MockGETView
(
View
):
def
get
(
self
,
request
,
**
kwargs
):
return
{
'foo'
:
[
'bar'
,
'baz'
]}
class
HTMLView
(
View
):
renderers
=
(
DocumentingHTMLRenderer
,
)
def
get
(
self
,
request
,
**
kwargs
):
return
'text'
class
HTMLView1
(
View
):
renderers
=
(
DocumentingHTMLRenderer
,
JSONRenderer
)
def
get
(
self
,
request
,
**
kwargs
):
return
'text'
urlpatterns
=
patterns
(
''
,
url
(
r'^.*\.(?P<format>.+)$'
,
MockView
.
as_view
(
renderers
=
[
RendererA
,
RendererB
])),
url
(
r'^$'
,
MockView
.
as_view
(
renderers
=
[
RendererA
,
RendererB
])),
url
(
r'^jsonp/jsonrenderer$'
,
MockGETView
.
as_view
(
renderers
=
[
JSONRenderer
,
JSONPRenderer
])),
url
(
r'^jsonp/nojsonrenderer$'
,
MockGETView
.
as_view
(
renderers
=
[
JSONPRenderer
])),
url
(
r'^html$'
,
HTMLView
.
as_view
()),
url
(
r'^html1$'
,
HTMLView1
.
as_view
()),
url
(
r'^api'
,
include
(
'djangorestframework.urls'
,
namespace
=
'djangorestframework'
))
)
class
RendererIntegrationTests
(
TestCase
):
"""
End-to-end testing of renderers using an RendererMixin on a generic view.
"""
urls
=
'djangorestframework.tests.renderers'
def
test_default_renderer_serializes_content
(
self
):
"""If the Accept header is not set the default renderer should serialize the response."""
resp
=
self
.
client
.
get
(
'/'
)
self
.
assertEquals
(
resp
[
'Content-Type'
],
RendererA
.
media_type
)
self
.
assertEquals
(
resp
.
content
,
RENDERER_A_SERIALIZER
(
DUMMYCONTENT
))
self
.
assertEquals
(
resp
.
status_code
,
DUMMYSTATUS
)
def
test_head_method_serializes_no_content
(
self
):
"""No response must be included in HEAD requests."""
resp
=
self
.
client
.
head
(
'/'
)
self
.
assertEquals
(
resp
.
status_code
,
DUMMYSTATUS
)
self
.
assertEquals
(
resp
[
'Content-Type'
],
RendererA
.
media_type
)
self
.
assertEquals
(
resp
.
content
,
''
)
def
test_default_renderer_serializes_content_on_accept_any
(
self
):
"""If the Accept header is set to */* the default renderer should serialize the response."""
resp
=
self
.
client
.
get
(
'/'
,
HTTP_ACCEPT
=
'*/*'
)
self
.
assertEquals
(
resp
[
'Content-Type'
],
RendererA
.
media_type
)
self
.
assertEquals
(
resp
.
content
,
RENDERER_A_SERIALIZER
(
DUMMYCONTENT
))
self
.
assertEquals
(
resp
.
status_code
,
DUMMYSTATUS
)
def
test_specified_renderer_serializes_content_default_case
(
self
):
"""If the Accept header is set the specified renderer should serialize the response.
(In this case we check that works for the default renderer)"""
resp
=
self
.
client
.
get
(
'/'
,
HTTP_ACCEPT
=
RendererA
.
media_type
)
self
.
assertEquals
(
resp
[
'Content-Type'
],
RendererA
.
media_type
)
self
.
assertEquals
(
resp
.
content
,
RENDERER_A_SERIALIZER
(
DUMMYCONTENT
))
self
.
assertEquals
(
resp
.
status_code
,
DUMMYSTATUS
)
def
test_specified_renderer_serializes_content_non_default_case
(
self
):
"""If the Accept header is set the specified renderer should serialize the response.
(In this case we check that works for a non-default renderer)"""
resp
=
self
.
client
.
get
(
'/'
,
HTTP_ACCEPT
=
RendererB
.
media_type
)
self
.
assertEquals
(
resp
[
'Content-Type'
],
RendererB
.
media_type
)
self
.
assertEquals
(
resp
.
content
,
RENDERER_B_SERIALIZER
(
DUMMYCONTENT
))
self
.
assertEquals
(
resp
.
status_code
,
DUMMYSTATUS
)
def
test_specified_renderer_serializes_content_on_accept_query
(
self
):
"""The '_accept' query string should behave in the same way as the Accept header."""
resp
=
self
.
client
.
get
(
'/?_accept=
%
s'
%
RendererB
.
media_type
)
self
.
assertEquals
(
resp
[
'Content-Type'
],
RendererB
.
media_type
)
self
.
assertEquals
(
resp
.
content
,
RENDERER_B_SERIALIZER
(
DUMMYCONTENT
))
self
.
assertEquals
(
resp
.
status_code
,
DUMMYSTATUS
)
def
test_unsatisfiable_accept_header_on_request_returns_406_status
(
self
):
"""If the Accept header is unsatisfiable we should return a 406 Not Acceptable response."""
resp
=
self
.
client
.
get
(
'/'
,
HTTP_ACCEPT
=
'foo/bar'
)
self
.
assertEquals
(
resp
.
status_code
,
status
.
HTTP_406_NOT_ACCEPTABLE
)
def
test_specified_renderer_serializes_content_on_format_query
(
self
):
"""If a 'format' query is specified, the renderer with the matching
format attribute should serialize the response."""
resp
=
self
.
client
.
get
(
'/?format=
%
s'
%
RendererB
.
format
)
self
.
assertEquals
(
resp
[
'Content-Type'
],
RendererB
.
media_type
)
self
.
assertEquals
(
resp
.
content
,
RENDERER_B_SERIALIZER
(
DUMMYCONTENT
))
self
.
assertEquals
(
resp
.
status_code
,
DUMMYSTATUS
)
def
test_specified_renderer_serializes_content_on_format_kwargs
(
self
):
"""If a 'format' keyword arg is specified, the renderer with the matching
format attribute should serialize the response."""
resp
=
self
.
client
.
get
(
'/something.formatb'
)
self
.
assertEquals
(
resp
[
'Content-Type'
],
RendererB
.
media_type
)
self
.
assertEquals
(
resp
.
content
,
RENDERER_B_SERIALIZER
(
DUMMYCONTENT
))
self
.
assertEquals
(
resp
.
status_code
,
DUMMYSTATUS
)
def
test_specified_renderer_is_used_on_format_query_with_matching_accept
(
self
):
"""If both a 'format' query and a matching Accept header specified,
the renderer with the matching format attribute should serialize the response."""
resp
=
self
.
client
.
get
(
'/?format=
%
s'
%
RendererB
.
format
,
HTTP_ACCEPT
=
RendererB
.
media_type
)
self
.
assertEquals
(
resp
[
'Content-Type'
],
RendererB
.
media_type
)
self
.
assertEquals
(
resp
.
content
,
RENDERER_B_SERIALIZER
(
DUMMYCONTENT
))
self
.
assertEquals
(
resp
.
status_code
,
DUMMYSTATUS
)
def
test_conflicting_format_query_and_accept_ignores_accept
(
self
):
"""If a 'format' query is specified that does not match the Accept
header, we should only honor the 'format' query string."""
resp
=
self
.
client
.
get
(
'/?format=
%
s'
%
RendererB
.
format
,
HTTP_ACCEPT
=
'dummy'
)
self
.
assertEquals
(
resp
[
'Content-Type'
],
RendererB
.
media_type
)
self
.
assertEquals
(
resp
.
content
,
RENDERER_B_SERIALIZER
(
DUMMYCONTENT
))
self
.
assertEquals
(
resp
.
status_code
,
DUMMYSTATUS
)
def
test_bla
(
self
):
# What the f***?
resp
=
self
.
client
.
get
(
'/?format=formatb'
,
HTTP_ACCEPT
=
'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
)
self
.
assertEquals
(
resp
[
'Content-Type'
],
RendererB
.
media_type
)
self
.
assertEquals
(
resp
.
content
,
RENDERER_B_SERIALIZER
(
DUMMYCONTENT
))
self
.
assertEquals
(
resp
.
status_code
,
DUMMYSTATUS
)
_flat_repr
=
'{"foo": ["bar", "baz"]}'
_indented_repr
=
'{
\n
"foo": [
\n
"bar",
\n
"baz"
\n
]
\n
}'
...
...
@@ -27,6 +186,7 @@ def strip_trailing_whitespace(content):
"""
return
re
.
sub
(
' +
\n
'
,
'
\n
'
,
content
)
class
JSONRendererTests
(
TestCase
):
"""
Tests specific to the JSON Renderer
...
...
@@ -51,30 +211,16 @@ class JSONRendererTests(TestCase):
content
=
renderer
.
render
(
obj
,
'application/json; indent=2'
)
self
.
assertEquals
(
strip_trailing_whitespace
(
content
),
_indented_repr
)
def
test_render_and_parse
(
self
):
"""
Test rendering and then parsing returns the original object.
IE obj -> render -> parse -> obj.
"""
obj
=
{
'foo'
:
[
'bar'
,
'baz'
]}
renderer
=
JSONRenderer
(
None
)
parser
=
JSONParser
(
None
)
content
=
renderer
.
render
(
obj
,
'application/json'
)
(
data
,
files
)
=
parser
.
parse
(
StringIO
(
content
))
self
.
assertEquals
(
obj
,
data
)
class
MockGETView
(
View
):
def
get
(
self
,
request
,
**
kwargs
):
def
get
(
self
,
request
,
*
args
,
*
*
kwargs
):
return
Response
({
'foo'
:
[
'bar'
,
'baz'
]})
urlpatterns
=
patterns
(
''
,
url
(
r'^jsonp/jsonrenderer$'
,
MockGETView
.
as_view
(
renderer
_classe
s
=
[
JSONRenderer
,
JSONPRenderer
])),
url
(
r'^jsonp/nojsonrenderer$'
,
MockGETView
.
as_view
(
renderer
_classe
s
=
[
JSONPRenderer
])),
url
(
r'^jsonp/jsonrenderer$'
,
MockGETView
.
as_view
(
renderers
=
[
JSONRenderer
,
JSONPRenderer
])),
url
(
r'^jsonp/nojsonrenderer$'
,
MockGETView
.
as_view
(
renderers
=
[
JSONPRenderer
])),
)
...
...
@@ -149,22 +295,21 @@ if YAMLRenderer:
self
.
assertEquals
(
obj
,
data
)
class
XMLRendererTestCase
(
TestCase
):
"""
Tests specific to the XML Renderer
"""
_complex_data
=
{
"creation_date"
:
datetime
.
datetime
(
2011
,
12
,
25
,
12
,
45
,
00
),
"name"
:
"name"
,
"creation_date"
:
datetime
.
datetime
(
2011
,
12
,
25
,
12
,
45
,
00
),
"name"
:
"name"
,
"sub_data_list"
:
[
{
"sub_id"
:
1
,
"sub_id"
:
1
,
"sub_name"
:
"first"
},
},
{
"sub_id"
:
2
,
"sub_id"
:
2
,
"sub_name"
:
"second"
}
]
...
...
@@ -219,12 +364,12 @@ class XMLRendererTestCase(TestCase):
renderer
=
XMLRenderer
(
None
)
content
=
renderer
.
render
({
'field'
:
None
},
'application/xml'
)
self
.
assertXMLContains
(
content
,
'<field></field>'
)
def
test_render_complex_data
(
self
):
"""
Test XML rendering.
"""
renderer
=
XMLRenderer
(
None
)
renderer
=
XMLRenderer
(
None
)
content
=
renderer
.
render
(
self
.
_complex_data
,
'application/xml'
)
self
.
assertXMLContains
(
content
,
'<sub_name>first</sub_name>'
)
self
.
assertXMLContains
(
content
,
'<sub_name>second</sub_name>'
)
...
...
@@ -233,9 +378,9 @@ class XMLRendererTestCase(TestCase):
"""
Test XML rendering.
"""
renderer
=
XMLRenderer
(
None
)
renderer
=
XMLRenderer
(
None
)
content
=
StringIO
(
renderer
.
render
(
self
.
_complex_data
,
'application/xml'
))
parser
=
XMLParser
(
None
)
complex_data_out
,
dummy
=
parser
.
parse
(
content
)
error_msg
=
"complex data differs!IN:
\n
%
s
\n\n
OUT:
\n
%
s"
%
(
repr
(
self
.
_complex_data
),
repr
(
complex_data_out
))
...
...
@@ -245,4 +390,3 @@ class XMLRendererTestCase(TestCase):
self
.
assertTrue
(
xml
.
startswith
(
'<?xml version="1.0" encoding="utf-8"?>
\n
<root>'
))
self
.
assertTrue
(
xml
.
endswith
(
'</root>'
))
self
.
assertTrue
(
string
in
xml
,
'
%
r not in
%
r'
%
(
string
,
xml
))
djangorestframework/tests/request.py
View file @
1cde31c8
...
...
@@ -4,205 +4,214 @@ Tests for content parsing, and form-overloaded content parsing.
from
django.conf.urls.defaults
import
patterns
from
django.contrib.auth.models
import
User
from
django.test
import
TestCase
,
Client
from
django.utils
import
simplejson
as
json
from
djangorestframework
import
status
from
djangorestframework.authentication
import
UserLoggedInAuthentication
from
djangorestframework.compat
import
RequestFactory
from
djangorestframework.mixins
import
RequestMixin
from
djangorestframework.parsers
import
FormParser
,
MultiPartParser
,
\
PlainTextParser
,
JSONParser
from
djangorestframework.utils
import
RequestFactory
from
djangorestframework.parsers
import
(
FormParser
,
MultiPartParser
,
PlainTextParser
,
JSONParser
)
from
djangorestframework.request
import
Request
from
djangorestframework.response
import
Response
from
djangorestframework.request
import
Request
from
djangorestframework.views
import
View
class
RequestTestCase
(
TestCase
):
def
build_request
(
self
,
method
,
*
args
,
**
kwargs
):
factory
=
RequestFactory
()
method
=
getattr
(
factory
,
method
)
original_request
=
method
(
*
args
,
**
kwargs
)
return
Request
(
original_request
)
factory
=
RequestFactory
()
class
TestMethodOverloading
(
RequestTestCase
):
def
test_standard_behaviour_determines_GET
(
self
):
"""GET requests identified"""
request
=
self
.
build_request
(
'get'
,
'/'
)
class
TestMethodOverloading
(
TestCase
):
def
test_GET_method
(
self
):
"""
GET requests identified.
"""
request
=
factory
.
get
(
'/'
)
self
.
assertEqual
(
request
.
method
,
'GET'
)
def
test_standard_behaviour_determines_POST
(
self
):
"""POST requests identified"""
request
=
self
.
build_request
(
'post'
,
'/'
)
def
test_POST_method
(
self
):
"""
POST requests identified.
"""
request
=
factory
.
post
(
'/'
)
self
.
assertEqual
(
request
.
method
,
'POST'
)
def
test_overloaded_POST_behaviour_determines_overloaded_method
(
self
):
"""POST requests can be overloaded to another method by setting a reserved form field"""
request
=
self
.
build_request
(
'post'
,
'/'
,
{
Request
.
_METHOD_PARAM
:
'DELETE'
})
self
.
assertEqual
(
request
.
method
,
'DELETE'
)
def
test_HEAD_is_a_valid_method
(
self
):
"""HEAD requests identified"""
request
=
request
=
self
.
build_request
(
'head'
,
'/'
)
def
test_HEAD_method
(
self
):
"""
HEAD requests identified.
"""
request
=
factory
.
head
(
'/'
)
self
.
assertEqual
(
request
.
method
,
'HEAD'
)
def
test_overloaded_method
(
self
):
"""
POST requests can be overloaded to another method by setting a
reserved form field
"""
request
=
factory
.
post
(
'/'
,
{
Request
.
_METHOD_PARAM
:
'DELETE'
})
self
.
assertEqual
(
request
.
method
,
'DELETE'
)
class
TestContentParsing
(
RequestTestCase
):
def
build_request
(
self
,
method
,
*
args
,
**
kwargs
):
factory
=
RequestFactory
()
parsers
=
kwargs
.
pop
(
'parsers'
,
None
)
method
=
getattr
(
factory
,
method
)
original_request
=
method
(
*
args
,
**
kwargs
)
rkwargs
=
{}
if
parsers
is
not
None
:
rkwargs
[
'parsers'
]
=
parsers
request
=
Request
(
original_request
,
**
rkwargs
)
# TODO: Just a hack because the parsers need a view. This will be fixed in the future
class
Obj
(
object
):
pass
obj
=
Obj
()
obj
.
request
=
request
for
p
in
request
.
parsers
:
p
.
view
=
obj
return
request
class
TestContentParsing
(
TestCase
):
def
test_standard_behaviour_determines_no_content_GET
(
self
):
"""Ensure request.DATA returns None for GET request with no content."""
request
=
self
.
build_request
(
'get'
,
'/'
)
"""
Ensure request.DATA returns None for GET request with no content.
"""
request
=
factory
.
get
(
'/'
)
self
.
assertEqual
(
request
.
DATA
,
None
)
def
test_standard_behaviour_determines_no_content_HEAD
(
self
):
"""Ensure request.DATA returns None for HEAD request."""
request
=
self
.
build_request
(
'head'
,
'/'
)
"""
Ensure request.DATA returns None for HEAD request.
"""
request
=
factory
.
head
(
'/'
)
self
.
assertEqual
(
request
.
DATA
,
None
)
def
test_standard_behaviour_determines_form_content_POST
(
self
):
"""Ensure request.DATA returns content for POST request with form content."""
form_data
=
{
'qwerty'
:
'uiop'
}
parsers
=
(
FormParser
(),
MultiPartParser
())
request
=
self
.
build_request
(
'post'
,
'/'
,
data
=
form_data
,
parsers
=
parsers
)
self
.
assertEqual
(
request
.
DATA
.
items
(),
form_data
.
items
())
"""
Ensure request.DATA returns content for POST request with form content.
"""
data
=
{
'qwerty'
:
'uiop'
}
parsers
=
(
FormParser
,
MultiPartParser
)
request
=
factory
.
post
(
'/'
,
data
,
parser
=
parsers
)
self
.
assertEqual
(
request
.
DATA
.
items
(),
data
.
items
())
def
test_standard_behaviour_determines_non_form_content_POST
(
self
):
"""Ensure request.DATA returns content for POST request with non-form content."""
"""
Ensure request.DATA returns content for POST request with
non-form content.
"""
content
=
'qwerty'
content_type
=
'text/plain'
parsers
=
(
PlainTextParser
()
,)
request
=
self
.
build_request
(
'post'
,
'/'
,
content
,
content_type
=
content_type
,
parsers
=
parsers
)
parsers
=
(
PlainTextParser
,)
request
=
factory
.
post
(
'/'
,
content
,
content_type
=
content_type
,
parsers
=
parsers
)
self
.
assertEqual
(
request
.
DATA
,
content
)
def
test_standard_behaviour_determines_form_content_PUT
(
self
):
"""Ensure request.DATA returns content for PUT request with form content."""
form_data
=
{
'qwerty'
:
'uiop'
}
parsers
=
(
FormParser
(),
MultiPartParser
())
request
=
self
.
build_request
(
'put'
,
'/'
,
data
=
form_data
,
parsers
=
parsers
)
self
.
assertEqual
(
request
.
DATA
.
items
(),
form_data
.
items
())
"""
Ensure request.DATA returns content for PUT request with form content.
"""
data
=
{
'qwerty'
:
'uiop'
}
parsers
=
(
FormParser
,
MultiPartParser
)
request
=
factory
.
put
(
'/'
,
data
,
parsers
=
parsers
)
self
.
assertEqual
(
request
.
DATA
.
items
(),
data
.
items
())
def
test_standard_behaviour_determines_non_form_content_PUT
(
self
):
"""Ensure request.DATA returns content for PUT request with non-form content."""
"""
Ensure request.DATA returns content for PUT request with
non-form content.
"""
content
=
'qwerty'
content_type
=
'text/plain'
parsers
=
(
PlainTextParser
(),
)
request
=
self
.
build_request
(
'put'
,
'/'
,
content
,
content_type
=
content_type
,
parsers
=
parsers
)
parsers
=
(
PlainTextParser
,
)
request
=
factory
.
put
(
'/'
,
content
,
content_type
=
content_type
,
parsers
=
parsers
)
self
.
assertEqual
(
request
.
DATA
,
content
)
def
test_overloaded_behaviour_allows_content_tunnelling
(
self
):
"""Ensure request.DATA returns content for overloaded POST request"""
"""
Ensure request.DATA returns content for overloaded POST request.
"""
content
=
'qwerty'
content_type
=
'text/plain'
form_data
=
{
Request
.
_CONTENT_PARAM
:
content
,
Request
.
_CONTENTTYPE_PARAM
:
content_type
}
parsers
=
(
PlainTextParser
(),)
request
=
self
.
build_request
(
'post'
,
'/'
,
form_data
,
parsers
=
parsers
)
data
=
{
Request
.
_CONTENT_PARAM
:
content
,
Request
.
_CONTENTTYPE_PARAM
:
content_type
}
parsers
=
(
PlainTextParser
,
)
request
=
factory
.
post
(
'/'
,
data
,
parsers
=
parsers
)
self
.
assertEqual
(
request
.
DATA
,
content
)
def
test_accessing_post_after_data_form
(
self
):
"""Ensures request.POST can be accessed after request.DATA in form request"""
form_data
=
{
'qwerty'
:
'uiop'
}
parsers
=
(
FormParser
(),
MultiPartParser
())
request
=
self
.
build_request
(
'post'
,
'/'
,
data
=
form_data
)
self
.
assertEqual
(
request
.
DATA
.
items
(),
form_data
.
items
())
self
.
assertEqual
(
request
.
POST
.
items
(),
form_data
.
items
())
"""
Ensures request.POST can be accessed after request.DATA in
form request.
"""
data
=
{
'qwerty'
:
'uiop'
}
request
=
factory
.
post
(
'/'
,
data
=
data
)
self
.
assertEqual
(
request
.
DATA
.
items
(),
data
.
items
())
self
.
assertEqual
(
request
.
POST
.
items
(),
data
.
items
())
def
test_accessing_post_after_data_for_json
(
self
):
"""Ensures request.POST can be accessed after request.DATA in json request"""
from
django.utils
import
simplejson
as
json
"""
Ensures request.POST can be accessed after request.DATA in
json request.
"""
data
=
{
'qwerty'
:
'uiop'
}
content
=
json
.
dumps
(
data
)
content_type
=
'application/json'
parsers
=
(
JSONParser
(),
)
parsers
=
(
JSONParser
,
)
request
=
self
.
build_request
(
'post'
,
'/'
,
content
,
content_type
=
content_type
,
parsers
=
parsers
)
request
=
factory
.
post
(
'/'
,
content
,
content_type
=
content_type
,
parsers
=
parsers
)
self
.
assertEqual
(
request
.
DATA
.
items
(),
data
.
items
())
self
.
assertEqual
(
request
.
POST
.
items
(),
[])
def
test_accessing_post_after_data_for_overloaded_json
(
self
):
"""Ensures request.POST can be accessed after request.DATA in overloaded json request"""
from
django.utils
import
simplejson
as
json
"""
Ensures request.POST can be accessed after request.DATA in overloaded
json request.
"""
data
=
{
'qwerty'
:
'uiop'
}
content
=
json
.
dumps
(
data
)
content_type
=
'application/json'
parsers
=
(
JSONParser
(),
)
parsers
=
(
JSONParser
,
)
form_data
=
{
Request
.
_CONTENT_PARAM
:
content
,
Request
.
_CONTENTTYPE_PARAM
:
content_type
}
request
=
self
.
build_request
(
'post'
,
'/'
,
data
=
form_data
,
parsers
=
parsers
)
request
=
factory
.
post
(
'/'
,
form_data
,
parsers
=
parsers
)
self
.
assertEqual
(
request
.
DATA
.
items
(),
data
.
items
())
self
.
assertEqual
(
request
.
POST
.
items
(),
form_data
.
items
())
def
test_accessing_data_after_post_form
(
self
):
"""Ensures request.DATA can be accessed after request.POST in form request"""
form_data
=
{
'qwerty'
:
'uiop'
}
"""
Ensures request.DATA can be accessed after request.POST in
form request.
"""
data
=
{
'qwerty'
:
'uiop'
}
parsers
=
(
FormParser
,
MultiPartParser
)
request
=
self
.
build_request
(
'post'
,
'/'
,
data
=
form_
data
,
parsers
=
parsers
)
request
=
factory
.
post
(
'/'
,
data
,
parsers
=
parsers
)
self
.
assertEqual
(
request
.
POST
.
items
(),
form_
data
.
items
())
self
.
assertEqual
(
request
.
DATA
.
items
(),
form_
data
.
items
())
self
.
assertEqual
(
request
.
POST
.
items
(),
data
.
items
())
self
.
assertEqual
(
request
.
DATA
.
items
(),
data
.
items
())
def
test_accessing_data_after_post_for_json
(
self
):
"""Ensures request.DATA can be accessed after request.POST in json request"""
from
django.utils
import
simplejson
as
json
"""
Ensures request.DATA can be accessed after request.POST in
json request.
"""
data
=
{
'qwerty'
:
'uiop'
}
content
=
json
.
dumps
(
data
)
content_type
=
'application/json'
parsers
=
(
JSONParser
(),)
request
=
self
.
build_request
(
'post'
,
'/'
,
content
,
content_type
=
content_type
,
parsers
=
parsers
)
post_items
=
request
.
POST
.
items
()
self
.
assertEqual
(
len
(
post_items
),
1
)
self
.
assertEqual
(
len
(
post_items
[
0
]),
2
)
self
.
assertEqual
(
post_items
[
0
][
0
],
content
)
parsers
=
(
JSONParser
,
)
request
=
factory
.
post
(
'/'
,
content
,
content_type
=
content_type
,
parsers
=
parsers
)
self
.
assertEqual
(
request
.
POST
.
items
(),
[])
self
.
assertEqual
(
request
.
DATA
.
items
(),
data
.
items
())
def
test_accessing_data_after_post_for_overloaded_json
(
self
):
"""Ensures request.DATA can be accessed after request.POST in overloaded json request"""
from
django.utils
import
simplejson
as
json
"""
Ensures request.DATA can be accessed after request.POST in overloaded
json request
"""
data
=
{
'qwerty'
:
'uiop'
}
content
=
json
.
dumps
(
data
)
content_type
=
'application/json'
parsers
=
(
JSONParser
(),
)
parsers
=
(
JSONParser
,
)
form_data
=
{
Request
.
_CONTENT_PARAM
:
content
,
Request
.
_CONTENTTYPE_PARAM
:
content_type
}
request
=
self
.
build_request
(
'post'
,
'/'
,
data
=
form_data
,
parsers
=
parsers
)
request
=
factory
.
post
(
'/'
,
form_data
,
parsers
=
parsers
)
self
.
assertEqual
(
request
.
POST
.
items
(),
form_data
.
items
())
self
.
assertEqual
(
request
.
DATA
.
items
(),
data
.
items
())
class
MockView
(
View
):
authentication
=
(
UserLoggedInAuthentication
,)
def
post
(
self
,
request
):
if
request
.
POST
.
get
(
'example'
)
is
not
None
:
return
Response
(
status
=
status
.
HTTP_200_OK
)
...
...
@@ -223,17 +232,19 @@ class TestContentParsingWithAuthentication(TestCase):
self
.
email
=
'lennon@thebeatles.com'
self
.
password
=
'password'
self
.
user
=
User
.
objects
.
create_user
(
self
.
username
,
self
.
email
,
self
.
password
)
self
.
req
=
RequestFactory
()
def
test_user_logged_in_authentication_has_post_when_not_logged_in
(
self
):
"""Ensures request.POST exists after UserLoggedInAuthentication when user doesn't log in"""
def
test_user_logged_in_authentication_has_POST_when_not_logged_in
(
self
):
"""
Ensures request.POST exists after UserLoggedInAuthentication when user
doesn't log in.
"""
content
=
{
'example'
:
'example'
}
response
=
self
.
client
.
post
(
'/'
,
content
)
self
.
assertEqual
(
status
.
HTTP_200_OK
,
response
.
status_code
,
"POST data is malformed"
)
self
.
assertEqual
(
status
.
HTTP_200_OK
,
response
.
status_code
)
response
=
self
.
csrf_client
.
post
(
'/'
,
content
)
self
.
assertEqual
(
status
.
HTTP_200_OK
,
response
.
status_code
,
"POST data is malformed"
)
self
.
assertEqual
(
status
.
HTTP_200_OK
,
response
.
status_code
)
# def test_user_logged_in_authentication_has_post_when_logged_in(self):
# """Ensures request.POST exists after UserLoggedInAuthentication when user does log in"""
...
...
djangorestframework/tests/response.py
View file @
1cde31c8
import
json
import
unittest
from
django.conf.urls.defaults
import
patterns
,
url
from
django.conf.urls.defaults
import
patterns
,
url
,
include
from
django.test
import
TestCase
from
djangorestframework.response
import
Response
,
ImmediateResponse
from
djangorestframework.mixins
import
ResponseMixin
from
djangorestframework.views
import
View
from
djangorestframework.compat
import
View
as
DjangoView
from
djangorestframework.renderers
import
BaseRenderer
,
DEFAULT_RENDERERS
from
djangorestframework.compat
import
RequestFactory
from
djangorestframework
import
status
from
djangorestframework.renderers
import
BaseRenderer
,
JSONRenderer
,
YAMLRenderer
,
\
XMLRenderer
,
JSONPRenderer
,
DocumentingHTMLRenderer
from
djangorestframework.renderers
import
(
BaseRenderer
,
JSONRenderer
,
DocumentingHTMLRenderer
,
DEFAULT_RENDERERS
)
class
TestResponseDetermineRenderer
(
TestCase
):
...
...
@@ -20,7 +21,7 @@ class TestResponseDetermineRenderer(TestCase):
def
get_response
(
self
,
url
=
''
,
accept_list
=
[],
renderers
=
[]):
kwargs
=
{}
if
accept_list
is
not
None
:
kwargs
[
'HTTP_ACCEPT'
]
=
HTTP_ACCEPT
=
','
.
join
(
accept_list
)
kwargs
[
'HTTP_ACCEPT'
]
=
','
.
join
(
accept_list
)
request
=
RequestFactory
()
.
get
(
url
,
**
kwargs
)
return
Response
(
request
=
request
,
renderers
=
renderers
)
...
...
@@ -43,7 +44,7 @@ class TestResponseDetermineRenderer(TestCase):
"""
response
=
self
.
get_response
(
accept_list
=
None
)
self
.
assertEqual
(
response
.
_determine_accept_list
(),
[
'*/*'
])
def
test_determine_accept_list_overriden_header
(
self
):
"""
Test Accept header overriding.
...
...
@@ -81,7 +82,7 @@ class TestResponseDetermineRenderer(TestCase):
renderer
,
media_type
=
response
.
_determine_renderer
()
self
.
assertEqual
(
media_type
,
'*/*'
)
self
.
assertTrue
(
renderer
,
prenderer
)
def
test_determine_renderer_no_renderer
(
self
):
"""
Test determine renderer when no renderer can satisfy the Accept list.
...
...
@@ -94,14 +95,14 @@ class TestResponseDetermineRenderer(TestCase):
class
TestResponseRenderContent
(
TestCase
):
def
get_response
(
self
,
url
=
''
,
accept_list
=
[],
content
=
None
):
request
=
RequestFactory
()
.
get
(
url
,
HTTP_ACCEPT
=
','
.
join
(
accept_list
))
return
Response
(
request
=
request
,
content
=
content
,
renderers
=
[
r
()
for
r
in
DEFAULT_RENDERERS
])
def
test_render
(
self
):
"""
Test rendering simple data to json.
Test rendering simple data to json.
"""
content
=
{
'a'
:
1
,
'b'
:
[
1
,
2
,
3
]}
content_type
=
'application/json'
...
...
@@ -134,34 +135,33 @@ class RendererB(BaseRenderer):
return
RENDERER_B_SERIALIZER
(
obj
)
class
MockView
(
ResponseMixin
,
Django
View
):
renderer
_classe
s
=
(
RendererA
,
RendererB
)
class
MockView
(
View
):
renderers
=
(
RendererA
,
RendererB
)
def
get
(
self
,
request
,
**
kwargs
):
response
=
Response
(
DUMMYCONTENT
,
status
=
DUMMYSTATUS
)
self
.
response
=
self
.
prepare_response
(
response
)
return
self
.
response
return
Response
(
DUMMYCONTENT
,
status
=
DUMMYSTATUS
)
class
HTMLView
(
View
):
renderer
_classe
s
=
(
DocumentingHTMLRenderer
,
)
renderers
=
(
DocumentingHTMLRenderer
,
)
def
get
(
self
,
request
,
**
kwargs
):
return
Response
(
'text'
)
class
HTMLView1
(
View
):
renderer
_classe
s
=
(
DocumentingHTMLRenderer
,
JSONRenderer
)
renderers
=
(
DocumentingHTMLRenderer
,
JSONRenderer
)
def
get
(
self
,
request
,
**
kwargs
):
return
Response
(
'text'
)
return
Response
(
'text'
)
urlpatterns
=
patterns
(
''
,
url
(
r'^.*\.(?P<format>.+)$'
,
MockView
.
as_view
(
renderer
_classe
s
=
[
RendererA
,
RendererB
])),
url
(
r'^$'
,
MockView
.
as_view
(
renderer
_classe
s
=
[
RendererA
,
RendererB
])),
url
(
r'^.*\.(?P<format>.+)$'
,
MockView
.
as_view
(
renderers
=
[
RendererA
,
RendererB
])),
url
(
r'^$'
,
MockView
.
as_view
(
renderers
=
[
RendererA
,
RendererB
])),
url
(
r'^html$'
,
HTMLView
.
as_view
()),
url
(
r'^html1$'
,
HTMLView1
.
as_view
()),
url
(
r'^restframework'
,
include
(
'djangorestframework.urls'
,
namespace
=
'djangorestframework'
))
)
...
...
@@ -257,13 +257,6 @@ class RendererIntegrationTests(TestCase):
self
.
assertEquals
(
resp
.
content
,
RENDERER_B_SERIALIZER
(
DUMMYCONTENT
))
self
.
assertEquals
(
resp
.
status_code
,
DUMMYSTATUS
)
def
test_bla
(
self
):
resp
=
self
.
client
.
get
(
'/?format=formatb'
,
HTTP_ACCEPT
=
'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
)
self
.
assertEquals
(
resp
[
'Content-Type'
],
RendererB
.
media_type
)
self
.
assertEquals
(
resp
.
content
,
RENDERER_B_SERIALIZER
(
DUMMYCONTENT
))
self
.
assertEquals
(
resp
.
status_code
,
DUMMYSTATUS
)
class
Issue122Tests
(
TestCase
):
"""
...
...
@@ -275,10 +268,10 @@ class Issue122Tests(TestCase):
"""
Test if no infinite recursion occurs.
"""
resp
=
self
.
client
.
get
(
'/html'
)
self
.
client
.
get
(
'/html'
)
def
test_html_renderer_is_first
(
self
):
"""
Test if no infinite recursion occurs.
"""
resp
=
self
.
client
.
get
(
'/html1'
)
self
.
client
.
get
(
'/html1'
)
djangorestframework/tests/reverse.py
View file @
1cde31c8
...
...
@@ -16,7 +16,8 @@ class MyView(View):
renderers
=
(
JSONRenderer
,
)
def
get
(
self
,
request
):
return
Response
(
reverse
(
'another'
,
request
))
return
Response
(
reverse
(
'myview'
,
request
=
request
))
urlpatterns
=
patterns
(
''
,
url
(
r'^myview$'
,
MyView
.
as_view
(),
name
=
'myview'
),
...
...
djangorestframework/tests/views.py
View file @
1cde31c8
from
django.conf.urls.defaults
import
patterns
,
url
from
django.core.urlresolvers
import
reverse
from
django.conf.urls.defaults
import
patterns
,
url
,
include
from
django.http
import
HttpResponse
from
django.test
import
TestCase
from
django.test
import
Client
from
django
import
forms
from
django.db
import
models
from
django.utils
import
simplejson
as
json
from
djangorestframework.views
import
View
from
djangorestframework.parsers
import
JSONParser
from
djangorestframework.resources
import
ModelResource
from
djangorestframework.views
import
ListOrCreateModelView
,
InstanceModelView
from
StringIO
import
StringIO
from
djangorestframework.views
import
(
View
,
ListOrCreateModelView
,
InstanceModelView
)
class
MockView
(
View
):
...
...
@@ -24,6 +25,7 @@ class MockViewFinal(View):
def
final
(
self
,
request
,
response
,
*
args
,
**
kwargs
):
return
HttpResponse
(
'{"test": "passed"}'
,
content_type
=
"application/json"
)
class
ResourceMockView
(
View
):
"""This is a resource-based mock view"""
...
...
@@ -34,6 +36,7 @@ class ResourceMockView(View):
form
=
MockForm
class
MockResource
(
ModelResource
):
"""This is a mock model-based resource"""
...
...
@@ -45,16 +48,16 @@ class MockResource(ModelResource):
model
=
MockResourceModel
fields
=
(
'foo'
,
'bar'
,
'baz'
)
urlpatterns
=
patterns
(
'djangorestframework.utils.staticviews'
,
url
(
r'^accounts/login$'
,
'api_login'
),
url
(
r'^accounts/logout$'
,
'api_logout'
),
urlpatterns
=
patterns
(
''
,
url
(
r'^mock/$'
,
MockView
.
as_view
()),
url
(
r'^mock/final/$'
,
MockViewFinal
.
as_view
()),
url
(
r'^resourcemock/$'
,
ResourceMockView
.
as_view
()),
url
(
r'^model/$'
,
ListOrCreateModelView
.
as_view
(
resource
=
MockResource
)),
url
(
r'^model/(?P<pk>[^/]+)/$'
,
InstanceModelView
.
as_view
(
resource
=
MockResource
)),
url
(
r'^restframework/'
,
include
(
'djangorestframework.urls'
,
namespace
=
'djangorestframework'
)),
)
class
BaseViewTests
(
TestCase
):
"""Test the base view class of djangorestframework"""
urls
=
'djangorestframework.tests.views'
...
...
@@ -62,8 +65,7 @@ class BaseViewTests(TestCase):
def
test_view_call_final
(
self
):
response
=
self
.
client
.
options
(
'/mock/final/'
)
self
.
assertEqual
(
response
[
'Content-Type'
]
.
split
(
';'
)[
0
],
"application/json"
)
parser
=
JSONParser
(
None
)
(
data
,
files
)
=
parser
.
parse
(
StringIO
(
response
.
content
))
data
=
json
.
loads
(
response
.
content
)
self
.
assertEqual
(
data
[
'test'
],
'passed'
)
def
test_options_method_simple_view
(
self
):
...
...
@@ -77,9 +79,9 @@ class BaseViewTests(TestCase):
self
.
_verify_options_response
(
response
,
name
=
'Resource Mock'
,
description
=
'This is a resource-based mock view'
,
fields
=
{
'foo'
:
'BooleanField'
,
'bar'
:
'IntegerField'
,
'baz'
:
'CharField'
,
fields
=
{
'foo'
:
'BooleanField'
,
'bar'
:
'IntegerField'
,
'baz'
:
'CharField'
,
})
def
test_options_method_model_resource_list_view
(
self
):
...
...
@@ -87,9 +89,9 @@ class BaseViewTests(TestCase):
self
.
_verify_options_response
(
response
,
name
=
'Mock List'
,
description
=
'This is a mock model-based resource'
,
fields
=
{
'foo'
:
'BooleanField'
,
'bar'
:
'IntegerField'
,
'baz'
:
'CharField'
,
fields
=
{
'foo'
:
'BooleanField'
,
'bar'
:
'IntegerField'
,
'baz'
:
'CharField'
,
})
def
test_options_method_model_resource_detail_view
(
self
):
...
...
@@ -97,17 +99,16 @@ class BaseViewTests(TestCase):
self
.
_verify_options_response
(
response
,
name
=
'Mock Instance'
,
description
=
'This is a mock model-based resource'
,
fields
=
{
'foo'
:
'BooleanField'
,
'bar'
:
'IntegerField'
,
'baz'
:
'CharField'
,
fields
=
{
'foo'
:
'BooleanField'
,
'bar'
:
'IntegerField'
,
'baz'
:
'CharField'
,
})
def
_verify_options_response
(
self
,
response
,
name
,
description
,
fields
=
None
,
status
=
200
,
mime_type
=
'application/json'
):
self
.
assertEqual
(
response
.
status_code
,
status
)
self
.
assertEqual
(
response
[
'Content-Type'
]
.
split
(
';'
)[
0
],
mime_type
)
parser
=
JSONParser
(
None
)
(
data
,
files
)
=
parser
.
parse
(
StringIO
(
response
.
content
))
data
=
json
.
loads
(
response
.
content
)
self
.
assertTrue
(
'application/json'
in
data
[
'renders'
])
self
.
assertEqual
(
name
,
data
[
'name'
])
self
.
assertEqual
(
description
,
data
[
'description'
])
...
...
@@ -123,15 +124,12 @@ class ExtraViewsTests(TestCase):
def
test_login_view
(
self
):
"""Ensure the login view exists"""
response
=
self
.
client
.
get
(
'/accounts/login'
)
response
=
self
.
client
.
get
(
reverse
(
'djangorestframework:login'
)
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
[
'Content-Type'
]
.
split
(
';'
)[
0
],
'text/html'
)
def
test_logout_view
(
self
):
"""Ensure the logout view exists"""
response
=
self
.
client
.
get
(
'/accounts/logout'
)
response
=
self
.
client
.
get
(
reverse
(
'djangorestframework:logout'
)
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
[
'Content-Type'
]
.
split
(
';'
)[
0
],
'text/html'
)
# TODO: Add login/logout behaviour tests
djangorestframework/urls.py
View file @
1cde31c8
from
django.conf.urls.defaults
import
patterns
from
django.conf.urls.defaults
import
patterns
,
url
urlpatterns
=
patterns
(
'djangorestframework.utils.staticviews'
,
(
r'^accounts/login/$'
,
'api_login'
),
(
r'^accounts/logout/$'
,
'api_logout'
),
template_name
=
{
'template_name'
:
'djangorestframework/login.html'
}
urlpatterns
=
patterns
(
'django.contrib.auth.views'
,
url
(
r'^login/$'
,
'login'
,
template_name
,
name
=
'login'
),
url
(
r'^logout/$'
,
'logout'
,
template_name
,
name
=
'logout'
),
)
djangorestframework/utils/__init__.py
View file @
1cde31c8
import
django
from
django.utils.encoding
import
smart_unicode
from
django.utils.xmlutils
import
SimplerXMLGenerator
from
django.core.urlresolvers
import
resolve
,
reverse
as
django_reverse
from
django.conf
import
settings
from
django.core.urlresolvers
import
resolve
from
djangorestframework.compat
import
StringIO
from
djangorestframework.compat
import
RequestFactory
as
DjangoRequestFactory
from
djangorestframework.request
import
Request
import
re
import
xml.etree.ElementTree
as
ET
#def admin_media_prefix(request):
# """Adds the ADMIN_MEDIA_PREFIX to the request context."""
# return {'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX}
from
mediatypes
import
media_type_matches
,
is_form_media_type
from
mediatypes
import
add_media_type_param
,
get_media_type_params
,
order_by_precedence
MSIE_USER_AGENT_REGEX
=
re
.
compile
(
r'^Mozilla/[0-9]+\.[0-9]+ \([^)]*; MSIE [0-9]+\.[0-9]+[a-z]?;[^)]*\)(?!.* Opera )'
)
def
as_tuple
(
obj
):
"""
Given an object which may be a list/tuple, another object, or None,
...
...
@@ -49,45 +43,6 @@ def url_resolves(url):
return
True
def
allowed_methods
(
view
):
"""
Return the list of uppercased allowed HTTP methods on `view`.
"""
return
[
method
.
upper
()
for
method
in
view
.
http_method_names
if
hasattr
(
view
,
method
)]
# From http://www.koders.com/python/fidB6E125C586A6F49EAC38992CF3AFDAAE35651975.aspx?s=mdef:xml
#class object_dict(dict):
# """object view of dict, you can
# >>> a = object_dict()
# >>> a.fish = 'fish'
# >>> a['fish']
# 'fish'
# >>> a['water'] = 'water'
# >>> a.water
# 'water'
# >>> a.test = {'value': 1}
# >>> a.test2 = object_dict({'name': 'test2', 'value': 2})
# >>> a.test, a.test2.name, a.test2.value
# (1, 'test2', 2)
# """
# def __init__(self, initd=None):
# if initd is None:
# initd = {}
# dict.__init__(self, initd)
#
# def __getattr__(self, item):
# d = self.__getitem__(item)
# # if value is the only key in object, you can omit it
# if isinstance(d, dict) and 'value' in d and len(d) == 1:
# return d['value']
# else:
# return d
#
# def __setattr__(self, item, value):
# self.__setitem__(item, value)
# From xml2dict
class
XML2Dict
(
object
):
...
...
@@ -99,24 +54,23 @@ class XML2Dict(object):
# Save attrs and text, hope there will not be a child with same name
if
node
.
text
:
node_tree
=
node
.
text
for
(
k
,
v
)
in
node
.
attrib
.
items
():
k
,
v
=
self
.
_namespace_split
(
k
,
v
)
for
(
k
,
v
)
in
node
.
attrib
.
items
():
k
,
v
=
self
.
_namespace_split
(
k
,
v
)
node_tree
[
k
]
=
v
#Save childrens
for
child
in
node
.
getchildren
():
tag
,
tree
=
self
.
_namespace_split
(
child
.
tag
,
self
.
_parse_node
(
child
))
if
tag
not
in
node_tree
:
# the first time, so store it in dict
if
tag
not
in
node_tree
:
# the first time, so store it in dict
node_tree
[
tag
]
=
tree
continue
old
=
node_tree
[
tag
]
if
not
isinstance
(
old
,
list
):
node_tree
.
pop
(
tag
)
node_tree
[
tag
]
=
[
old
]
# multi times, so change old dict to a list
node_tree
[
tag
]
.
append
(
tree
)
# add the new one
node_tree
[
tag
]
=
[
old
]
# multi times, so change old dict to a list
node_tree
[
tag
]
.
append
(
tree
)
# add the new one
return
node_tree
def
_namespace_split
(
self
,
tag
,
value
):
"""
Split the tag '{http://cs.sfsu.edu/csc867/myscheduler}patients'
...
...
@@ -179,23 +133,41 @@ class XMLRenderer():
xml
.
endDocument
()
return
stream
.
getvalue
()
def
dict2xml
(
input
):
return
XMLRenderer
()
.
dict2xml
(
input
)
def
reverse
(
viewname
,
request
,
*
args
,
**
kwargs
):
class
RequestFactory
(
DjangoRequestFactory
):
"""
Do the same as :py:func:`django.core.urlresolvers.reverse` but using
*request* to build a fully qualified URL.
Replicate RequestFactory, but return Request, not HttpRequest.
"""
return
request
.
build_absolute_uri
(
django_reverse
(
viewname
,
*
args
,
**
kwargs
))
if
django
.
VERSION
>=
(
1
,
4
):
from
django.core.urlresolvers
import
reverse_lazy
as
django_reverse_lazy
def
reverse_lazy
(
viewname
,
request
,
*
args
,
**
kwargs
):
"""
Do the same as :py:func:`django.core.urlresolvers.reverse_lazy` but using
*request* to build a fully qualified URL.
"""
return
request
.
build_absolute_uri
(
django_reverse_lazy
(
viewname
,
*
args
,
**
kwargs
))
def
get
(
self
,
*
args
,
**
kwargs
):
parsers
=
kwargs
.
pop
(
'parsers'
,
None
)
request
=
super
(
RequestFactory
,
self
)
.
get
(
*
args
,
**
kwargs
)
return
Request
(
request
,
parsers
)
def
post
(
self
,
*
args
,
**
kwargs
):
parsers
=
kwargs
.
pop
(
'parsers'
,
None
)
request
=
super
(
RequestFactory
,
self
)
.
post
(
*
args
,
**
kwargs
)
return
Request
(
request
,
parsers
)
def
put
(
self
,
*
args
,
**
kwargs
):
parsers
=
kwargs
.
pop
(
'parsers'
,
None
)
request
=
super
(
RequestFactory
,
self
)
.
put
(
*
args
,
**
kwargs
)
return
Request
(
request
,
parsers
)
def
delete
(
self
,
*
args
,
**
kwargs
):
parsers
=
kwargs
.
pop
(
'parsers'
,
None
)
request
=
super
(
RequestFactory
,
self
)
.
delete
(
*
args
,
**
kwargs
)
return
Request
(
request
,
parsers
)
def
head
(
self
,
*
args
,
**
kwargs
):
parsers
=
kwargs
.
pop
(
'parsers'
,
None
)
request
=
super
(
RequestFactory
,
self
)
.
head
(
*
args
,
**
kwargs
)
return
Request
(
request
,
parsers
)
def
options
(
self
,
*
args
,
**
kwargs
):
parsers
=
kwargs
.
pop
(
'parsers'
,
None
)
request
=
super
(
RequestFactory
,
self
)
.
options
(
*
args
,
**
kwargs
)
return
Request
(
request
,
parsers
)
djangorestframework/utils/staticviews.py
deleted
100644 → 0
View file @
5fd4c639
from
django.contrib.auth.views
import
*
from
django.conf
import
settings
from
django.http
import
HttpResponse
from
django.shortcuts
import
render_to_response
from
django.template
import
RequestContext
import
base64
# BLERGH
# Replicate django.contrib.auth.views.login simply so we don't have get users to update TEMPLATE_CONTEXT_PROCESSORS
# to add ADMIN_MEDIA_PREFIX to the RequestContext. I don't like this but really really want users to not have to
# be making settings changes in order to accomodate django-rest-framework
@csrf_protect
@never_cache
def
api_login
(
request
,
template_name
=
'djangorestframework/login.html'
,
redirect_field_name
=
REDIRECT_FIELD_NAME
,
authentication_form
=
AuthenticationForm
):
"""Displays the login form and handles the login action."""
redirect_to
=
request
.
REQUEST
.
get
(
redirect_field_name
,
''
)
if
request
.
method
==
"POST"
:
form
=
authentication_form
(
data
=
request
.
POST
)
if
form
.
is_valid
():
# Light security check -- make sure redirect_to isn't garbage.
if
not
redirect_to
or
' '
in
redirect_to
:
redirect_to
=
settings
.
LOGIN_REDIRECT_URL
# Heavier security check -- redirects to http://example.com should
# not be allowed, but things like /view/?param=http://example.com
# should be allowed. This regex checks if there is a '//' *before* a
# question mark.
elif
'//'
in
redirect_to
and
re
.
match
(
r'[^\?]*//'
,
redirect_to
):
redirect_to
=
settings
.
LOGIN_REDIRECT_URL
# Okay, security checks complete. Log the user in.
auth_login
(
request
,
form
.
get_user
())
if
request
.
session
.
test_cookie_worked
():
request
.
session
.
delete_test_cookie
()
return
HttpResponseRedirect
(
redirect_to
)
else
:
form
=
authentication_form
(
request
)
request
.
session
.
set_test_cookie
()
#current_site = get_current_site(request)
return
render_to_response
(
template_name
,
{
'form'
:
form
,
redirect_field_name
:
redirect_to
,
#'site': current_site,
#'site_name': current_site.name,
'ADMIN_MEDIA_PREFIX'
:
settings
.
ADMIN_MEDIA_PREFIX
,
},
context_instance
=
RequestContext
(
request
))
def
api_logout
(
request
,
next_page
=
None
,
template_name
=
'djangorestframework/login.html'
,
redirect_field_name
=
REDIRECT_FIELD_NAME
):
return
logout
(
request
,
next_page
,
template_name
,
redirect_field_name
)
djangorestframework/views.py
View file @
1cde31c8
...
...
@@ -6,15 +6,13 @@ By setting or modifying class attributes on your view, you change it's predefine
"""
import
re
from
django.core.urlresolvers
import
set_script_prefix
,
get_script_prefix
from
django.utils.html
import
escape
from
django.utils.safestring
import
mark_safe
from
django.views.decorators.csrf
import
csrf_exempt
from
djangorestframework.compat
import
View
as
DjangoView
,
apply_markdown
from
djangorestframework.response
import
ImmediateResponse
from
djangorestframework.response
import
Response
,
ImmediateResponse
from
djangorestframework.mixins
import
*
from
djangorestframework.utils
import
allowed_methods
from
djangorestframework
import
resources
,
renderers
,
parsers
,
authentication
,
permissions
,
status
...
...
@@ -81,12 +79,12 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
or `None` to use default behaviour.
"""
renderer
_classe
s
=
renderers
.
DEFAULT_RENDERERS
renderers
=
renderers
.
DEFAULT_RENDERERS
"""
List of renderer classes the resource can serialize the response with, ordered by preference.
"""
parser
_classe
s
=
parsers
.
DEFAULT_PARSERS
parsers
=
parsers
.
DEFAULT_PARSERS
"""
List of parser classes the resource can parse the request with.
"""
...
...
@@ -118,7 +116,15 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
"""
Return the list of allowed HTTP methods, uppercased.
"""
return
allowed_methods
(
self
)
return
[
method
.
upper
()
for
method
in
self
.
http_method_names
if
hasattr
(
self
,
method
)]
@property
def
default_response_headers
(
self
):
return
{
'Allow'
:
', '
.
join
(
self
.
allowed_methods
),
'Vary'
:
'Authenticate, Accept'
}
def
get_name
(
self
):
"""
...
...
@@ -183,32 +189,35 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
def
initial
(
self
,
request
,
*
args
,
**
kargs
):
"""
Returns an `HttpRequest`. This method is a hook for any code that needs to run
prior to
anything else.
Required if you want to do things like set `request.upload_handlers`
before
the authentication and dispatch handling is run.
This method is a hook for any code that needs to run prior to
anything else.
Required if you want to do things like set `request.upload_handlers`
before
the authentication and dispatch handling is run.
"""
pass
def
final
(
self
,
request
,
response
,
*
args
,
**
kargs
):
"""
Returns an `HttpResponse`. This method is a hook for any code that needs to run
after everything else in the view.
This method is a hook for any code that needs to run after everything
else in the view.
Returns the final response object.
"""
# Always add these headers.
response
[
'Allow'
]
=
', '
.
join
(
allowed_methods
(
self
))
# sample to allow caching using Vary http header
response
[
'Vary'
]
=
'Authenticate, Accept'
response
.
view
=
self
response
.
request
=
request
response
.
renderers
=
self
.
renderers
for
key
,
value
in
self
.
headers
.
items
():
response
[
key
]
=
value
return
response
# Note: session based authentication is explicitly CSRF validated,
# all other authentication is CSRF exempt.
@csrf_exempt
def
dispatch
(
self
,
request
,
*
args
,
**
kwargs
):
self
.
request
=
self
.
create_request
(
request
)
request
=
self
.
create_request
(
request
)
self
.
request
=
request
self
.
args
=
args
self
.
kwargs
=
kwargs
self
.
headers
=
self
.
default_response_headers
try
:
self
.
initial
(
request
,
*
args
,
**
kwargs
)
...
...
@@ -222,26 +231,17 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
else
:
handler
=
self
.
http_method_not_allowed
# TODO: should we enforce HttpResponse, like Django does ?
response
=
handler
(
request
,
*
args
,
**
kwargs
)
# Prepare response for the response cycle.
self
.
response
=
response
=
self
.
prepare_response
(
response
)
# Pre-serialize filtering (eg filter complex objects into natively serializable types)
# TODO: ugly hack to handle both HttpResponse and Response.
if
hasattr
(
response
,
'raw_content'
):
if
isinstance
(
response
,
Response
):
# Pre-serialize filtering (eg filter complex objects into natively serializable types)
response
.
raw_content
=
self
.
filter_response
(
response
.
raw_content
)
else
:
response
.
content
=
self
.
filter_response
(
response
.
content
)
except
ImmediateResponse
,
response
:
# Prepare response for the response cycle.
self
.
response
=
response
=
self
.
prepare_response
(
response
)
except
ImmediateResponse
,
exc
:
response
=
exc
.
response
# `final` is the last opportunity to temper with the response, or even
# completely replace it.
return
self
.
final
(
request
,
response
,
*
args
,
**
kwargs
)
self
.
response
=
self
.
final
(
request
,
response
,
*
args
,
**
kwargs
)
return
self
.
response
def
options
(
self
,
request
,
*
args
,
**
kwargs
):
content
=
{
...
...
@@ -266,7 +266,7 @@ class ModelView(View):
resource
=
resources
.
ModelResource
class
InstanceModelView
(
InstanceMixin
,
ReadModelMixin
,
UpdateModelMixin
,
DeleteModelMixin
,
ModelView
):
class
InstanceModelView
(
ReadModelMixin
,
UpdateModelMixin
,
DeleteModelMixin
,
ModelView
):
"""
A view which provides default operations for read/update/delete against a model instance.
"""
...
...
docs/howto/setup.rst
View file @
1cde31c8
...
...
@@ -49,20 +49,20 @@ YAML
YAML support is optional, and requires `PyYAML`_.
Login / Logout
--------------
Django REST framework includes login and logout views that are useful if
you're using the self-documenting API::
Django REST framework includes login and logout views that are needed if
you're using the self-documenting API.
Make sure you include the following in your `urlconf`::
from django.conf.urls.defaults import patterns
from django.conf.urls.defaults import patterns
, url
urlpatterns = patterns('djangorestframework.views',
# Add your resources here
(r'^accounts/login/$', 'api_login'),
(r'^accounts/logout/$', 'api_logout'),
)
urlpatterns = patterns('',
...
url(r'^restframework', include('djangorestframework.urls', namespace='djangorestframework'))
)
.. _django.contrib.staticfiles: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/
.. _django-staticfiles: http://pypi.python.org/pypi/django-staticfiles/
...
...
docs/index.rst
View file @
1cde31c8
...
...
@@ -64,6 +64,12 @@ To add Django REST framework to a Django project:
* Ensure that the ``djangorestframework`` directory is on your ``PYTHONPATH``.
* Add ``djangorestframework`` to your ``INSTALLED_APPS``.
* Add the following to your URLconf. (To include the REST framework Login/Logout views.)::
urlpatterns = patterns('',
...
url(r'
^
restframework
', include('
djangorestframework
.
urls
', namespace='
djangorestframework
'))
)
For more information on settings take a look at the :ref:`setup` section.
...
...
examples/blogpost/models.py
View file @
1cde31c8
...
...
@@ -2,6 +2,7 @@ from django.db import models
from
django.template.defaultfilters
import
slugify
import
uuid
def
uuid_str
():
return
str
(
uuid
.
uuid1
())
...
...
@@ -14,6 +15,7 @@ RATING_CHOICES = ((0, 'Awful'),
MAX_POSTS
=
10
class
BlogPost
(
models
.
Model
):
key
=
models
.
CharField
(
primary_key
=
True
,
max_length
=
64
,
default
=
uuid_str
,
editable
=
False
)
title
=
models
.
CharField
(
max_length
=
128
)
...
...
@@ -37,4 +39,3 @@ class Comment(models.Model):
comment
=
models
.
TextField
()
rating
=
models
.
IntegerField
(
blank
=
True
,
null
=
True
,
choices
=
RATING_CHOICES
,
help_text
=
'How did you rate this post?'
)
created
=
models
.
DateTimeField
(
auto_now_add
=
True
)
examples/blogpost/resources.py
View file @
1cde31c8
...
...
@@ -11,8 +11,15 @@ class BlogPostResource(ModelResource):
fields
=
(
'created'
,
'title'
,
'slug'
,
'content'
,
'url'
,
'comments'
)
ordering
=
(
'-created'
,)
def
url
(
self
,
instance
):
return
reverse
(
'blog-post'
,
kwargs
=
{
'key'
:
instance
.
key
},
request
=
self
.
request
)
def
comments
(
self
,
instance
):
return
reverse
(
'comments'
,
request
,
kwargs
=
{
'blogpost'
:
instance
.
key
})
return
reverse
(
'comments'
,
kwargs
=
{
'blogpost'
:
instance
.
key
},
request
=
self
.
request
)
class
CommentResource
(
ModelResource
):
...
...
@@ -24,4 +31,6 @@ class CommentResource(ModelResource):
ordering
=
(
'-created'
,)
def
blogpost
(
self
,
instance
):
return
reverse
(
'blog-post'
,
request
,
kwargs
=
{
'key'
:
instance
.
blogpost
.
key
})
return
reverse
(
'blog-post'
,
kwargs
=
{
'key'
:
instance
.
blogpost
.
key
},
request
=
self
.
request
)
examples/mixin/urls.py
View file @
1cde31c8
...
...
@@ -10,11 +10,12 @@ from django.conf.urls.defaults import patterns, url
class
ExampleView
(
ResponseMixin
,
View
):
"""An example view using Django 1.3's class based views.
Uses djangorestframework's RendererMixin to provide support for multiple output formats."""
renderer
_classe
s
=
DEFAULT_RENDERERS
renderers
=
DEFAULT_RENDERERS
def
get
(
self
,
request
):
url
=
reverse
(
'mixin-view'
,
request
)
response
=
Response
({
'description'
:
'Some example content'
,
'url'
:
reverse
(
'mixin-view'
,
request
)
},
status
=
200
)
'url'
:
url
},
status
=
200
)
self
.
response
=
self
.
prepare_response
(
response
)
return
self
.
response
...
...
@@ -22,4 +23,3 @@ class ExampleView(ResponseMixin, View):
urlpatterns
=
patterns
(
''
,
url
(
r'^$'
,
ExampleView
.
as_view
(),
name
=
'mixin-view'
),
)
examples/modelresourceexample/models.py
View file @
1cde31c8
...
...
@@ -2,6 +2,7 @@ from django.db import models
MAX_INSTANCES
=
10
class
MyModel
(
models
.
Model
):
foo
=
models
.
BooleanField
()
bar
=
models
.
IntegerField
(
help_text
=
'Must be an integer.'
)
...
...
@@ -15,5 +16,3 @@ class MyModel(models.Model):
super
(
MyModel
,
self
)
.
save
(
*
args
,
**
kwargs
)
while
MyModel
.
objects
.
all
()
.
count
()
>
MAX_INSTANCES
:
MyModel
.
objects
.
all
()
.
order_by
(
'-created'
)[
0
]
.
delete
()
examples/modelresourceexample/resources.py
View file @
1cde31c8
from
djangorestframework.resources
import
ModelResource
from
djangorestframework.reverse
import
reverse
from
modelresourceexample.models
import
MyModel
class
MyModelResource
(
ModelResource
):
model
=
MyModel
fields
=
(
'foo'
,
'bar'
,
'baz'
,
'url'
)
ordering
=
(
'created'
,)
def
url
(
self
,
instance
):
return
reverse
(
'model-resource-instance'
,
kwargs
=
{
'id'
:
instance
.
id
},
request
=
self
.
request
)
examples/modelresourceexample/urls.py
View file @
1cde31c8
...
...
@@ -2,7 +2,10 @@ from django.conf.urls.defaults import patterns, url
from
djangorestframework.views
import
ListOrCreateModelView
,
InstanceModelView
from
modelresourceexample.resources
import
MyModelResource
my_model_list
=
ListOrCreateModelView
.
as_view
(
resource
=
MyModelResource
)
my_model_instance
=
InstanceModelView
.
as_view
(
resource
=
MyModelResource
)
urlpatterns
=
patterns
(
''
,
url
(
r'^$'
,
ListOrCreateModelView
.
as_view
(
resource
=
MyModelResource
)
,
name
=
'model-resource-root'
),
url
(
r'^(?P<
pk>[0-9]+)/$'
,
InstanceModelView
.
as_view
(
resource
=
MyModelResource
)
),
url
(
r'^$'
,
my_model_list
,
name
=
'model-resource-root'
),
url
(
r'^(?P<
id>[0-9]+)/$'
,
my_model_instance
,
name
=
'model-resource-instance'
),
)
examples/objectstore/views.py
View file @
1cde31c8
...
...
@@ -28,6 +28,20 @@ def remove_oldest_files(dir, max_files):
[
os
.
remove
(
path
)
for
path
in
ctime_sorted_paths
[
max_files
:]]
def
get_filename
(
key
):
"""
Given a stored object's key returns the file's path.
"""
return
os
.
path
.
join
(
OBJECT_STORE_DIR
,
key
)
def
get_file_url
(
key
,
request
):
"""
Given a stored object's key returns the URL for the object.
"""
return
reverse
(
'stored-object'
,
kwargs
=
{
'key'
:
key
},
request
=
request
)
class
ObjectStoreRoot
(
View
):
"""
Root of the Object Store API.
...
...
@@ -38,20 +52,25 @@ class ObjectStoreRoot(View):
"""
Return a list of all the stored object URLs. (Ordered by creation time, newest first)
"""
filepaths
=
[
os
.
path
.
join
(
OBJECT_STORE_DIR
,
file
)
for
file
in
os
.
listdir
(
OBJECT_STORE_DIR
)
if
not
file
.
startswith
(
'.'
)]
filepaths
=
[
os
.
path
.
join
(
OBJECT_STORE_DIR
,
file
)
for
file
in
os
.
listdir
(
OBJECT_STORE_DIR
)
if
not
file
.
startswith
(
'.'
)]
ctime_sorted_basenames
=
[
item
[
0
]
for
item
in
sorted
([(
os
.
path
.
basename
(
path
),
os
.
path
.
getctime
(
path
))
for
path
in
filepaths
],
key
=
operator
.
itemgetter
(
1
),
reverse
=
True
)]
return
Response
([
reverse
(
'stored-object'
,
request
,
kwargs
=
{
'key'
:
key
})
for
key
in
ctime_sorted_basenames
])
content
=
[
get_file_url
(
key
,
request
)
for
key
in
ctime_sorted_basenames
]
return
Response
(
content
)
def
post
(
self
,
request
):
"""
Create a new stored object, with a unique key.
"""
key
=
str
(
uuid
.
uuid1
())
pathname
=
os
.
path
.
join
(
OBJECT_STORE_DIR
,
key
)
pickle
.
dump
(
self
.
CONTENT
,
open
(
pathname
,
'wb'
))
filename
=
get_filename
(
key
)
pickle
.
dump
(
self
.
CONTENT
,
open
(
filename
,
'wb'
))
remove_oldest_files
(
OBJECT_STORE_DIR
,
MAX_FILES
)
url
=
reverse
(
'stored-object'
,
request
,
kwargs
=
{
'key'
:
key
}
)
url
=
get_file_url
(
key
,
request
)
return
Response
(
self
.
CONTENT
,
status
.
HTTP_201_CREATED
,
{
'Location'
:
url
})
...
...
@@ -60,30 +79,31 @@ class StoredObject(View):
Represents a stored object.
The object may be any picklable content.
"""
def
get
(
self
,
request
,
key
):
"""
Return a stored object, by unpickling the contents of a locally stored file.
Return a stored object, by unpickling the contents of a locally
stored file.
"""
pathname
=
os
.
path
.
join
(
OBJECT_STORE_DIR
,
key
)
if
not
os
.
path
.
exists
(
path
name
):
filename
=
get_filename
(
key
)
if
not
os
.
path
.
exists
(
file
name
):
return
Response
(
status
=
status
.
HTTP_404_NOT_FOUND
)
return
Response
(
pickle
.
load
(
open
(
path
name
,
'rb'
)))
return
Response
(
pickle
.
load
(
open
(
file
name
,
'rb'
)))
def
put
(
self
,
request
,
key
):
"""
Update/create a stored object, by pickling the request content to a locally stored file.
Update/create a stored object, by pickling the request content to a
locally stored file.
"""
pathname
=
os
.
path
.
join
(
OBJECT_STORE_DIR
,
key
)
pickle
.
dump
(
self
.
CONTENT
,
open
(
path
name
,
'wb'
))
filename
=
get_filename
(
key
)
pickle
.
dump
(
self
.
CONTENT
,
open
(
file
name
,
'wb'
))
return
Response
(
self
.
CONTENT
)
def
delete
(
self
,
request
,
key
):
"""
Delete a stored object, by removing it's pickled file.
"""
pathname
=
os
.
path
.
join
(
OBJECT_STORE_DIR
,
key
)
if
not
os
.
path
.
exists
(
path
name
):
filename
=
get_filename
(
key
)
if
not
os
.
path
.
exists
(
file
name
):
return
Response
(
status
=
status
.
HTTP_404_NOT_FOUND
)
os
.
remove
(
path
name
)
os
.
remove
(
file
name
)
return
Response
()
examples/pygments_api/forms.py
View file @
1cde31c8
...
...
@@ -6,6 +6,7 @@ from pygments.styles import get_all_styles
LEXER_CHOICES
=
sorted
([(
item
[
1
][
0
],
item
[
0
])
for
item
in
get_all_lexers
()])
STYLE_CHOICES
=
sorted
((
item
,
item
)
for
item
in
list
(
get_all_styles
()))
class
PygmentsForm
(
forms
.
Form
):
"""A simple form with some of the most important pygments settings.
The code to be highlighted can be specified either in a text field, or by URL.
...
...
@@ -24,5 +25,3 @@ class PygmentsForm(forms.Form):
initial
=
'python'
)
style
=
forms
.
ChoiceField
(
choices
=
STYLE_CHOICES
,
initial
=
'friendly'
)
examples/pygments_api/tests.py
View file @
1cde31c8
...
...
@@ -14,13 +14,13 @@ class TestPygmentsExample(TestCase):
self
.
factory
=
RequestFactory
()
self
.
temp_dir
=
tempfile
.
mkdtemp
()
views
.
HIGHLIGHTED_CODE_DIR
=
self
.
temp_dir
def
tearDown
(
self
):
try
:
shutil
.
rmtree
(
self
.
temp_dir
)
except
Exception
:
pass
def
test_get_to_root
(
self
):
'''Just do a get on the base url'''
request
=
self
.
factory
.
get
(
'/pygments'
)
...
...
@@ -44,6 +44,3 @@ class TestPygmentsExample(TestCase):
response
=
view
(
request
)
response_locations
=
json
.
loads
(
response
.
content
)
self
.
assertEquals
(
locations
,
response_locations
)
examples/pygments_api/views.py
View file @
1cde31c8
from
__future__
import
with_statement
# for python 2.5
from
django.conf
import
settings
from
djangorestframework.resources
import
FormResource
from
djangorestframework.response
import
Response
from
djangorestframework.renderers
import
BaseRenderer
from
djangorestframework.reverse
import
reverse
...
...
@@ -30,9 +29,13 @@ def list_dir_sorted_by_ctime(dir):
"""
Return a list of files sorted by creation time
"""
filepaths
=
[
os
.
path
.
join
(
dir
,
file
)
for
file
in
os
.
listdir
(
dir
)
if
not
file
.
startswith
(
'.'
)]
return
[
item
[
0
]
for
item
in
sorted
(
[(
path
,
os
.
path
.
getctime
(
path
))
for
path
in
filepaths
],
key
=
operator
.
itemgetter
(
1
),
reverse
=
False
)
]
filepaths
=
[
os
.
path
.
join
(
dir
,
file
)
for
file
in
os
.
listdir
(
dir
)
if
not
file
.
startswith
(
'.'
)]
ctimes
=
[(
path
,
os
.
path
.
getctime
(
path
))
for
path
in
filepaths
]
ctimes
=
sorted
(
ctimes
,
key
=
operator
.
itemgetter
(
1
),
reverse
=
False
)
return
[
filepath
for
filepath
,
ctime
in
ctimes
]
def
remove_oldest_files
(
dir
,
max_files
):
"""
...
...
@@ -60,8 +63,11 @@ class PygmentsRoot(View):
"""
Return a list of all currently existing snippets.
"""
unique_ids
=
[
os
.
path
.
split
(
f
)[
1
]
for
f
in
list_dir_sorted_by_ctime
(
HIGHLIGHTED_CODE_DIR
)]
return
Response
([
reverse
(
'pygments-instance'
,
request
,
args
=
[
unique_id
])
for
unique_id
in
unique_ids
])
unique_ids
=
[
os
.
path
.
split
(
f
)[
1
]
for
f
in
list_dir_sorted_by_ctime
(
HIGHLIGHTED_CODE_DIR
)]
urls
=
[
reverse
(
'pygments-instance'
,
args
=
[
unique_id
],
request
=
request
)
for
unique_id
in
unique_ids
]
return
Response
(
urls
)
def
post
(
self
,
request
):
"""
...
...
@@ -81,7 +87,7 @@ class PygmentsRoot(View):
remove_oldest_files
(
HIGHLIGHTED_CODE_DIR
,
MAX_FILES
)
location
=
reverse
(
'pygments-instance'
,
request
,
args
=
[
unique_id
]
)
location
=
reverse
(
'pygments-instance'
,
args
=
[
unique_id
],
request
=
request
)
return
Response
(
status
=
status
.
HTTP_201_CREATED
,
headers
=
{
'Location'
:
location
})
...
...
@@ -90,7 +96,7 @@ class PygmentsInstance(View):
Simply return the stored highlighted HTML file with the correct mime type.
This Resource only renders HTML and uses a standard HTML renderer rather than the renderers.DocumentingHTMLRenderer class.
"""
renderer
_classes
=
(
HTMLRenderer
,
)
renderer
s
=
(
HTMLRenderer
,
)
def
get
(
self
,
request
,
unique_id
):
"""
...
...
@@ -110,4 +116,3 @@ class PygmentsInstance(View):
return
Response
(
status
=
status
.
HTTP_404_NOT_FOUND
)
os
.
remove
(
pathname
)
return
Response
()
examples/requestexample/views.py
View file @
1cde31c8
...
...
@@ -22,7 +22,7 @@ class MyBaseViewUsingEnhancedRequest(RequestMixin, View):
Base view enabling the usage of enhanced requests with user defined views.
"""
parser
_classe
s
=
parsers
.
DEFAULT_PARSERS
parsers
=
parsers
.
DEFAULT_PARSERS
def
dispatch
(
self
,
request
,
*
args
,
**
kwargs
):
self
.
request
=
request
=
self
.
create_request
(
request
)
...
...
@@ -41,4 +41,3 @@ class EchoRequestContentView(MyBaseViewUsingEnhancedRequest):
def
put
(
self
,
request
,
*
args
,
**
kwargs
):
return
HttpResponse
((
"Found
%
s in request.DATA, content :
%
s"
%
(
type
(
request
.
DATA
),
request
.
DATA
)))
examples/resourceexample/forms.py
View file @
1cde31c8
from
django
import
forms
class
MyForm
(
forms
.
Form
):
foo
=
forms
.
BooleanField
(
required
=
False
)
bar
=
forms
.
IntegerField
(
help_text
=
'Must be an integer.'
)
...
...
examples/resourceexample/views.py
View file @
1cde31c8
...
...
@@ -16,9 +16,11 @@ class ExampleView(View):
Handle GET requests, returning a list of URLs pointing to
three other views.
"""
urls
=
[
reverse
(
'another-example'
,
request
,
kwargs
=
{
'num'
:
num
})
for
num
in
range
(
3
)]
return
Response
({
"Some other resources"
:
urls
})
resource_urls
=
[
reverse
(
'another-example'
,
kwargs
=
{
'num'
:
num
},
request
=
request
)
for
num
in
range
(
3
)]
return
Response
({
"Some other resources"
:
resource_urls
})
class
AnotherExampleView
(
View
):
...
...
examples/sandbox/views.py
View file @
1cde31c8
...
...
@@ -19,7 +19,7 @@ class Sandbox(View):
For example, to get the default representation using curl:
bash: curl -X GET http://rest.ep.io/
Or, to get the plaintext documentation represention:
bash: curl -X GET http://rest.ep.io/ -H 'Accept: text/plain'
...
...
@@ -49,19 +49,19 @@ class Sandbox(View):
def
get
(
self
,
request
):
return
Response
([
{
'name'
:
'Simple Resource example'
,
'url'
:
reverse
(
'example-resource'
,
request
)},
'url'
:
reverse
(
'example-resource'
,
request
=
request
)},
{
'name'
:
'Simple ModelResource example'
,
'url'
:
reverse
(
'model-resource-root'
,
request
)},
'url'
:
reverse
(
'model-resource-root'
,
request
=
request
)},
{
'name'
:
'Simple Mixin-only example'
,
'url'
:
reverse
(
'mixin-view'
,
request
)},
{
'name'
:
'Object store API'
'url'
:
reverse
(
'object-store-root'
,
request
)},
'url'
:
reverse
(
'mixin-view'
,
request
=
request
)},
{
'name'
:
'Object store API'
,
'url'
:
reverse
(
'object-store-root'
,
request
=
request
)},
{
'name'
:
'Code highlighting API'
,
'url'
:
reverse
(
'pygments-root'
,
request
)},
'url'
:
reverse
(
'pygments-root'
,
request
=
request
)},
{
'name'
:
'Blog posts API'
,
'url'
:
reverse
(
'blog-posts-root'
,
request
)},
'url'
:
reverse
(
'blog-posts-root'
,
request
=
request
)},
{
'name'
:
'Permissions example'
,
'url'
:
reverse
(
'permissions-example'
,
request
)},
'url'
:
reverse
(
'permissions-example'
,
request
=
request
)},
{
'name'
:
'Simple request mixin example'
,
'url'
:
reverse
(
'request-example'
,
request
)}
'url'
:
reverse
(
'request-example'
,
request
=
request
)}
])
examples/urls.py
View file @
1cde31c8
from
django.conf.urls.defaults
import
patterns
,
include
from
django.conf.urls.defaults
import
patterns
,
include
,
url
from
sandbox.views
import
Sandbox
try
:
from
django.contrib.staticfiles.urls
import
staticfiles_urlpatterns
...
...
@@ -15,9 +15,7 @@ urlpatterns = patterns('',
(
r'^pygments/'
,
include
(
'pygments_api.urls'
)),
(
r'^blog-post/'
,
include
(
'blogpost.urls'
)),
(
r'^permissions-example/'
,
include
(
'permissionsexample.urls'
)),
(
r'^request-example/'
,
include
(
'requestexample.urls'
)),
(
r'^'
,
include
(
'djangorestframework.urls'
)),
url
(
r'^restframework/'
,
include
(
'djangorestframework.urls'
,
namespace
=
'djangorestframework'
)),
)
urlpatterns
+=
staticfiles_urlpatterns
()
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