diff options
Diffstat (limited to 'djangorestframework/resource.py')
| -rw-r--r-- | djangorestframework/resource.py | 186 | 
1 files changed, 0 insertions, 186 deletions
diff --git a/djangorestframework/resource.py b/djangorestframework/resource.py deleted file mode 100644 index 80e5df2a..00000000 --- a/djangorestframework/resource.py +++ /dev/null @@ -1,186 +0,0 @@ -from django.core.urlresolvers import set_script_prefix -from django.views.decorators.csrf import csrf_exempt - -from djangorestframework.compat import View -from djangorestframework.emitters import EmitterMixin -from djangorestframework.parsers import ParserMixin -from djangorestframework.authenticators import AuthenticatorMixin -from djangorestframework.validators import FormValidatorMixin -from djangorestframework.response import Response, ResponseException -from djangorestframework.request import RequestMixin -from djangorestframework import emitters, parsers, authenticators, status - - -# TODO: Figure how out references and named urls need to work nicely -# TODO: POST on existing 404 URL, PUT on existing 404 URL -# -# NEXT: Exceptions on func() -> 500, tracebacks emitted if settings.DEBUG - -__all__ = ['Resource'] - - -class Resource(EmitterMixin, ParserMixin, AuthenticatorMixin, FormValidatorMixin, RequestMixin, View): -    """Handles incoming requests and maps them to REST operations, -    performing authentication, input deserialization, input validation, output serialization.""" - -    # List of RESTful operations which may be performed on this resource. -    # These are going to get dropped at some point, the allowable methods will be defined simply by -    # which methods are present on the request (in the same way as Django's generic View) -    allowed_methods = ('GET',) -    anon_allowed_methods = () - -    # List of emitters the resource can serialize the response with, ordered by preference. -    emitters = ( emitters.JSONEmitter, -                 emitters.DocumentingHTMLEmitter, -                 emitters.DocumentingXHTMLEmitter, -                 emitters.DocumentingPlainTextEmitter, -                 emitters.XMLEmitter ) - -    # List of parsers the resource can parse the request with. -    parsers = ( parsers.JSONParser, -                parsers.FormParser, -                parsers.MultipartParser ) -     -    # List of all authenticating methods to attempt. -    authenticators = ( authenticators.UserLoggedInAuthenticator, -                       authenticators.BasicAuthenticator ) - -    # Optional form for input validation and presentation of HTML formatted responses. -    form = None - -    # Allow name and description for the Resource to be set explicitly, -    # overiding the default classname/docstring behaviour. -    # These are used for documentation in the standard html and text emitters. -    name = None -    description = None - -    # Map standard HTTP methods to function calls -    callmap = { 'GET': 'get', 'POST': 'post',  -                'PUT': 'put', 'DELETE': 'delete' } - -    def get(self, request, auth, *args, **kwargs): -        """Must be subclassed to be implemented.""" -        self.not_implemented('GET') - - -    def post(self, request, auth, content, *args, **kwargs): -        """Must be subclassed to be implemented.""" -        self.not_implemented('POST') - - -    def put(self, request, auth, content, *args, **kwargs): -        """Must be subclassed to be implemented.""" -        self.not_implemented('PUT') - - -    def delete(self, request, auth, *args, **kwargs): -        """Must be subclassed to be implemented.""" -        self.not_implemented('DELETE') - - -    def not_implemented(self, operation): -        """Return an HTTP 500 server error if an operation is called which has been allowed by -        allowed_methods, but which has not been implemented.""" -        raise ResponseException(status.HTTP_500_INTERNAL_SERVER_ERROR, -                                {'detail': '%s operation on this resource has not been implemented' % (operation, )}) - - -    def check_method_allowed(self, method, auth): -        """Ensure the request method is permitted for this resource, raising a ResourceException if it is not.""" - -        if not method in self.callmap.keys(): -            raise ResponseException(status.HTTP_501_NOT_IMPLEMENTED, -                                    {'detail': 'Unknown or unsupported method \'%s\'' % method}) - -        if not method in self.allowed_methods: -            raise ResponseException(status.HTTP_405_METHOD_NOT_ALLOWED, -                                    {'detail': 'Method \'%s\' not allowed on this resource.' % method}) - -        if auth is None and not method in self.anon_allowed_methods: -            raise ResponseException(status.HTTP_403_FORBIDDEN, -                                    {'detail': 'You do not have permission to access this resource. ' + -                                     'You may need to login or otherwise authenticate the request.'}) - - -    def cleanup_response(self, data): -        """Perform any resource-specific data filtering prior to the standard HTTP -        content-type serialization. - -        Eg filter complex objects that cannot be serialized by json/xml/etc into basic objects that can. -         -        TODO: This is going to be removed.  I think that the 'fields' behaviour is going to move into -        the EmitterMixin and Emitter classes.""" -        return data - -    # Session based authentication is explicitly CSRF validated, all other authentication is CSRF exempt. -    @csrf_exempt -    def dispatch(self, request, *args, **kwargs): -        """This method is the core of Resource, through which all requests are passed. - -        Broadly this consists of the following procedure: - -        0. ensure the operation is permitted -        1. deserialize request content into request data, using standard HTTP content types (PUT/POST only) -        2. cleanup and validate request data (PUT/POST only) -        3. call the core method to get the response data -        4. cleanup the response data -        5. serialize response data into response content, using standard HTTP content negotiation -        """ - -        self.request = request - -        # Calls to 'reverse' will not be fully qualified unless we set the scheme/host/port here. -        prefix = '%s://%s' % (request.is_secure() and 'https' or 'http', request.get_host()) -        set_script_prefix(prefix) - -        try: -            # Authenticate the request, and store any context so that the resource operations can -            # do more fine grained authentication if required. -            # -            # Typically the context will be a user, or None if this is an anonymous request, -            # but it could potentially be more complex (eg the context of a request key which -            # has been signed against a particular set of permissions) -            auth_context = self.authenticate(request) - -            # If using a form POST with '_method'/'_content'/'_content_type' overrides, then alter -            # self.method, self.content_type, self.CONTENT appropriately. -            self.perform_form_overloading() - -            # Ensure the requested operation is permitted on this resource -            self.check_method_allowed(self.method, auth_context) - -            # Get the appropriate create/read/update/delete function -            func = getattr(self, self.callmap.get(self.method, None)) -     -            # Either generate the response data, deserializing and validating any request data -            # TODO: This is going to change to: func(request, *args, **kwargs) -            # That'll work out now that we have the lazily evaluated self.CONTENT property. -            if self.method in ('PUT', 'POST'): -                response_obj = func(request, auth_context, self.CONTENT, *args, **kwargs) - -            else: -                response_obj = func(request, auth_context, *args, **kwargs) - -            # Allow return value to be either Response, or an object, or None -            if isinstance(response_obj, Response): -                response = response_obj -            elif response_obj is not None: -                response = Response(status.HTTP_200_OK, response_obj) -            else: -                response = Response(status.HTTP_204_NO_CONTENT) - -            # Pre-serialize filtering (eg filter complex objects into natively serializable types) -            response.cleaned_content = self.cleanup_response(response.raw_content) - -        except ResponseException, exc: -            response = exc.response - -        # Always add these headers. -        # -        # TODO - this isn't actually the correct way to set the vary header, -        # also it's currently sub-obtimal for HTTP caching - need to sort that out.  -        response.headers['Allow'] = ', '.join(self.allowed_methods) -        response.headers['Vary'] = 'Authenticate, Accept' - -        return self.emit(response) -  | 
