예제 #1
0
    def serialize_result(self, result, request, emitter_format):
        """
        The request has been executed succefully and we end up here to
        serialize the result

        @type result: dict
        @param result: Result of the execution of the handler, wrapped in a
        dictionary. The dictionary contains the I{data} key, whose value is the
        result of running the operation. The value can be another dictionary,
        list or simply a string. If it's a dictionary or list, it might contain
        other dictionaries/lists, strings, or even I{datetime} functions. All
        dictionary key/value pairs, and list elements, should be strings, other
        than the I{datetime} functions. In anycase, I{self.data} should be
        serializable very easily, without any magic.

        @type request: HTTPRequest
        @param request: Incoming request

        @type emitter_format: str
        @param emitter_format: Emitter format
        """
        # Find the Emitter class, and the corresponding content
        # type
        emitter_class, mimetype = Emitter.get(emitter_format)

        # create instance of the emitter class
        serializer = emitter_class(self.handler, result, None)
        # serialize the result
        serialized_result = serializer.render(request)

        return serialized_result, mimetype, emitter_format
예제 #2
0
    def __call__(self, request, *args, **kwargs):
        # Support emitter both through (?P<emitter_format>) and ?format=emitter.
        em_format = self.determine_emitter(request, *args, **kwargs)
        kwargs.pop('emitter_format', None)

        response = self.response_class()

        try:
            emitter, ct = Emitter.get(em_format)
        except ValueError:
            raise PistonBadRequestException("Invalid output format specified '%s'." % em_format)

        if not response.error_message:
            meth = None
            try:
                handler, meth, fields, anonymous = self.process_request(request, response, *args, **kwargs)
            except Exception, e:
                handler, meth, fields, anonymous = None, None, (), False
                self.error_handler(response, e, request, meth)
예제 #3
0
    def __call__(self, request, *args, **kwargs):
        # Support emitter both through (?P<emitter_format>) and ?format=emitter.
        em_format = self.determine_emitter(request, *args, **kwargs)
        kwargs.pop('emitter_format', None)

        response = self.response_class()

        try:
            emitter, ct = Emitter.get(em_format)
        except ValueError:
            raise PistonBadRequestException(
                "Invalid output format specified '%s'." % em_format)

        if not response.error_message:
            meth = None
            try:
                handler, meth, fields, anonymous = self.process_request(
                    request, response, *args, **kwargs)
            except Exception, e:
                handler, meth, fields, anonymous = None, None, (), False
                self.error_handler(response, e, request, meth)
예제 #4
0
    def form_validation_response(self, request, e, em_format):
        """
        Method to return form validation error information.
        You will probably want to override this in your own
        `Resource` subclass.
        """
        try:
            emitter, ct = Emitter.get(em_format)
            fields = self.handler.fields
        except ValueError:
            result = rc.BAD_REQUEST
            result.content = "Invalid output format specified '%s'." % em_format
            return result

        serialized_errors = dict(
            (key, [unicode(v) for v in values])
            for key,values in e.form.errors.items()
        )
        srl = emitter(serialized_errors, typemapper, self.handler, fields, False)
        stream = srl.render(request)
        resp = HttpResponse(stream, mimetype=ct, status=400)
        return resp
