繁体   English   中英

修改DRF请求对象以调度到其他视图

[英]Modify DRF Request objects for dispatch to other views

上下文

可以将AWS Elastic Beanstalk Worker层配置为自动接受单个 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属性简单地替换为有效负载,以便内部视图仅看到针对该请求的数据(在分发视图中为有效负载)。

问题

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

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)

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

SO广为接受,只要您传入适当的request对象,就可以将视图称为普通函数。 至少在DRF @api_view装饰器创建视图的情况下,实际上可能并非如此。

仍在努力解决这一问题。

出于某种原因-这可能是DRF中的错误,但我不确定-您不能只从另一个函数中调用@api_view函数并传递request对象。 就是行不通。

什么工作是分解出来完成实际工作的代码,并从单独调用它@api_view功能,还dispatch视图。 也就是说,像这样:

def perform_action(request, data):
    """
    Do something

    `request` is included for completeness in case it's necessary,
    but the data normally read from `request.data` should be read instead
    from the `data` argument.
    """
    serializer = ActionSerializer(data=data)
    if serializer.is_valid():
        ... # Whatever your action should be, goes here

@api_view(http_method_names=['POST'])
@permission_classes([AllowAny])
def action(request)
    perform_action(request, request.data)
    return Response()  # 200, no content

@api_view(http_method_names=['POST'])
@permission_classes([AllowAny])
def dispatch(request):
    """
    Dispatch a request to a sibling endpoint.
    """
    routing_table = {
        'action': perform_action, # etc...
    }

    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()

        view(request, serializer.validated_data['payload'])

    return Response()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM