[英]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
对象很复杂; 产生一个新的是不可行的。 同时,我们需要修改该数据,以便将其正确传递给下一个视图,以便该视图只能看到其预期格式的数据。
我该怎么做?
我尝试编辑上面的示例以删除对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.