通用视图的 Django REST API 自定义方法

Posted

技术标签:

【中文标题】通用视图的 Django REST API 自定义方法【英文标题】:Django REST API custom methods for generic views 【发布时间】:2017-02-27 05:07:47 【问题描述】:

我是一名实习生,正在从事一个项目,我在该项目中开发 DRF API,该 API 需要与我的同事使用 Ionic 框架编写的移动应用程序进行交互。 我们正在创建新用户。我的查看方法如下:

class NewUser(generics.CreateAPIView):
    model = User
    permission_classes = [permissions.AllowAny]
    serializer_class = NewUserSerializer

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        token, created = Token.objects.get_or_create(user=serializer.instance)
        return Response('token': token.key, status=status.HTTP_201_CREATED, headers=headers)

当有人想通过 POST 请求创建新用户时,如果用户名尚未被使用,则 API 返回 201 状态代码和 JSON 中的令牌,如果用户名已被使用,则返回 400 状态和 JSON 中的错误消息。当我的同事尝试使用已经存在的名称创建用户名时,我的同事要求我将状态消息更改为 200。他说他不能使用 ERROR 响应。 他的代码如下:

$http.post(url,
username:$scope.tel,
password:$scope.passwd
).success(function(data)
alert(data);
$ionicLoading.hide();
console.log(data);
)

问题: 1) 我是否应该调整我的 API 以发送 200 状态而不是更符合逻辑的 400 以显示“用户已注册”错误? 我试图更改我的代码,但在 DRF 的 CreateAPIView/ModelSerializer 中找不到要覆盖的方法。我最终将我的视图类重写为方法:

@api_view(['POST'])
def newUser(request):
    """
    Saves a new user on the database
    """
    if request.method == 'POST':

        serializer = NewUserSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            token, created = Token.objects.get_or_create(user=serializer.instance)
            return Response('token': token.key, status=status.HTTP_201_CREATED, headers=serializer.data)
        else:
            return Response(serializer.errors, status=status.HTTP_200_OK)

问题: 2)如果我想改变 API 和响应的行为,我应该覆盖哪个方法 3)我是 Django 新手,但仍然不知道我们应该在哪里使用通用视图 VS。 @.... 方法

【问题讨论】:

【参考方案1】:

在这种情况下,200 vs 400 主要是偏好。 400 表示“错误请求”。对于格式不正确的请求,这通常比不满足某些条件的请求更正确。

200 恰如其分地传达了正确的信息:

您的请求有效,但我没有创建新记录。

关于如何进行覆盖。最短路径是覆盖CreateAPIView.create 并更改使用的响应代码。您还应该通过调用super 来避免重复CreateAPIView 的默认行为。

class CreateUserView(generics.CreateAPIView):
    model = User
    permission_classes = [permissions.AllowAny]
    serializer_class = NewUserSerializer

    def create(self, request, *args, **kwargs):
        response = super(CreateUserView, self).create(request, *args, **kwargs)
        token, created = Token.objects.get_or_create(user=serializer.instance)
        response.status = status.HTTP_200_OK
        response.data = 'token': token.key
        return response

就我个人而言,我还可以制作我的NewUserSerializer 来拥有一个令牌字段并处理该令牌,这样我就不必在View 中进行这项工作。它不属于View

【讨论】:

感谢您的关注,但您的代码错过了我们创建“序列化程序”的部分。所以在 'token, created = Token.objects.get_or_create(user=serializer.instance)' 行中有 Global name not defined 错误 很公平,真正重要的是响应状态的变化。但是再一次,因为你在这里有不正确的关注点分离,它会导致你重复代码。您应该将 Token 创建作为序列化程序 save 的一部分,这样您就不会在视图中拥有所有这些业务逻辑,并避免从超类复制粘贴代码。【参考方案2】:

保存和删除钩子:

mixin 类提供以下方法,并提供轻松覆盖对象保存或删除行为。

perform_create(self, serializer) - 由 CreateModelMixin 调用 保存一个新的对象实例。 perform_update(self, 序列化程序) - 保存现有对象实例时由 UpdateModelMixin 调用。 perform_destroy(self, instance) - 由 DestroyModelMixin 调用 删除对象实例。

这些钩子对于设置请求中隐含但不属于请求数据的属性特别有用。例如,您可以根据请求用户或基于 URL 关键字参数在对象上设置属性。

https://www.django-rest-framework.org/api-guide/generic-views/#methods

class CourseOrder(generics.CreateAPIView):
    serializer_class = serializers.OrderCoursesSerializer
    permission_classes = [permissions.AllowAny]

    # hook before creating
    def perform_create(self, serializer):
        # print(serializer['name'].value)

        # save post data
        serializer.save()

        try:
            subject, from_email, to = 'New order', 'zelenchyks@gmail.com', 'zelenchyks@gmail.com'
            text_content = 'New order'
            html_content = '''
               <p>client name: %s </p>
               <p>client phone: %s </p>    
                ''' 
                % (serializer['name'].value, serializer['mobile'].value)

            msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
            msg.attach_alternative(html_content, "text/html")
            msg.send()
        except Warning:
            print('Huston we have a problems with smtp')

【讨论】:

以上是关于通用视图的 Django REST API 自定义方法的主要内容,如果未能解决你的问题,请参考以下文章

Django REST framwork-03-使用mixin和基于类的通用(generics)视图

Django 中的自定义与通用视图

DRF(Django REST Framework)框架

收到电子邮件时如何自定义密码确认视图 Django Rest Auth

从 Django 中基于类的通用视图自定义表单变量的首选方法是啥?

如何使 Vue.js 将散列密码发布到 Django REST API AbstractBaseUser 自定义模型?