예제 #5
0
class Resource(object):
    """
    Resource. Create one for your URL mappings, just
    like you would with Django. Takes one argument,
    the handler. The second argument is optional, and
    is an authentication handler. If not specified,
    `NoAuthentication` will be used by default.
    """
    callmap = {
        'GET': 'read',
        'POST': 'create',
        'PUT': 'update',
        'DELETE': 'delete'
    }

    def __init__(self, handler, authentication=None):
        if not callable(handler):
            raise AttributeError("Handler not callable.")

        self.handler = handler()
        self.csrf_exempt = getattr(self.handler, 'csrf_exempt', True)

        if not authentication:
            self.authentication = (NoAuthentication(), )
        elif isinstance(authentication, (list, tuple)):
            self.authentication = authentication
        else:
            self.authentication = (authentication, )

        # Erroring
        self.email_errors = getattr(settings, 'PISTON_EMAIL_ERRORS', True)
        self.display_errors = getattr(settings, 'PISTON_DISPLAY_ERRORS', True)
        self.stream = getattr(settings, 'PISTON_STREAM_OUTPUT', False)

    def determine_emitter(self, request, *args, **kwargs):
        """
        Function for determening which emitter to use
        for output. It lives here so you can easily subclass
        `Resource` in order to change how emission is detected.

        You could also check for the `Accept` HTTP header here,
        since that pretty much makes sense. Refer to `Mimer` for
        that as well.
        """
        em = kwargs.pop('emitter_format', None)

        if not em:
            em = request.GET.get('format', 'json')

        return em

    def form_validation_response(self, e):
        """
        Method to return form validation error information.
        You will probably want to override this in your own
        `Resource` subclass.
        """
        resp = rc.BAD_REQUEST
        resp.write(u' ' + unicode(e.form.errors))
        return resp

    @property
    def anonymous(self):
        """
        Gets the anonymous handler. Also tries to grab a class
        if the `anonymous` value is a string, so that we can define
        anonymous handlers that aren't defined yet (like, when
        you're subclassing your basehandler into an anonymous one.)
        """
        if hasattr(self.handler, 'anonymous'):
            anon = self.handler.anonymous

            if callable(anon):
                return anon

            for klass in typemapper.keys():
                if anon == klass.__name__:
                    return klass

        return None

    def authenticate(self, request, rm):
        actor, anonymous = False, True

        for authenticator in self.authentication:
            if not authenticator.is_authenticated(request):
                if self.anonymous and \
                    rm in self.anonymous.allowed_methods:

                    actor, anonymous = self.anonymous(), True
                else:
                    actor, anonymous = authenticator.challenge, CHALLENGE
            else:
                return self.handler, self.handler.is_anonymous

        return actor, anonymous

    @vary_on_headers('Authorization')
    def __call__(self, request, *args, **kwargs):
        """
        NB: Sends a `Vary` header so we don't cache requests
        that are different (OAuth stuff in `Authorization` header.)
        """
        rm = request.method.upper()

        # Django's internal mechanism doesn't pick up
        # PUT request, so we trick it a little here.
        if rm == "PUT":
            coerce_put_post(request)

        actor, anonymous = self.authenticate(request, rm)

        if anonymous is CHALLENGE:
            return actor()
        else:
            handler = actor

        # Allow for emulated PUT requests -- http://backbonejs.org/#Sync-emulateHTTP
        if 'HTTP_X_HTTP_METHOD_OVERRIDE' in request.META:
            rm = request.META.get('HTTP_X_HTTP_METHOD_OVERRIDE')
            if rm == "PUT":
                request.PUT = request.POST

        # Translate nested datastructs into `request.data` here.
        if rm in ('POST', 'PUT'):
            try:
                translate_mime(request)
            except MimerDataException:
                return rc.BAD_REQUEST
            if not hasattr(request, 'data'):
                if rm == 'POST':
                    request.data = request.POST
                else:
                    request.data = request.PUT

        if not rm in handler.allowed_methods:
            return HttpResponseNotAllowed(handler.allowed_methods)

        meth = getattr(handler, self.callmap.get(rm, ''), None)
        if not meth:
            raise Http404

        # Support emitter both through (?P<emitter_format>) and ?format=emitter.
        em_format = self.determine_emitter(request, *args, **kwargs)

        kwargs.pop('emitter_format', None)

        # Clean up the request object a bit, since we might
        # very well have `oauth_`-headers in there, and we
        # don't want to pass these along to the handler.
        request = self.cleanup_request(request)

        try:
            result = meth(request, *args, **kwargs)
        except Exception, e:
            result = self.error_handler(e, request, meth, em_format)

        try:
            emitter, ct = Emitter.get(em_format)
            fields = handler.fields

            if hasattr(handler, 'list_fields') and isinstance(
                    result, (list, tuple, QuerySet, RawQuerySet)):
                fields = handler.list_fields
        except ValueError:
            result = rc.BAD_REQUEST
            result.content = "Invalid output format specified '%s'." % em_format
            return result

        status_code = 200

        # If we're looking at a response object which contains non-string
        # content, then assume we should use the emitter to format that
        # content
        if self._use_emitter(result):
            status_code = result.status_code
            # Note: We can't use result.content here because that
            # method attempts to convert the content into a string
            # which we don't want.  when
            # _is_string/_base_content_is_iter is False _container is
            # the raw data
            result = result._container

        srl = emitter(result, typemapper, handler, fields, anonymous)

        try:
            """
            Decide whether or not we want a generator here,
            or we just want to buffer up the entire result
            before sending it to the client. Won't matter for
            smaller datasets, but larger will have an impact.
            """
            if self.stream:
                stream = srl.stream_render(request)
            else:
                stream = srl.render(request)

            if not isinstance(stream, HttpResponse):
                resp = HttpResponse(stream,
                                    content_type=ct,
                                    status=status_code)
            else:
                resp = stream

            resp.streaming = self.stream

            return resp
        except HttpStatusCode, e:
            return e.response
