我必须为现有的基于Django的Web应用程序创建一个API,并且我正在使用DRF这样做。虽然我之前从未使用过DRF,但在遇到这个问题之前,一切都非常清晰。这是相关网址:
http://www.<customer-website-name>.com/api/friendship-requests/<sender-pk>/
根据我给出的要求规范,它应该像这样处理:
FriendshipRequest
或404用户发送给当前已通过身份验证的用户的pk = <sender-pk>
对象,如果此类请求不存在。AcceptReject
对象(例如,JSON {"accepted": false}
)。如果accepted == True
,请清理FriendshipRequest
,创建一个新的Friendship
对象,返回201;如果accepted == False
,请将FriendshipRequest
标记为已拒绝,请返回204 这是AcceptReject
课程及其序列化工具:
class AcceptReject(object):
def __init__(self, accepted):
self.accepted = accepted
class AcceptRejectSerializer(serializers.Serializer):
accepted = serializers.BooleanField()
这是我用来处理网址的视图:
class FriendshipRequestDetail(mixins.RetrieveModelMixin,
generics.GenericAPIView):
def get_serializer_class(self):
if self.request.method == 'PUT':
return serializers.AcceptRejectSerializer
return serializers.FriendshipRequestSerializer
def get_object(self):
sender = generics.get_object_or_404(get_user_model(),
pk=self.kwargs['pk'])
obj = generics.get_object_or_404(FriendshipRequest,
from_user=sender,
to_user=self.request.user)
self.check_object_permissions(self.request, obj)
return obj
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return Response(status=status.HTTP_204_NO_CONTENT)
我天真地认为重写get_serializer_class()
以基于请求方法返回序列化程序就足够了,但它不是。
另外,正如您所看到的,我甚至还没有开始编写PUT处理程序。当我尝试使用可浏览的api时,它的存在足以导致以下错误:
Environment:
Request Method: GET
Request URL: http://127.0.0.1:8000/api/friendship-requests/5/
Django Version: 1.8.4
Python Version: 3.4.3
Installed Applications:
('django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.gis',
'rest_framework',
'rest_framework.authtoken',
'friendship',
'REDACTED.REDACTED')
Installed Middleware:
('django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware')
Traceback:
File "/home/manvis/REDACTED/env/lib/python3.4/site-packages/django/core/handlers/base.py" in get_response
164. response = response.render()
File "/home/manvis/REDACTED/env/lib/python3.4/site-packages/django/template/response.py" in render
158. self.content = self.rendered_content
File "/home/manvis/REDACTED/env/lib/python3.4/site-packages/rest_framework/response.py" in rendered_content
71. ret = renderer.render(self.data, media_type, context)
File "/home/manvis/REDACTED/env/lib/python3.4/site-packages/rest_framework/renderers.py" in render
669. context = self.get_context(data, accepted_media_type, renderer_context)
File "/home/manvis/REDACTED/env/lib/python3.4/site-packages/rest_framework/renderers.py" in get_context
614. raw_data_put_form = self.get_raw_data_form(data, view, 'PUT', request)
File "/home/manvis/REDACTED/env/lib/python3.4/site-packages/rest_framework/renderers.py" in get_raw_data_form
561. content = renderer.render(serializer.data, accepted, context)
File "/home/manvis/REDACTED/env/lib/python3.4/site-packages/rest_framework/serializers.py" in data
487. ret = super(Serializer, self).data
File "/home/manvis/REDACTED/env/lib/python3.4/site-packages/rest_framework/serializers.py" in data
223. self._data = self.to_representation(self.instance)
File "/home/manvis/REDACTED/env/lib/python3.4/site-packages/rest_framework/serializers.py" in to_representation
447. attribute = field.get_attribute(instance)
File "/home/manvis/REDACTED/env/lib/python3.4/site-packages/rest_framework/fields.py" in get_attribute
418. raise type(exc)(msg)
Exception Type: AttributeError at /api/friendship-requests/5/
Exception Value: Got AttributeError when attempting to get a value for field `accepted` on serializer `AcceptRejectSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `FriendshipRequest` instance.
Original exception text was: 'FriendshipRequest' object has no attribute 'accepted'.
就像我说的那样,这是我第一次使用DRF,但是,如果我正确理解了跟踪,似乎错误源于DRF尝试为可浏览的api呈现表单,不幸的是,我不能没有它,因为一个可浏览的api也是一个要求。
我需要更改/覆盖哪些内容才能使浏览api适用于此视图?
答案 0 :(得分:0)
问题来自于您破坏REST约定的事实。您可以在PUT
请求中使用操作,而不是资源表示。在REST中,您希望在is_accepted
对象中包含state
或FriendRequest
字段,并且通过修改此字段,您可以接受或拒绝请求。我强烈建议您向规范的设计人员指出。
在可浏览的API中,DRF生成一个表单以更新记录,因此它通过调用get_serializer()
(返回AcceptRejectSerializer
)并将此序列化程序转换为表单来获取序列化程序。然后通过调用get_object()
(返回FriendshipRequest
对象)获取记录,并尝试使用从记录中获取的数据填充表单。显然,记录和序列化器中的字段不匹配,因此您会收到错误。
作为一种糟糕的解决方案,您可以在accept
方法中添加get_object()
属性:
def get_object(self):
sender = generics.get_object_or_404(get_user_model(),
pk=self.kwargs['pk'])
obj = generics.get_object_or_404(FriendshipRequest,
from_user=sender,
to_user=self.request.user)
self.check_object_permissions(self.request, obj)
obj.accept = False
return obj
或者将其添加到FriendshipRequest
模型中:
class FriendshipRequest(...):
# ...
accept = False
或者您可以尝试override view
跳过表单填写步骤。