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
73cc7755
Commit
73cc7755
authored
Aug 26, 2012
by
Tom Christie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Drop ImmediateResponse
parent
edd8f596
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
56 additions
and
53 deletions
+56
-53
djangorestframework/exceptions.py
+25
-8
djangorestframework/permissions.py
+6
-12
djangorestframework/response.py
+0
-22
djangorestframework/tests/response.py
+1
-1
djangorestframework/tests/throttling.py
+3
-3
djangorestframework/views.py
+21
-7
No files found.
djangorestframework/exceptions.py
View file @
73cc7755
"""
Handled exceptions raised by REST framework.
In addition Django's built in 403 and 404 exceptions are handled.
(`django.http.Http404` and `django.core.exceptions.PermissionDenied`)
"""
from
djangorestframework
import
status
from
djangorestframework
import
status
class
ParseError
(
Exception
):
class
ParseError
(
Exception
):
status_code
=
status
.
HTTP_400_BAD_REQUEST
status_code
=
status
.
HTTP_400_BAD_REQUEST
default_detail
=
'Malformed request'
default_detail
=
'Malformed request
.
'
def
__init__
(
self
,
detail
=
None
):
def
__init__
(
self
,
detail
=
None
):
self
.
detail
=
detail
or
self
.
default_detail
self
.
detail
=
detail
or
self
.
default_detail
...
@@ -11,7 +17,7 @@ class ParseError(Exception):
...
@@ -11,7 +17,7 @@ class ParseError(Exception):
class
PermissionDenied
(
Exception
):
class
PermissionDenied
(
Exception
):
status_code
=
status
.
HTTP_403_FORBIDDEN
status_code
=
status
.
HTTP_403_FORBIDDEN
default_detail
=
'You do not have permission to access this resource'
default_detail
=
'You do not have permission to access this resource
.
'
def
__init__
(
self
,
detail
=
None
):
def
__init__
(
self
,
detail
=
None
):
self
.
detail
=
detail
or
self
.
default_detail
self
.
detail
=
detail
or
self
.
default_detail
...
@@ -19,19 +25,30 @@ class PermissionDenied(Exception):
...
@@ -19,19 +25,30 @@ class PermissionDenied(Exception):
class
MethodNotAllowed
(
Exception
):
class
MethodNotAllowed
(
Exception
):
status_code
=
status
.
HTTP_405_METHOD_NOT_ALLOWED
status_code
=
status
.
HTTP_405_METHOD_NOT_ALLOWED
default_detail
=
"Method '
%
s' not allowed"
default_detail
=
"Method '
%
s' not allowed
.
"
def
__init__
(
self
,
method
,
detail
):
def
__init__
(
self
,
method
,
detail
=
None
):
self
.
detail
=
(
detail
or
self
.
default_detail
)
%
method
self
.
detail
=
(
detail
or
self
.
default_detail
)
%
method
class
UnsupportedMediaType
(
Exception
):
class
UnsupportedMediaType
(
Exception
):
status_code
=
status
.
HTTP_415_UNSUPPORTED_MEDIA_TYPE
status_code
=
status
.
HTTP_415_UNSUPPORTED_MEDIA_TYPE
default_detail
=
"Unsupported media type '
%
s' in request"
default_detail
=
"Unsupported media type '
%
s' in request
.
"
def
__init__
(
self
,
media_type
,
detail
=
None
):
def
__init__
(
self
,
media_type
,
detail
=
None
):
self
.
detail
=
(
detail
or
self
.
default_detail
)
%
media_type
self
.
detail
=
(
detail
or
self
.
default_detail
)
%
media_type
# class Throttled(Exception):
# def __init__(self, detail):
class
Throttled
(
Exception
):
# self.detail = detail
status_code
=
status
.
HTTP_429_TOO_MANY_REQUESTS
default_detail
=
"Request was throttled. Expected available in
%
d seconds."
def
__init__
(
self
,
wait
,
detail
=
None
):
import
math
self
.
detail
=
(
detail
or
self
.
default_detail
)
%
int
(
math
.
ceil
(
wait
))
REST_FRAMEWORK_EXCEPTIONS
=
(
ParseError
,
PermissionDenied
,
MethodNotAllowed
,
UnsupportedMediaType
,
Throttled
)
djangorestframework/permissions.py
View file @
73cc7755
...
@@ -6,9 +6,7 @@ Permission behavior is provided by mixing the :class:`mixins.PermissionsMixin` c
...
@@ -6,9 +6,7 @@ Permission behavior is provided by mixing the :class:`mixins.PermissionsMixin` c
"""
"""
from
django.core.cache
import
cache
from
django.core.cache
import
cache
from
djangorestframework
import
status
from
djangorestframework.exceptions
import
PermissionDenied
,
Throttled
from
djangorestframework.exceptions
import
PermissionDenied
from
djangorestframework.response
import
ImmediateResponse
import
time
import
time
__all__
=
(
__all__
=
(
...
@@ -24,11 +22,6 @@ __all__ = (
...
@@ -24,11 +22,6 @@ __all__ = (
SAFE_METHODS
=
[
'GET'
,
'HEAD'
,
'OPTIONS'
]
SAFE_METHODS
=
[
'GET'
,
'HEAD'
,
'OPTIONS'
]
_503_SERVICE_UNAVAILABLE
=
ImmediateResponse
(
{
'detail'
:
'request was throttled'
},
status
=
status
.
HTTP_503_SERVICE_UNAVAILABLE
)
class
BasePermission
(
object
):
class
BasePermission
(
object
):
"""
"""
A base class from which all permission classes should inherit.
A base class from which all permission classes should inherit.
...
@@ -192,7 +185,7 @@ class BaseThrottle(BasePermission):
...
@@ -192,7 +185,7 @@ class BaseThrottle(BasePermission):
"""
"""
self
.
history
.
insert
(
0
,
self
.
now
)
self
.
history
.
insert
(
0
,
self
.
now
)
cache
.
set
(
self
.
key
,
self
.
history
,
self
.
duration
)
cache
.
set
(
self
.
key
,
self
.
history
,
self
.
duration
)
header
=
'status=SUCCESS; next=
%
s
sec'
%
self
.
next
()
header
=
'status=SUCCESS; next=
%
.2
f
sec'
%
self
.
next
()
self
.
view
.
headers
[
'X-Throttle'
]
=
header
self
.
view
.
headers
[
'X-Throttle'
]
=
header
def
throttle_failure
(
self
):
def
throttle_failure
(
self
):
...
@@ -200,9 +193,10 @@ class BaseThrottle(BasePermission):
...
@@ -200,9 +193,10 @@ class BaseThrottle(BasePermission):
Called when a request to the API has failed due to throttling.
Called when a request to the API has failed due to throttling.
Raises a '503 service unavailable' response.
Raises a '503 service unavailable' response.
"""
"""
header
=
'status=FAILURE; next=
%
s sec'
%
self
.
next
()
wait
=
self
.
next
()
header
=
'status=FAILURE; next=
%.2
f sec'
%
wait
self
.
view
.
headers
[
'X-Throttle'
]
=
header
self
.
view
.
headers
[
'X-Throttle'
]
=
header
raise
_503_SERVICE_UNAVAILABLE
raise
Throttled
(
wait
)
def
next
(
self
):
def
next
(
self
):
"""
"""
...
@@ -215,7 +209,7 @@ class BaseThrottle(BasePermission):
...
@@ -215,7 +209,7 @@ class BaseThrottle(BasePermission):
available_requests
=
self
.
num_requests
-
len
(
self
.
history
)
+
1
available_requests
=
self
.
num_requests
-
len
(
self
.
history
)
+
1
return
'
%.2
f'
%
(
remaining_duration
/
float
(
available_requests
)
)
return
remaining_duration
/
float
(
available_requests
)
class
PerUserThrottling
(
BaseThrottle
):
class
PerUserThrottling
(
BaseThrottle
):
...
...
djangorestframework/response.py
View file @
73cc7755
...
@@ -161,25 +161,3 @@ class Response(SimpleTemplateResponse):
...
@@ -161,25 +161,3 @@ class Response(SimpleTemplateResponse):
},
},
status
=
status
.
HTTP_406_NOT_ACCEPTABLE
,
status
=
status
.
HTTP_406_NOT_ACCEPTABLE
,
view
=
self
.
view
,
request
=
self
.
request
,
renderers
=
[
renderer
])
view
=
self
.
view
,
request
=
self
.
request
,
renderers
=
[
renderer
])
class
ImmediateResponse
(
Response
,
Exception
):
"""
An exception representing an Response that should be returned immediately.
Any content should be serialized as-is, without being filtered.
"""
#TODO: this is just a temporary fix, the whole rendering/support for ImmediateResponse, should be remade : see issue #163
def
render
(
self
):
try
:
return
super
(
Response
,
self
)
.
render
()
except
ImmediateResponse
:
renderer
,
media_type
=
self
.
_determine_renderer
()
self
.
renderers
.
remove
(
renderer
)
if
len
(
self
.
renderers
)
==
0
:
raise
RuntimeError
(
'Caught an ImmediateResponse while '
\
'trying to render an ImmediateResponse'
)
return
self
.
render
()
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
response
=
Response
(
*
args
,
**
kwargs
)
djangorestframework/tests/response.py
View file @
73cc7755
...
@@ -4,7 +4,7 @@ import unittest
...
@@ -4,7 +4,7 @@ import unittest
from
django.conf.urls.defaults
import
patterns
,
url
,
include
from
django.conf.urls.defaults
import
patterns
,
url
,
include
from
django.test
import
TestCase
from
django.test
import
TestCase
from
djangorestframework.response
import
Response
,
NotAcceptable
,
ImmediateResponse
from
djangorestframework.response
import
Response
,
NotAcceptable
from
djangorestframework.views
import
View
from
djangorestframework.views
import
View
from
djangorestframework.compat
import
RequestFactory
from
djangorestframework.compat
import
RequestFactory
from
djangorestframework
import
status
from
djangorestframework
import
status
...
...
djangorestframework/tests/throttling.py
View file @
73cc7755
...
@@ -45,7 +45,7 @@ class ThrottlingTests(TestCase):
...
@@ -45,7 +45,7 @@ class ThrottlingTests(TestCase):
request
=
self
.
factory
.
get
(
'/'
)
request
=
self
.
factory
.
get
(
'/'
)
for
dummy
in
range
(
4
):
for
dummy
in
range
(
4
):
response
=
MockView
.
as_view
()(
request
)
response
=
MockView
.
as_view
()(
request
)
self
.
assertEqual
(
503
,
response
.
status_code
)
self
.
assertEqual
(
429
,
response
.
status_code
)
def
set_throttle_timer
(
self
,
view
,
value
):
def
set_throttle_timer
(
self
,
view
,
value
):
"""
"""
...
@@ -62,7 +62,7 @@ class ThrottlingTests(TestCase):
...
@@ -62,7 +62,7 @@ class ThrottlingTests(TestCase):
request
=
self
.
factory
.
get
(
'/'
)
request
=
self
.
factory
.
get
(
'/'
)
for
dummy
in
range
(
4
):
for
dummy
in
range
(
4
):
response
=
MockView
.
as_view
()(
request
)
response
=
MockView
.
as_view
()(
request
)
self
.
assertEqual
(
503
,
response
.
status_code
)
self
.
assertEqual
(
429
,
response
.
status_code
)
# Advance the timer by one second
# Advance the timer by one second
self
.
set_throttle_timer
(
MockView
,
1
)
self
.
set_throttle_timer
(
MockView
,
1
)
...
@@ -90,7 +90,7 @@ class ThrottlingTests(TestCase):
...
@@ -90,7 +90,7 @@ class ThrottlingTests(TestCase):
"""
"""
Ensure request rate is limited globally per View for PerViewThrottles
Ensure request rate is limited globally per View for PerViewThrottles
"""
"""
self
.
ensure_is_throttled
(
MockView_PerViewThrottling
,
503
)
self
.
ensure_is_throttled
(
MockView_PerViewThrottling
,
429
)
def
ensure_response_header_contains_proper_throttle_field
(
self
,
view
,
expected_headers
):
def
ensure_response_header_contains_proper_throttle_field
(
self
,
view
,
expected_headers
):
"""
"""
...
...
djangorestframework/views.py
View file @
73cc7755
...
@@ -6,12 +6,14 @@ By setting or modifying class attributes on your view, you change it's predefine
...
@@ -6,12 +6,14 @@ By setting or modifying class attributes on your view, you change it's predefine
"""
"""
import
re
import
re
from
django.core.exceptions
import
PermissionDenied
from
django.http
import
Http404
from
django.utils.html
import
escape
from
django.utils.html
import
escape
from
django.utils.safestring
import
mark_safe
from
django.utils.safestring
import
mark_safe
from
django.views.decorators.csrf
import
csrf_exempt
from
django.views.decorators.csrf
import
csrf_exempt
from
djangorestframework.compat
import
View
as
DjangoView
,
apply_markdown
from
djangorestframework.compat
import
View
as
DjangoView
,
apply_markdown
from
djangorestframework.response
import
Response
,
ImmediateResponse
from
djangorestframework.response
import
Response
from
djangorestframework.request
import
Request
from
djangorestframework.request
import
Request
from
djangorestframework
import
renderers
,
parsers
,
authentication
,
permissions
,
status
,
exceptions
from
djangorestframework
import
renderers
,
parsers
,
authentication
,
permissions
,
status
,
exceptions
...
@@ -219,13 +221,27 @@ class View(DjangoView):
...
@@ -219,13 +221,27 @@ class View(DjangoView):
response
[
key
]
=
value
response
[
key
]
=
value
return
response
return
response
def
handle_exception
(
self
,
exc
):
"""
Handle any exception that occurs, by returning an appropriate response,
or re-raising the error.
"""
if
isinstance
(
exc
,
exceptions
.
REST_FRAMEWORK_EXCEPTIONS
):
return
Response
({
'detail'
:
exc
.
detail
},
status
=
exc
.
status_code
)
elif
isinstance
(
exc
,
Http404
):
return
Response
({
'detail'
:
'Not found'
},
status
=
status
.
HTTP_404_NOT_FOUND
)
elif
isinstance
(
exc
,
PermissionDenied
):
return
Response
({
'detail'
:
'Permission denied'
},
status
=
status
.
HTTP_403_FORBIDDEN
)
raise
# Note: session based authentication is explicitly CSRF validated,
# Note: session based authentication is explicitly CSRF validated,
# all other authentication is CSRF exempt.
# all other authentication is CSRF exempt.
@csrf_exempt
@csrf_exempt
def
dispatch
(
self
,
request
,
*
args
,
**
kwargs
):
def
dispatch
(
self
,
request
,
*
args
,
**
kwargs
):
request
=
Request
(
request
,
parsers
=
self
.
parsers
,
authentication
=
self
.
authentication
)
request
=
Request
(
request
,
parsers
=
self
.
parsers
,
authentication
=
self
.
authentication
)
self
.
request
=
request
self
.
request
=
request
self
.
args
=
args
self
.
args
=
args
self
.
kwargs
=
kwargs
self
.
kwargs
=
kwargs
self
.
headers
=
self
.
default_response_headers
self
.
headers
=
self
.
default_response_headers
...
@@ -244,10 +260,8 @@ class View(DjangoView):
...
@@ -244,10 +260,8 @@ class View(DjangoView):
response
=
handler
(
request
,
*
args
,
**
kwargs
)
response
=
handler
(
request
,
*
args
,
**
kwargs
)
except
ImmediateResponse
,
exc
:
except
Exception
as
exc
:
response
=
exc
.
response
response
=
self
.
handle_exception
(
exc
)
except
(
exceptions
.
ParseError
,
exceptions
.
PermissionDenied
)
as
exc
:
response
=
Response
({
'detail'
:
exc
.
detail
},
status
=
exc
.
status_code
)
self
.
response
=
self
.
final
(
request
,
response
,
*
args
,
**
kwargs
)
self
.
response
=
self
.
final
(
request
,
response
,
*
args
,
**
kwargs
)
return
self
.
response
return
self
.
response
...
@@ -265,4 +279,4 @@ class View(DjangoView):
...
@@ -265,4 +279,4 @@ class View(DjangoView):
for
name
,
field
in
form
.
fields
.
iteritems
():
for
name
,
field
in
form
.
fields
.
iteritems
():
field_name_types
[
name
]
=
field
.
__class__
.
__name__
field_name_types
[
name
]
=
field
.
__class__
.
__name__
content
[
'fields'
]
=
field_name_types
content
[
'fields'
]
=
field_name_types
raise
Immediate
Response
(
content
,
status
=
status
.
HTTP_200_OK
)
raise
Response
(
content
,
status
=
status
.
HTTP_200_OK
)
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