예제 #6
0
 def reconfig(self, data):
     self.interval = int(data.get('interval', 60))
     if self.loghandler:
         logger.removeHandler(self.loghandler)
     fname = data.get('logfile', '/var/log/sauron.log')
     # Set up the logging file
     self.loghandler = logging.FileHandler(fname, mode='a')
     self.loghandler.setFormatter(formatter)
     self.loghandler.setLevel(logging.INFO)
     logger.addHandler(self.loghandler)
     
     # Read in /all/ the metrics!
     try:
         if len(data['metrics']) == 0:
             logger.error('No metrics in config file!')
             exit(1)
         for key,value in data['metrics'].items():
             try:
                 try:
                     d = dict(value.items())
                     self.metrics[key].reconfig(**d)
                 except:
                     module = value['module']
                     m = __import__('sauron.metrics.%s' % module)
                     m = getattr(m, 'metrics')
                     m = getattr(m, module)
                     c = getattr(m, module)
                     del d['module']
                     d['name'] = key
                     self.metrics[key] = c(**d)
             except KeyError:
                 logger.exception('No module listed for metric %s' % key)
                 exit(1)
             except ImportError:
                 logger.exception('Unable to import module %s' % module)
                 exit(1)
             except TypeError as e:
                 logger.exception('Unable to initialize metric %s' % key)
                 exit(1)
             except MetricException as e:
                 logger.exception('Module Exception %s' % module)
                 exit(1)
     except KeyError:
         logger.error('No metrics in config file!')
         exit(1)
     
     # Read in /all/ the emitters!
     try:
         if self.dryrun:
             logger.warn('Skipping all emitters because of --dry-run')
             self.emitters[''] = Emitter()
             return
         if len(data['emitters']) == 0:
             logger.error('No metrics in config file!')
             exit(1)
         for key,value in data['emitters'].items():
             try:
                 m = __import__('sauron.emitters.%s' % key)
                 m = getattr(m, 'emitters')
                 m = getattr(m, key)
                 c = getattr(m, key)
                 d = dict(value.items())
                 self.emitters[key] = c(**d)
             except ImportError:
                 logger.exception('Unable to import module %s' % key)
                 exit(1)
             except TypeError as e:
                 logger.exception('Unable to initialize emitter %s' % key)
                 exit(1)
             except EmitterException as e:
                 logger.exception('Error with module %s' % module)
                 exit(1)
     except:
         logger.exception('Emitter error!')
         exit(1)
예제 #7
0
                    assert range_start != None
                    assert range_end != None
                    return (range_start, range_end)

                try:
                    total = result.count()
                    start, end = get_range(request_range[0], request_range[1],
                                           total)
                    result = result[start:end + 1]
                    content_range = "items %i-%i/%i" % (start, end, total)
                except BadRangeException, e:
                    resp = rc.BAD_RANGE
                    resp.write("\n%s" % e.value)
                    return resp

        emitter, ct = Emitter.get(em_format)
        fields = handler.fields
        if hasattr(handler, 'list_fields') and (isinstance(result, list) or
                                                isinstance(result, QuerySet)):
            fields = handler.list_fields

        srl = emitter(result, typemapper, handler, fields, anonymous)

        try:
            """
            Decide whether or not we want a generator here,
            or we just want to buffer up the entire result
            before sending it to the client. Won't matter for
            smaller datasets, but larger will have an impact.
            """
            if self.stream: stream = srl.stream_render(request)
