修改DRF请求对象以便分派到其他视图

时间:2017-05-18 15:23:27

标签: python django amazon-web-services django-rest-framework

上下文

AWS Elastic Beanstalk工作层可以配置为自动接受单个 AWS SQS队列。此队列上的传入消息可以路由到单个端点,工作层中的某个实例将响应该端点;他们向工作人员展示了POST事件。

我的工作人员运行的软件基于Django / DRF。

目标

我希望我的工作层实例能够处理多种类型的传入事件。理想情况下,SQS可以配置为将请求传递给多个端点,但这似乎是不可能的。

我的实施

class DispatchSerializer(serializers.Serializer):
    "`to` is the name of the destination endpoint; `payload` is the data I want delivered"
    to = serializers.CharField(
        max_length=80,
    )

    payload = serializers.JSONField()

@api_view(http_method_names=['POST'])
@permission_classes([AllowAny])
def dispatch(request):
    """
    Dispatch a request to a sibling endpoint.
    """
    routing_table = {
        ... # this is specific to my application; 
            # it's just a dict of names to views
    }

    serializer = DispatchSerializer(data=request.data)
    if serializer.is_valid(raise_exception=True):
        to = serializer.validated_data['to']
        try:
            view = routing_table[to]
        except KeyError:
            raise Http404()

        # https://github.com/encode/django-rest-framework/blob/master/rest_framework/request.py#L183-L187
        request._full_data = serializer.validated_data['payload']
        return view(request)

    # If this returns other than 200, SQS simply re-attempts the request later. 
    # No returned data is preserved, so we might as well not return any.
    return Response()

正如您所看到的,我尝试简单地将请求的_full_data属性替换为有效负载,以便内部视图仅查看用于它的数据(发送视图中的数据是有效负载)。 / p>

问题

此实现实际上不起作用。我收到了这种形式的错误:

Traceback (most recent call last):
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 378, in __getattribute__
    return super(Request, self).__getattribute__(attr)
AttributeError: 'Request' object has no attribute 'body'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 378, in __getattribute__
    return super(Request, self).__getattribute__(attr)
AttributeError: 'Request' object has no attribute 'body'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/django/core/handlers/exception.py", line 39, in inner
    response = get_response(request)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/django/core/handlers/base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/django/core/handlers/base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
    return view_func(*args, **kwargs)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/django/views/generic/base.py", line 68, in view
    return self.dispatch(request, *args, **kwargs)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/views.py", line 483, in dispatch
    response = self.handle_exception(exc)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/views.py", line 443, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/views.py", line 480, in dispatch
    response = handler(request, *args, **kwargs)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/decorators.py", line 52, in handler
    return func(*args, **kwargs)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/project/django-backend/cardamom/worker/views.py", line 196, in dispatch
    return view(request)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
    return view_func(*args, **kwargs)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/django/views/generic/base.py", line 68, in view
    return self.dispatch(request, *args, **kwargs)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/views.py", line 483, in dispatch
    response = self.handle_exception(exc)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/views.py", line 443, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/views.py", line 480, in dispatch
    response = handler(request, *args, **kwargs)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/decorators.py", line 52, in handler
    return func(*args, **kwargs)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/project/django-backend/cardamom/core/views/mailchimp.py", line 229, in wrapper
    return func(*args, **kwargs)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/project/django-backend/cardamom/worker/views.py", line 52, in wrapper
    result = f(log_entry, *args, **kwargs)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/project/django-backend/cardamom/worker/views.py", line 106, in match_one
    user_id = int(request.data['user_id'])
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 378, in __getattribute__
    return super(Request, self).__getattribute__(attr)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 186, in data
    self._load_data_and_files()
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 246, in _load_data_and_files
    self._data, self._files = self._parse()
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 290, in _parse
    stream = self.stream
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 378, in __getattribute__
    return super(Request, self).__getattribute__(attr)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 173, in stream
    self._load_stream()
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 270, in _load_stream
    self._stream = six.BytesIO(self.body)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 382, in __getattribute__
    return getattr(self._request, attr)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 382, in __getattribute__
    return getattr(self._request, attr)
  File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/django/http/request.py", line 264, in body
    raise RawPostDataException("You cannot access body after reading from request's data stream")
django.http.request.RawPostDataException: You cannot access body after reading from request's data stream

问题重述

request对象很复杂;产生一个新的是不可信的。同时,我们需要修改此数据,以便将其正确传递到下一个视图,以便该视图仅以预期格式查看数据。

我该如何做到这一点?

[edit]传递未修改的请求不起作用

我尝试编辑上面的示例以删除对request对象的任何修改,而是创建了以下序列化程序mixin:

class UndispatcherMixin:
    """
    Adjust a serializer such that it can accept its normal serialization,
    or alternately the payload of a DispatchSerializer.
    """

    def is_valid(self, raise_exception=False):
        if not hasattr(self, '_validated_data'):
            assert hasattr(self, 'initial_data'), (
                'Cannot call `.is_valid()` as no `data=` keyword argument was '
                'passed when instantiating the serializer instance.'
            )
            ds = DispatchSerializer(data=self.initial_data)

            if ds.is_valid(raise_exception=False):
                self.initial_data = ds.validated_data['payload']
        return super().is_valid(raise_exception=raise_exception)

然后将该mixin添加到下游视图使用的相关序列化器中。有趣的是,它失败了与之前相同的django.http.RawPostDataException

只要你传入一个合适的request对象,你就可以将你的观点称为普通函数。这可能实际上并非如此,至少对于视图是由DRF @api_view装饰器创建的情况而言。

仍然在努力解决这个问题。

1 个答案:

答案 0 :(得分:0)

无论出于何种原因 - 这可能是DRF中的错误,但我不确定 - 您无法从另一个中调用@api_view函数并传递{ {1}}对象。它只是没有用。

的工作原理是分解执行实际工作的代码,并将其与request函数以及@api_view视图分开调用。就是这样:

dispatch