【问题标题】:Can we use serializer_class attribute with APIView(django rest framework)?我们可以在 APIView(django rest 框架)中使用 serializer_class 属性吗?
【发布时间】:2017-01-04 06:49:19
【问题描述】:

根据 DRF 文档,使用 GenericAPIView 时应设置 serializer_class 属性。但是为什么serializer_class 属性甚至可以与APIView 一起使用?

这是我的 API 代码:

class UserView(APIView):

    serializer_class = SignupSerializer

    @transaction.atomic
    def post(self, request):
        email = get_data_param(request, 'email', None)
        password = get_data_param(request, 'password', None)

        params = request.POST.copy()
        params["username"] = email

        serializer = UserSerializer(data=params)
        if serializer.is_valid(raise_exception=True):
            user = serializer.save()
            user.set_password(password)
            user.save()
            params["user"] = user.id

        serializer = CustomProfileSerializer(data=params)
        if serializer.is_valid(raise_exception=True):
            profile = serializer.save()
            return Response(response_json(True, profile.to_dict(), None))

class SignupSerializer(serializers.Serializer):
    email = serializers.EmailField(max_length=100)
    password = serializers.CharField(max_length=50)

当我在浏览器中浏览此 API 时,它确实将 emailpassword 字段显示为输入,但如果我不设置此 serializer_class 属性,则不会显示任何输入字段。
理想情况下,此serializer_class 属性不应与APIView 一起使用。我已经搜索了很多,但没有任何与此相关的可用信息。

谁能解释一下这种行为?谢谢。

【问题讨论】:

  • APIView 的源代码中没有提到 serializer_class 或其父类 View。您正在那里创建一个类变量“serializer_class”,所以我只能猜测您必须在您未显示的代码中的其他地方(直接或间接)引用它。
  • 为什么你认为 APIView 不会使用序列化器类?它仍然必须确定您的数据是什么样的以及如何对其进行序列化。您应该使用带有 APIView 的序列化程序类。您最好还是使用 APIView 上的通用视图之一。
  • @tyebillion: 我已经更新了显示完整版本的代码
  • @electrometro:APIView 不应使用 serializer_class 属性,因为 DRF 文档中没有提及它。只有 GenericAPIView 具有此功能。但是有和没有这个 serializer_class 属性的 APIVIew 的行为是不同的。当我使用 APIView 声明该属性时,它的行为类似于 GenericAPIView,当我浏览该 API 时,它会在浏览器中显示 2 个输入字段,即电子邮件和密码。而如果我不使用 APIView 声明 serializer_class 属性,则这两个输入字段不会显示在 Web 浏览器中。

标签: python django django-views django-rest-framework


【解决方案1】:

我认为这可以帮助你。
创建 serializer.py 并写入:

from rest_framework import serializers

from .models import User


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('email', 'password')

和views.py:

from rest_framework.response import Response
from rest_framework.views import APIView

from .serializers import UserSerializer
from .models import User


class AddUserAPIView(APIView):
    def post(self, request):
        user_serializer = UserSerializer(data=request.data)
        if user_serializer.is_valid():
            user = user_serializer.save() 
            user.set_password(user_serializer.data["password"])
            return Response({'message': 'user added successfully!'})

        return Response({'message': user_serializer.errors})

【讨论】:

    【解决方案2】:

    你是绝对正确的!!
    APIView 不使用serializer_class(默认情况下),因为它不打算处理任何请求处理逻辑!

    不过,用于在浏览器中呈现 API 的 BrowsableAPIRenderer 会检查 serializer_class 属性并将其设置为 View 序列化程序(如果存在)。我们可以在BrowsableAPIRenderer 代码中看到这一点:

    1. 渲染器的_get_serializer类:

      def _get_serializer(self, serializer_class, view_instance, request, *args, **kwargs):
          kwargs['context'] = {
             'request': request,
             'format': self.format,
             'view': view_instance
         }
         return serializer_class(*args, **kwargs)
      
    2. 以及它用于设置渲染器序列化器(如果存在)的方式,在get_rendered_html_form内部:

      • Line 483:has_serializer_class = getattr(view, 'serializer_class', None)

      • Lines 497 to 509:

        if has_serializer:
            if method in ('PUT', 'PATCH'):
                serializer = view.get_serializer(instance=instance, **kwargs)
            else:
                serializer = view.get_serializer(**kwargs)
        else:
            # at this point we must have a serializer_class
            if method in ('PUT', 'PATCH'):
                serializer = self._get_serializer(view.serializer_class, view,
                                                  request, instance=instance, **kwargs)
            else:
                serializer = self._get_serializer(view.serializer_class, view,
                                                  request, **kwargs)
        

    本质上,您通过提供serializer_class 属性不小心覆盖了BrowserAPIRenderer 关于APIView 的默认行为。值得一提的是,我对此事的看法是,这应该是不可能的!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-06-20
      • 2021-01-16
      • 2020-04-21
      • 1970-01-01
      • 2022-08-14
      • 1970-01-01
      • 1970-01-01
      • 2021-08-06
      相关资源
      最近更新 更多