예제 #8
0
    def __call__(self, request, *args, **kwargs):
        """
        NB: Sends a `Vary` header so we don't cache requests
        that are different (OAuth stuff in `Authorization` header.)
        """
        rm = request.method.upper()

        # Django's internal mechanism doesn't pick up
        # PUT request, so we trick it a little here.
        if rm == "PUT":
            coerce_put_post(request)

        actor, anonymous = self.authenticate(request, rm)

        if anonymous is CHALLENGE:
            return actor()
        else:
            handler = actor

        # Translate nested datastructs into `request.data` here.
        if rm in ('POST', 'PUT'):
            try:
                translate_mime(request)
            except MimerDataException:
                return rc.BAD_REQUEST
            if not hasattr(request, 'data'):
                if rm == 'POST':
                    request.data = request.POST
                else:
                    request.data = request.PUT

        if not rm in handler.allowed_methods:
            return HttpResponseNotAllowed(handler.allowed_methods)

        meth = getattr(handler, self.callmap.get(rm, ''), None)
        if not meth:
            raise Http404

        # Support emitter through (?P<emitter_format>) and ?format=emitter
        # and lastly Accept: header processing
        em_format = self.determine_emitter(request, *args, **kwargs)
        if not em_format:
            request_has_accept = 'HTTP_ACCEPT' in request.META
            if request_has_accept and self.strict_accept:
                return rc.NOT_ACCEPTABLE
            em_format = self.default_emitter

        kwargs.pop('emitter_format', None)

        # Clean up the request object a bit, since we might
        # very well have `oauth_`-headers in there, and we
        # don't want to pass these along to the handler.
        request = self.cleanup_request(request)

        try:
            result = meth(request, *args, **kwargs)
        except Exception as e:
            result = self.error_handler(e, request, meth, em_format)

        try:
            emitter, ct = Emitter.get(em_format)
            fields = handler.fields

            if hasattr(handler, 'list_fields') and isinstance(
                    result, (list, tuple, QuerySet)):
                fields = handler.list_fields
        except ValueError:
            result = rc.BAD_REQUEST
            result.content = "Invalid output format specified '%s'." % em_format
            return result

        status_code = 200

        # If we're looking at a response object which contains non-string
        # content, then assume we should use the emitter to format that
        # content
        if self._use_emitter(result):
            status_code = result.status_code
            # Note: We can't use result.content here because that
            # method attempts to convert the content into a string
            # which we don't want.  when
            # _is_string/_base_content_is_iter is False _container is
            # the raw data
            result = result._container

        srl = emitter(result, typemapper, handler, fields, anonymous)

        try:
            """
            Decide whether or not we want a generator here,
            or we just want to buffer up the entire result
            before sending it to the client. Won't matter for
            smaller datasets, but larger will have an impact.
            """
            if self.stream: stream = srl.stream_render(request)
            else: stream = srl.render(request)

            if not isinstance(stream, HttpResponse):
                resp = HttpResponse(stream,
                                    content_type=ct,
                                    status=status_code)
            else:
                resp = stream

            resp.streaming = self.stream

            return resp
        except HttpStatusCode as e:
            return e.response
