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
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
45 changed files
with
656 additions
and
663 deletions
+656
-663
AUTHORS
+1
-0
djangorestframework/__init__.py
+1
-1
djangorestframework/authentication.py
+0
-2
djangorestframework/compat.py
+6
-10
djangorestframework/mixins.py
+25
-82
djangorestframework/parsers.py
+21
-24
djangorestframework/renderers.py
+15
-28
djangorestframework/request.py
+83
-74
djangorestframework/resources.py
+10
-54
djangorestframework/response.py
+52
-56
djangorestframework/reverse.py
+8
-11
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
+4
-2
djangorestframework/tests/renderers.py
+167
-23
djangorestframework/tests/request.py
+0
-0
djangorestframework/tests/response.py
+18
-25
djangorestframework/tests/reverse.py
+2
-1
djangorestframework/tests/views.py
+26
-28
djangorestframework/urls.py
+7
-4
djangorestframework/utils/__init__.py
+38
-66
djangorestframework/utils/staticviews.py
+0
-61
djangorestframework/views.py
+33
-33
docs/howto/setup.rst
+8
-8
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
+0
-3
examples/pygments_api/views.py
+14
-9
examples/requestexample/views.py
+1
-2
examples/resourceexample/forms.py
+1
-0
examples/resourceexample/views.py
+4
-2
examples/sandbox/views.py
+9
-9
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,6 +428,7 @@ try:
except
ImportError
:
yaml
=
None
import
unittest
try
:
import
unittest.skip
...
...
@@ -458,10 +455,9 @@ except ImportError: # python < 2.7
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,38 +38,32 @@ 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,15 +88,14 @@ class JSONParser(BaseParser):
status
=
status
.
HTTP_400_BAD_REQUEST
)
if
yaml
:
class
YAMLParser
(
BaseParser
):
class
YAMLParser
(
BaseParser
):
"""
Parses YAML-serialized data.
"""
media_type
=
'application/yaml'
def
parse
(
self
,
stream
):
def
parse
(
self
,
stream
,
meta
,
upload_handlers
):
"""
Returns a 2-tuple of `(data, files)`.
...
...
@@ -114,8 +108,6 @@ if yaml:
raise
ImmediateResponse
(
{
'detail'
:
'YAML parse error -
%
s'
%
unicode
(
exc
)},
status
=
status
.
HTTP_400_BAD_REQUEST
)
else
:
YAMLParser
=
None
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`.
"""
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,8 +154,7 @@ class XMLRenderer(BaseRenderer):
return
dict2xml
(
obj
)
if
yaml
:
class
YAMLRenderer
(
BaseRenderer
):
class
YAMLRenderer
(
BaseRenderer
):
"""
Renderer which serializes to YAML.
"""
...
...
@@ -173,8 +170,6 @@ if yaml:
return
''
return
yaml
.
safe_dump
(
obj
)
else
:
YAMLRenderer
=
None
class
TemplateRenderer
(
BaseRenderer
):
...
...
@@ -218,7 +213,7 @@ 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
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
This diff is collapsed.
Click to expand it.
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
.
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
},
raise
NotAcceptable
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
,
renderers
=
self
.
renderers
)
def
_get_renderers
(
self
):
if
hasattr
(
self
,
'_renderers'
):
return
self
.
_renderers
return
()
def
_set_renderers
(
self
,
value
):
self
.
_renderers
=
value
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
]
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
)
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
(
...
...
djangorestframework/tests/renderers.py
View file @
1cde31c8
This diff is collapsed.
Click to expand it.
djangorestframework/tests/request.py
View file @
1cde31c8
This diff is collapsed.
Click to expand it.
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
)
...
...
@@ -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'
)
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,8 +54,8 @@ 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
():
...
...
@@ -116,7 +71,6 @@ class XML2Dict(object):
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
):
"""
Do the same as :py:func:`django.core.urlresolvers.reverse` but using
*request* to build a fully qualified URL.
"""
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
):
class
RequestFactory
(
DjangoRequestFactory
):
"""
Do the same as :py:func:`django.core.urlresolvers.reverse_lazy` but using
*request* to build a fully qualified URL.
Replicate RequestFactory, but return Request, not HttpRequest.
"""
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
)
if
isinstance
(
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'
):
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,19 +49,19 @@ 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/
...
...
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
...
...
@@ -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
})
resource_urls
=
[
reverse
(
'another-example'
,
kwargs
=
{
'num'
:
num
},
request
=
request
)
for
num
in
range
(
3
)]
return
Response
({
"Some other resources"
:
urls
})
return
Response
({
"Some other resources"
:
resource_
urls
})
class
AnotherExampleView
(
View
):
...
...
examples/sandbox/views.py
View file @
1cde31c8
...
...
@@ -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