示例#1
0
    def process_exception(self, request, exception):

        # Don't catch exceptions when in debug mode
        if settings.DEBUG:
            return

        # Ignore Http404s (defer to Django's built-in 404 handling)
        if isinstance(exception, Http404):
            return

        # Handle exceptions that occur from REST API requests
        if is_api_request(request):
            return rest_api_server_error(request)

        # Determine the type of exception. If it's a common issue, return a custom error page with instructions.
        custom_template = None
        if isinstance(exception, ProgrammingError):
            custom_template = 'exceptions/programming_error.html'
        elif isinstance(exception, ImportError):
            custom_template = 'exceptions/import_error.html'
        elif isinstance(exception, PermissionError):
            custom_template = 'exceptions/permission_error.html'

        # Return a custom error message, or fall back to Django's default 500 error handling
        if custom_template:
            return server_error(request, template_name=custom_template)
示例#2
0
 def __call__(self, request):
     response = self.get_response(request)
     if is_api_request(request):
         response['API-Version'] = settings.REST_FRAMEWORK_VERSION
     return response
示例#3
0
    def __call__(self, request):

        # Initialize an empty list to cache objects being saved.
        _thread_locals.changed_objects = []

        # Assign a random unique ID to the request. This will be used to associate multiple object changes made during
        # the same request.
        request.id = uuid.uuid4()

        # Connect our receivers to the post_save and post_delete signals.
        post_save.connect(handle_changed_object,
                          dispatch_uid='handle_changed_object')
        pre_delete.connect(handle_deleted_object,
                           dispatch_uid='handle_deleted_object')

        # Provide a hook for purging the change cache
        purge_changelog.connect(purge_objectchange_cache)

        # Process the request
        response = self.get_response(request)

        # If the change cache is empty, there's nothing more we need to do.
        if not _thread_locals.changed_objects:
            return response

        # Disconnect our receivers from the post_save and post_delete signals.
        post_save.disconnect(handle_changed_object,
                             dispatch_uid='handle_changed_object')
        pre_delete.disconnect(handle_deleted_object,
                              dispatch_uid='handle_deleted_object')

        # Create records for any cached objects that were changed.
        redis_failed = False
        for instance, action in _thread_locals.changed_objects:

            # Refresh cached custom field values
            if action in [
                    ObjectChangeActionChoices.ACTION_CREATE,
                    ObjectChangeActionChoices.ACTION_UPDATE
            ]:
                if hasattr(instance, 'cache_custom_fields'):
                    instance.cache_custom_fields()

            # Record an ObjectChange if applicable
            if hasattr(instance, 'to_objectchange'):
                objectchange = instance.to_objectchange(action)
                objectchange.user = request.user
                objectchange.request_id = request.id
                objectchange.save()

            # Enqueue webhooks
            try:
                enqueue_webhooks(instance, request.user, request.id, action)
            except RedisError as e:
                if not redis_failed and not is_api_request(request):
                    messages.error(
                        request,
                        "There was an error processing webhooks for this request. Check that the Redis service is "
                        "running and reachable. The full error details were: {}"
                        .format(e))
                    redis_failed = True

            # Increment metric counters
            if action == ObjectChangeActionChoices.ACTION_CREATE:
                model_inserts.labels(instance._meta.model_name).inc()
            elif action == ObjectChangeActionChoices.ACTION_UPDATE:
                model_updates.labels(instance._meta.model_name).inc()
            elif action == ObjectChangeActionChoices.ACTION_DELETE:
                model_deletes.labels(instance._meta.model_name).inc()

        # Housekeeping: 1% chance of clearing out expired ObjectChanges. This applies only to requests which result in
        # one or more changes being logged.
        if settings.CHANGELOG_RETENTION and random.randint(1, 100) == 1:
            cutoff = timezone.now() - timedelta(
                days=settings.CHANGELOG_RETENTION)
            purged_count, _ = ObjectChange.objects.filter(
                time__lt=cutoff).delete()

        return response