예제 #9
0
     receive a basic "500 Internal Server Error" message.
     """
     
     exc_type, exc_value, tb = sys.exc_info()
     rep = ExceptionReporter(request, exc_type, exc_value, tb.tb_next)
     
     if self.email_errors:
         self.email_exception(rep)
     if self.display_errors:
         return HttpResponseServerError(
             format_error('\n'.join(rep.format_exception())))
     else:
         raise
 
 # Return serialized data
 emitter, ct = Emitter.get(em_format)
 srl = emitter(result, recurse_level, typemapper, handler, handler.fields, anonymous)
 
 try:
     """
     Decide whether or not we want a generator here,
     or we just want to buffer up the entire result
     before sending it to the client. Won't matter for
     smaller datasets, but larger will have an impact.
     """
     if self.stream: stream = srl.stream_render(request)
     else: stream = srl.render(request)
     
     resp = HttpResponse(stream, mimetype=ct)
     resp.streaming = self.stream
     return resp
예제 #10
0
    def __call__(self, request, *args, **kwargs):
        """
        NB: Sends a `Vary` header so we don't cache requests
        that are different (OAuth stuff in `Authorization` header.)
        """
        rm = request.method.upper()

        # Django's internal mechanism doesn't pick up
        # PUT request, so we trick it a little here.
        if rm == "PUT":
            coerce_put_post(request)

        actor, anonymous = self.authenticate(request, rm)

        if anonymous is CHALLENGE:
            return actor(request)
        else:
            handler = actor

        # Translate nested datastructs into `request.data` here.
        if rm in ('POST', 'PUT'):
            try:
                translate_mime(request)
            except MimerDataException:
                return rc.BAD_REQUEST
            if not hasattr(request, 'data'):
                if rm == 'POST':
                    request.data = request.POST
                else:
                    request.data = request.PUT

        if not rm in handler.allowed_methods:
            return HttpResponseNotAllowed(handler.allowed_methods)

        meth = getattr(handler, self.callmap.get(rm, ''), None)
        if not meth:
            raise Http404

        # Support emitter both through (?P<emitter_format>) and ?format=emitter.
        em_format = self.determine_emitter(request, *args, **kwargs)

        kwargs.pop('emitter_format', None)

        # Clean up the request object a bit, since we might
        # very well have `oauth_`-headers in there, and we
        # don't want to pass these along to the handler.
        request = self.cleanup_request(request)

        result = meth(request, *args, **kwargs)

        try:
            emitter, ct = Emitter.get(em_format)
            fields = handler.fields

            if hasattr(handler, 'list_fields') and isinstance(result, (list, tuple, QuerySet)):
                fields = handler.list_fields
        except ValueError:
            result = rc.BAD_REQUEST
            result.content = "Invalid output format specified '%s'." % em_format
            return result

        status_code = 200

        # If we're looking at a response object which contains non-string
        # content, then assume we should use the emitter to format that
        # content
        if isinstance(result, HttpResponse) and not result._is_string:
            status_code = result.status_code
            # Note: We can't use result.content here because that method attempts
            # to convert the content into a string which we don't want.
            # when _is_string is False _container is the raw data
            result = result._container

        srl = emitter(result, typemapper, handler, fields, anonymous)

        try:
            """
            Decide whether or not we want a generator here,
            or we just want to buffer up the entire result
            before sending it to the client. Won't matter for
            smaller datasets, but larger will have an impact.
            """
            if self.stream: stream = srl.stream_render(request)
            else: stream = srl.render(request)

            if not isinstance(stream, HttpResponse):
                resp = HttpResponse(stream, mimetype=ct, status=status_code)
            else:
                resp = stream

            resp.streaming = self.stream

            return resp
        except HttpStatusCode, e:
            return e.response
예제 #11
0
               to the caller, so he can tell you what error they got.

            If `PISTON_DISPLAY_ERRORS` is not enabled, the caller will
            receive a basic "500 Internal Server Error" message.
            """
            exc_type, exc_value, tb = sys.exc_info()
            rep = ExceptionReporter(request, exc_type, exc_value, tb.tb_next)
            if self.email_errors:
                self.email_exception(rep)
            if self.display_errors:
                return HttpResponseServerError(
                    format_error('\n'.join(rep.format_exception())))
            else:
                raise
        
        emitter, ct = Emitter.get(em_format)
        
        fields = handler.fields
        if hasattr(handler, 'list_fields') and (
                isinstance(result, list) or isinstance(result, QuerySet)):
            fields = handler.list_fields

        srl = emitter(result, typemapper, handler, fields, anonymous)

        try:
            """
            Decide whether or not we want a generator here,
            or we just want to buffer up the entire result
            before sending it to the client. Won't matter for
            smaller datasets, but larger will have an impact.
            """
