mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-02-09 08:00:52 +03:00
Refactoring some basics
This commit is contained in:
parent
d52b4c5c61
commit
1c78bf53db
|
@ -39,13 +39,14 @@ class BaseAuthentication(object):
|
||||||
|
|
||||||
class BasicAuthentication(BaseAuthentication):
|
class BasicAuthentication(BaseAuthentication):
|
||||||
"""
|
"""
|
||||||
Use HTTP Basic authentication.
|
Base class for HTTP Basic authentication.
|
||||||
|
Subclasses should implement `.authenticate_credentials()`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def authenticate(self, request):
|
def authenticate(self, request):
|
||||||
"""
|
"""
|
||||||
Returns a :obj:`User` if a correct username and password have been supplied
|
Returns a `User` if a correct username and password have been supplied
|
||||||
using HTTP Basic authentication. Otherwise returns :const:`None`.
|
using HTTP Basic authentication. Otherwise returns `None`.
|
||||||
"""
|
"""
|
||||||
from django.utils.encoding import smart_unicode, DjangoUnicodeDecodeError
|
from django.utils.encoding import smart_unicode, DjangoUnicodeDecodeError
|
||||||
|
|
||||||
|
@ -58,14 +59,29 @@ class BasicAuthentication(BaseAuthentication):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
uname, passwd = smart_unicode(auth_parts[0]), smart_unicode(auth_parts[2])
|
userid, password = smart_unicode(auth_parts[0]), smart_unicode(auth_parts[2])
|
||||||
except DjangoUnicodeDecodeError:
|
except DjangoUnicodeDecodeError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
user = authenticate(username=uname, password=passwd)
|
return self.authenticate_credentials(userid, password)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def authenticate_credentials(self, userid, password):
|
||||||
|
"""
|
||||||
|
Given the Basic authentication userid and password, authenticate
|
||||||
|
and return a user instance.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError('.authenticate_credentials() must be overridden')
|
||||||
|
|
||||||
|
|
||||||
|
class UserBasicAuthentication(BasicAuthentication):
|
||||||
|
def authenticate_credentials(self, userid, password):
|
||||||
|
"""
|
||||||
|
Authenticate the userid and password against username and password.
|
||||||
|
"""
|
||||||
|
user = authenticate(username=userid, password=password)
|
||||||
if user is not None and user.is_active:
|
if user is not None and user.is_active:
|
||||||
return user
|
return user
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class SessionAuthentication(BaseAuthentication):
|
class SessionAuthentication(BaseAuthentication):
|
||||||
|
|
|
@ -25,7 +25,7 @@ class ParseError(APIException):
|
||||||
|
|
||||||
class PermissionDenied(APIException):
|
class PermissionDenied(APIException):
|
||||||
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 perform this action.'
|
||||||
|
|
||||||
def __init__(self, detail=None):
|
def __init__(self, detail=None):
|
||||||
self.detail = detail or self.default_detail
|
self.detail = detail or self.default_detail
|
||||||
|
|
|
@ -52,7 +52,7 @@ class IsAdminUser(BasePermission):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def check_permission(self, request, obj=None):
|
def check_permission(self, request, obj=None):
|
||||||
if request.user and request.user.is_staff():
|
if request.user and request.user.is_staff:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ class DjangoModelPermissions(BasePermission):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Map methods into required permission codes.
|
# Map methods into required permission codes.
|
||||||
# Override this if you need to also provide 'read' permissions,
|
# Override this if you need to also provide 'view' permissions,
|
||||||
# or if you want to provide custom permission codes.
|
# or if you want to provide custom permission codes.
|
||||||
perms_map = {
|
perms_map = {
|
||||||
'GET': [],
|
'GET': [],
|
||||||
|
|
|
@ -144,9 +144,9 @@ class Response(SimpleTemplateResponse):
|
||||||
# attempting more specific media types first
|
# attempting more specific media types first
|
||||||
# NB. The inner loop here isn't as bad as it first looks :)
|
# 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)
|
# Worst case is we're looping over len(accept_list) * len(self.renderers)
|
||||||
for media_type_list in order_by_precedence(accepts):
|
for media_type_set in order_by_precedence(accepts):
|
||||||
for renderer in renderers:
|
for renderer in renderers:
|
||||||
for media_type in media_type_list:
|
for media_type in media_type_set:
|
||||||
if renderer.can_handle_response(media_type):
|
if renderer.can_handle_response(media_type):
|
||||||
return renderer, media_type
|
return renderer, media_type
|
||||||
|
|
||||||
|
|
|
@ -246,9 +246,9 @@ class JSONPRendererTests(TestCase):
|
||||||
Test JSONP rendering with View JSON Renderer.
|
Test JSONP rendering with View JSON Renderer.
|
||||||
"""
|
"""
|
||||||
resp = self.client.get('/jsonp/jsonrenderer',
|
resp = self.client.get('/jsonp/jsonrenderer',
|
||||||
HTTP_ACCEPT='application/json-p')
|
HTTP_ACCEPT='application/javascript')
|
||||||
self.assertEquals(resp.status_code, 200)
|
self.assertEquals(resp.status_code, 200)
|
||||||
self.assertEquals(resp['Content-Type'], 'application/json-p')
|
self.assertEquals(resp['Content-Type'], 'application/javascript')
|
||||||
self.assertEquals(resp.content, 'callback(%s);' % _flat_repr)
|
self.assertEquals(resp.content, 'callback(%s);' % _flat_repr)
|
||||||
|
|
||||||
def test_without_callback_without_json_renderer(self):
|
def test_without_callback_without_json_renderer(self):
|
||||||
|
@ -256,9 +256,9 @@ class JSONPRendererTests(TestCase):
|
||||||
Test JSONP rendering without View JSON Renderer.
|
Test JSONP rendering without View JSON Renderer.
|
||||||
"""
|
"""
|
||||||
resp = self.client.get('/jsonp/nojsonrenderer',
|
resp = self.client.get('/jsonp/nojsonrenderer',
|
||||||
HTTP_ACCEPT='application/json-p')
|
HTTP_ACCEPT='application/javascript')
|
||||||
self.assertEquals(resp.status_code, 200)
|
self.assertEquals(resp.status_code, 200)
|
||||||
self.assertEquals(resp['Content-Type'], 'application/json-p')
|
self.assertEquals(resp['Content-Type'], 'application/javascript')
|
||||||
self.assertEquals(resp.content, 'callback(%s);' % _flat_repr)
|
self.assertEquals(resp.content, 'callback(%s);' % _flat_repr)
|
||||||
|
|
||||||
def test_with_callback(self):
|
def test_with_callback(self):
|
||||||
|
@ -267,9 +267,9 @@ class JSONPRendererTests(TestCase):
|
||||||
"""
|
"""
|
||||||
callback_func = 'myjsonpcallback'
|
callback_func = 'myjsonpcallback'
|
||||||
resp = self.client.get('/jsonp/nojsonrenderer?callback=' + callback_func,
|
resp = self.client.get('/jsonp/nojsonrenderer?callback=' + callback_func,
|
||||||
HTTP_ACCEPT='application/json-p')
|
HTTP_ACCEPT='application/javascript')
|
||||||
self.assertEquals(resp.status_code, 200)
|
self.assertEquals(resp.status_code, 200)
|
||||||
self.assertEquals(resp['Content-Type'], 'application/json-p')
|
self.assertEquals(resp['Content-Type'], 'application/javascript')
|
||||||
self.assertEquals(resp.content, '%s(%s);' % (callback_func, _flat_repr))
|
self.assertEquals(resp.content, '%s(%s);' % (callback_func, _flat_repr))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,15 +21,6 @@ from djangorestframework.settings import api_settings
|
||||||
from djangorestframework import parsers, authentication, status, exceptions, mixins
|
from djangorestframework import parsers, authentication, status, exceptions, mixins
|
||||||
|
|
||||||
|
|
||||||
__all__ = (
|
|
||||||
'View',
|
|
||||||
'ModelView',
|
|
||||||
'InstanceModelView',
|
|
||||||
'ListModelView',
|
|
||||||
'ListOrCreateModelView'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _remove_trailing_string(content, trailing):
|
def _remove_trailing_string(content, trailing):
|
||||||
"""
|
"""
|
||||||
Strip trailing component `trailing` from `content` if it exists.
|
Strip trailing component `trailing` from `content` if it exists.
|
||||||
|
@ -65,11 +56,6 @@ def _camelcase_to_spaces(content):
|
||||||
|
|
||||||
|
|
||||||
class APIView(_View):
|
class APIView(_View):
|
||||||
"""
|
|
||||||
Handles incoming requests and maps them to REST operations.
|
|
||||||
Performs request deserialization, response serialization, authentication and input validation.
|
|
||||||
"""
|
|
||||||
|
|
||||||
renderers = api_settings.DEFAULT_RENDERERS
|
renderers = api_settings.DEFAULT_RENDERERS
|
||||||
"""
|
"""
|
||||||
List of renderer classes the view can serialize the response with, ordered by preference.
|
List of renderer classes the view can serialize the response with, ordered by preference.
|
||||||
|
@ -81,7 +67,7 @@ class APIView(_View):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
authentication = (authentication.SessionAuthentication,
|
authentication = (authentication.SessionAuthentication,
|
||||||
authentication.BasicAuthentication)
|
authentication.UserBasicAuthentication)
|
||||||
"""
|
"""
|
||||||
List of all authenticating methods to attempt.
|
List of all authenticating methods to attempt.
|
||||||
"""
|
"""
|
||||||
|
@ -155,10 +141,21 @@ class APIView(_View):
|
||||||
def http_method_not_allowed(self, request, *args, **kwargs):
|
def http_method_not_allowed(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Called if `request.method` does not corrospond to a handler method.
|
Called if `request.method` does not corrospond to a handler method.
|
||||||
We raise an exception, which is handled by `.handle_exception()`.
|
|
||||||
"""
|
"""
|
||||||
raise exceptions.MethodNotAllowed(request.method)
|
raise exceptions.MethodNotAllowed(request.method)
|
||||||
|
|
||||||
|
def permission_denied(self, request):
|
||||||
|
"""
|
||||||
|
If request is not permitted, determine what kind of exception to raise.
|
||||||
|
"""
|
||||||
|
raise exceptions.PermissionDenied()
|
||||||
|
|
||||||
|
def throttled(self, request, wait):
|
||||||
|
"""
|
||||||
|
If request is throttled, determine what kind of exception to raise.
|
||||||
|
"""
|
||||||
|
raise exceptions.Throttled(wait)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _parsed_media_types(self):
|
def _parsed_media_types(self):
|
||||||
"""
|
"""
|
||||||
|
@ -208,35 +205,29 @@ class APIView(_View):
|
||||||
|
|
||||||
def check_permissions(self, request, obj=None):
|
def check_permissions(self, request, obj=None):
|
||||||
"""
|
"""
|
||||||
Check user permissions and either raise an ``PermissionDenied`` or return.
|
Check if request should be permitted.
|
||||||
"""
|
"""
|
||||||
for permission in self.get_permissions():
|
for permission in self.get_permissions():
|
||||||
if not permission.check_permission(request, obj):
|
if not permission.check_permission(request, obj):
|
||||||
raise exceptions.PermissionDenied()
|
self.permission_denied(request)
|
||||||
|
|
||||||
def check_throttles(self, request):
|
def check_throttles(self, request):
|
||||||
"""
|
"""
|
||||||
Check throttles and either raise a `Throttled` exception or return.
|
Check if request should be throttled.
|
||||||
"""
|
"""
|
||||||
for throttle in self.get_throttles():
|
for throttle in self.get_throttles():
|
||||||
if not throttle.check_throttle(request):
|
if not throttle.check_throttle(request):
|
||||||
raise exceptions.Throttled(throttle.wait())
|
self.throttled(request, throttle.wait())
|
||||||
|
|
||||||
def initial(self, request, *args, **kargs):
|
def initialize_request(self, request, *args, **kargs):
|
||||||
"""
|
"""
|
||||||
This method runs prior to anything else in the view.
|
Returns the initial request object.
|
||||||
It should return the initial request object.
|
|
||||||
|
|
||||||
You may need to override this if you want to do things like set
|
|
||||||
`request.upload_handlers` before the authentication and dispatch
|
|
||||||
handling is run.
|
|
||||||
"""
|
"""
|
||||||
return Request(request, parsers=self.parsers, authentication=self.authentication)
|
return Request(request, parsers=self.parsers, authentication=self.authentication)
|
||||||
|
|
||||||
def final(self, request, response, *args, **kargs):
|
def finalize_response(self, request, response, *args, **kargs):
|
||||||
"""
|
"""
|
||||||
This method runs after everything else in the view.
|
Returns the final response object.
|
||||||
It should return the final response object.
|
|
||||||
"""
|
"""
|
||||||
if isinstance(response, Response):
|
if isinstance(response, Response):
|
||||||
response.view = self
|
response.view = self
|
||||||
|
@ -248,6 +239,13 @@ class APIView(_View):
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
def initial(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Runs anything that needs to occur prior to calling the method handlers.
|
||||||
|
"""
|
||||||
|
self.check_permissions(request)
|
||||||
|
self.check_throttles(request)
|
||||||
|
|
||||||
def handle_exception(self, exc):
|
def handle_exception(self, exc):
|
||||||
"""
|
"""
|
||||||
Handle any exception that occurs, by returning an appropriate response,
|
Handle any exception that occurs, by returning an appropriate response,
|
||||||
|
@ -270,16 +268,24 @@ class APIView(_View):
|
||||||
# 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):
|
||||||
|
"""
|
||||||
|
`APIView.dispatch()` is pretty much the same as Django's regular
|
||||||
|
`View.dispatch()`, except that it includes hooks to:
|
||||||
|
|
||||||
|
* Initialize the request object.
|
||||||
|
* Finalize the response object.
|
||||||
|
* Handle exceptions that occur in the handler method.
|
||||||
|
* An initial hook for code such as permission checking that should
|
||||||
|
occur prior to running the method handlers.
|
||||||
|
"""
|
||||||
|
request = self.initialize_request(request, *args, **kwargs)
|
||||||
|
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
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.request = self.initial(request, *args, **kwargs)
|
self.initial(request, *args, **kwargs)
|
||||||
|
|
||||||
# Check that the request is allowed
|
|
||||||
self.check_permissions(request)
|
|
||||||
self.check_throttles(request)
|
|
||||||
|
|
||||||
# Get the appropriate handler method
|
# Get the appropriate handler method
|
||||||
if request.method.lower() in self.http_method_names:
|
if request.method.lower() in self.http_method_names:
|
||||||
|
@ -292,7 +298,7 @@ class APIView(_View):
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
response = self.handle_exception(exc)
|
response = self.handle_exception(exc)
|
||||||
|
|
||||||
self.response = self.final(request, response, *args, **kwargs)
|
self.response = self.finalize_response(request, response, *args, **kwargs)
|
||||||
return self.response
|
return self.response
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user