예제 #12
0
class Resource(object):
    """
    Resource. Create one for your URL mappings, just
    like you would with Django. Takes one argument,
    the handler. The second argument is optional, and
    is an authentication handler. If not specified,
    `NoAuthentication` will be used by default.
    """
    callmap = {
        'GET': 'read',
        'POST': 'create',
        'PUT': 'update',
        'DELETE': 'delete'
    }

    def __init__(self, handler, authentication=None):
        if not callable(handler):
            raise AttributeError, "Handler not callable."

        self.handler = handler()
        self.csrf_exempt = getattr(self.handler, 'csrf_exempt', True)

        if not authentication:
            self.authentication = (NoAuthentication(), )
        elif isinstance(authentication, (list, tuple)):
            self.authentication = authentication
        else:
            self.authentication = (authentication, )

        # Erroring
        self.email_errors = getattr(settings, 'PISTON_EMAIL_ERRORS', True)
        self.display_errors = getattr(settings, 'PISTON_DISPLAY_ERRORS', True)
        self.stream = getattr(settings, 'PISTON_STREAM_OUTPUT', False)
        # Emitter selection
        self.strict_accept = getattr(settings, 'PISTON_STRICT_ACCEPT_HANDLING',
                                     False)
        self.default_emitter = getattr(settings, 'PISTON_DEFAULT_EMITTER',
                                       'json')

    def determine_emitter(self, request, *args, **kwargs):
        """
        Function for determening which emitter to use
        for output. It lives here so you can easily subclass
        `Resource` in order to change how emission is detected.
        """
        try:
            return kwargs['emitter_format']
        except KeyError:
            pass
        if 'format' in request.GET:
            return request.GET.get('format')
        if mimeparse and 'HTTP_ACCEPT' in request.META:
            supported_mime_types = set()
            emitter_map = {}
            for name, (klass, content_type) in Emitter.EMITTERS.items():
                content_type_without_encoding = content_type.split(';')[0]
                supported_mime_types.add(content_type_without_encoding)
                emitter_map[content_type_without_encoding] = name
            preferred_content_type = mimeparse.best_match(
                list(supported_mime_types), request.META['HTTP_ACCEPT'])
            return emitter_map.get(preferred_content_type, None)

    def form_validation_response(self, e):
        """
        Method to return form validation error information. 
        You will probably want to override this in your own
        `Resource` subclass.
        """
        resp = rc.BAD_REQUEST
        resp.write(u' ' + unicode(e.form.errors))
        return resp

    @property
    def anonymous(self):
        """
        Gets the anonymous handler. Also tries to grab a class
        if the `anonymous` value is a string, so that we can define
        anonymous handlers that aren't defined yet (like, when
        you're subclassing your basehandler into an anonymous one.)
        """
        if hasattr(self.handler, 'anonymous'):
            anon = self.handler.anonymous

            if callable(anon):
                return anon

            for klass in typemapper.keys():
                if anon == klass.__name__:
                    return klass

        return None

    def authenticate(self, request, rm):
        actor, anonymous = False, True

        for authenticator in self.authentication:
            if not authenticator.is_authenticated(request):
                if self.anonymous and \
                    rm in self.anonymous.allowed_methods:

                    actor, anonymous = self.anonymous(), True
                else:
                    actor, anonymous = authenticator.challenge, CHALLENGE
            else:
                return self.handler, self.handler.is_anonymous

        return actor, anonymous

    @vary_on_headers('Authorization')
    def __call__(self, request, *args, **kwargs):
        """
        NB: Sends a `Vary` header so we don't cache requests
        that are different (OAuth stuff in `Authorization` header.)
        """
        rm = request.method.upper()

        # Django's internal mechanism doesn't pick up
        # PUT request, so we trick it a little here.
        if rm == "PUT":
            coerce_put_post(request)

        actor, anonymous = self.authenticate(request, rm)

        if anonymous is CHALLENGE:
            return actor()
        else:
            handler = actor

        # Translate nested datastructs into `request.data` here.
        if rm in ('POST', 'PUT'):
            try:
                translate_mime(request)
            except MimerDataException:
                return rc.BAD_REQUEST
            if not hasattr(request, 'data'):
                if rm == 'POST':
                    request.data = request.POST
                else:
                    request.data = request.PUT

        if not rm in handler.allowed_methods:
            return HttpResponseNotAllowed(handler.allowed_methods)

        meth = getattr(handler, self.callmap.get(rm, ''), None)
        if not meth:
            raise Http404

        # Support emitter through (?P<emitter_format>) and ?format=emitter
        # and lastly Accept: header processing
        em_format = self.determine_emitter(request, *args, **kwargs)
        if not em_format:
            request_has_accept = 'HTTP_ACCEPT' in request.META
            if request_has_accept and self.strict_accept:
                return rc.NOT_ACCEPTABLE
            em_format = self.default_emitter

        kwargs.pop('emitter_format', None)

        # Clean up the request object a bit, since we might
        # very well have `oauth_`-headers in there, and we
        # don't want to pass these along to the handler.
        request = self.cleanup_request(request)

        try:
            result = meth(request, *args, **kwargs)
        except Exception, e:
            result = self.error_handler(e, request, meth, em_format)

        try:
            emitter, ct = Emitter.get(em_format)
            fields = handler.fields

            if hasattr(handler, 'list_fields') and isinstance(
                    result, (list, tuple, QuerySet)):
                fields = handler.list_fields
        except ValueError:
            result = rc.BAD_REQUEST
            result.content = "Invalid output format specified '%s'." % em_format
            return result

        status_code = 200

        # If we're looking at a response object which contains non-string
        # content, then assume we should use the emitter to format that
        # content
        if self._use_emitter(result):
            status_code = result.status_code
            result = result.content

        srl = emitter(result, typemapper, handler, fields, anonymous)

        try:
            """
            Decide whether or not we want a generator here,
            or we just want to buffer up the entire result
            before sending it to the client. Won't matter for
            smaller datasets, but larger will have an impact.
            """
            if self.stream:
                stream = srl.stream_render(request)
            else:
                stream = srl.render(request)

            if not isinstance(stream, HttpResponse):
                if django.VERSION >= (1, 7):
                    resp = HttpResponse(stream,
                                        content_type=ct,
                                        status=status_code)
                else:
                    resp = HttpResponse(stream,
                                        mimetype=ct,
                                        status=status_code)
            else:
                resp = stream

            resp.streaming = self.stream

            return resp
        except HttpStatusCode, e:
            return e.response
예제 #13
0
    def execute_request(self, request, *args, **kwargs):
        """
        This is the entry point for all incoming requests
        (To be more precise, the URL mapper calls the
        L{resource.Resource.__call__} that does
        some pre-processing, which then calls L{execute_request} )

        It guides the request through all the necessary steps up to the point
        that its result is serialized into a dictionary.

        @type request: HTTPRequest object
        @param request: Incoming request

        @rype: dict
        @return: Dictionary of the result. The dictionary contains the
        following keys:
        
        * data: Contains the result of running the requested operation. The
        value to this key can be a dictionary, list, or string. Within this
        data structure, any dictionaries or lists are made of strings, with the
        exception of dates, which appear as I{datetime} functions, and will be
        serialized by the JSONEmitter.

        * total: Is only included if slicing was performed, and indicates the
        total result size.

        * Any other key can be included, if L{BaseHandler.enrich_response} has
        been overridden
        """
        # Validate request body data
        if hasattr(request, 'data') and request.data is not None:
            if request.method.upper() == 'PUT':
                # In the case of PUT requests, we first force the evaluation of
                # the affected dataset (theferore if there are any
                # HttpResourceGone exceptions, they will be raised now), and then in the
                # ``validate`` method, we perform any data validations. We
                # assign it to parameter ``request.dataset``.
                request.dataset = self.data(request, *args, **kwargs)              
            self.validate(request, *args, **kwargs)
        
        # Pick action to run
        action = getattr(self,  CALLMAP.get(request.method.upper()))
        # Run it
        data = action(request, *args, **kwargs)
        # Select output fields
        fields = self.get_output_fields(request) 
        # Slice
        sliced_data, total = self.response_slice_data(request, data)
        # inject fake dynamic fields to the response data
        sliced_data = self.inject_fake_dynamic_fields(request, sliced_data, fields)            
        
        # Use the emitter to serialize any python objects / data structures
        # within I{sliced_data}, to serializable forms(dict, list, string), 
        # so that the specific emitter we use for returning
        # the response, can easily serialize them in some other format,
        # The L{Emitter} is responsible for making sure that only fields contained in
        # I{fields} will be included in the result.
        emitter = Emitter(self, sliced_data, fields)      
        ser_data = emitter.construct()

        # Structure the response data
        ret = {'data': ser_data}
        if total is not None:
            ret['total'] = total
        # Add extra metadata
        self.enrich_response(ret, data)

        if request.method.upper() == 'DELETE':
            self.data_safe_for_delete(data)

        return ret