【问题标题】:Django Rest Framework Serializers and ViewsDjango Rest Framework 序列化器和视图
【发布时间】:2016-09-29 00:14:35
【问题描述】:

我很困惑如何在 DRF 中的序列化程序和视图中实现方法:

我有一个扩展 AbstractBaseUser 的帐户模型。视图集如下所示:

class AccountViewSet(viewsets.ModelViewSet):
    lookup_field = 'username'
    queryset = Account.objects.all()
    serializer_class = AccountSerializer

    def get_permissions(self):
        if self.request.method in permissions.SAFE_METHODS:
            return (permissions.AllowAny(), TokenHasReadWriteScope())

        if self.request.method == 'POST':
            return (permissions.AllowAny(), TokenHasReadWriteScope())

        return (permissions.IsAuthenticated(), IsAccountOwner(), TokenHasReadWriteScope())

    def create(self, request):
        serializer = self.serializer_class(data=request.data)

        if serializer.is_valid():
            Account.objects.create_user(**serializer.validated_data)

            return Response(serializer.validated_data, status=status.HTTP_201_CREATED)
        return Response({
            'status': 'Bad request',
            'message': 'Account could not be created with received data.'
        }, status=status.HTTP_400_BAD_REQUEST)

这样的序列化器:

class AccountSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, required=False)
    confirm_password = serializers.CharField(write_only=True, required=False)

    class Meta:
        model = Account
        fields = ('id', 'email', 'username', 'created_at', 'updated_at',
                  'first_name', 'last_name', 'tagline', 'password',
                  'confirm_password',)
        read_only_fields = ('created_at', 'updated_at',)

    def create(self, validated_data):
        return Account.objects.create(**validated_data)

    def update(self, instance, validated_data):
        instance.username = validated_data.get('username', instance.username)

        instance.save()

        password = validated_data.get('password', None)
        confirm_password = validated_data.get('confirm_password', None)

        if password and confirm_password:
            instance.set_password(password)
            instance.save()

            update_session_auth_hash(self.context.get('request'), instance)

        return instance

def validate(self, data):
        if data['password'] and data['confirm_password'] and data['password'] == data['confirm_password']:
            try:
                validate_password(data['password'], user=data['username']):

                return data
            except ValidationError:
                raise serializers.ValidationError("Password is not valid.")
        raise serializers.ValidationError("Passwords do not match.")

在视图的 create 方法中,它会检查序列化程序是否有效,然后将其保存并根据结果返回响应。我的第一个问题是何时调用序列化程序 create() 方法?在我看来,通过在视图的 create() 方法中调用 create_user (模型方法)似乎完全绕过了该方法。它会被调用吗?拥有它有什么意义?

其次,我在从更新方法返回状态码时遇到问题,实例保存在序列化程序中。如果验证失败,序列化程序 update() 中的代码会起作用吗?

这是我目前所拥有的:

def update(self, request, pk=None):
        serializer = self.serializer_class(data=request.data)

        if serializer.is_valid():
            << what goes here??? >>

            return Response(serializer.validated_data, status=status.HTTP_200_OK)
        except serializers.ValidationError as e:
        return Response({
            'status': 'Bad request',
            'message': str(e)    
        }, status=status.HTTP_400_BAD_REQUEST)

        return Response({
            'status': 'Bad request',
            'message': 'Account could not be updated with received data.'
        }, status=status.HTTP_400_BAD_REQUEST)

我迫切需要澄清一下。我不确定如何通过视图/序列化器方法请求流,也不确定如何将实例保存在序列化器中并决定同时在视图中返回哪个响应。

编辑:

我删除了createupdate 方法,并为AccountViewSet 修复了get_permissions,并按照您的建议将用户名验证添加到validate。我还更新了序列化程序的创建和更新方法,以下是新版本:

def create(self, validated_data):
    instance = super(AccountSerializer, self).create(validated_data)
    instance.set_password(validated_data['password'])
    instance.save()
    return instance

def update(self, instance, validated_data):
    instance.username = validated_data.get('username', instance.username)

    password = validated_data.get('password', None)
    confirm_password = validated_data.get('confirm_password', None)

    if password and confirm_password:
        instance.set_password(password)
        instance.save()
        update_session_auth_hash(self.context.get('request'), instance)
    else:
        instance.save()

    return instance

我唯一的问题是有必要在create 之后调用set_password 吗? create 不会给新用户设置密码吗? createupdate 的视图中没有代码可以吗?在没有视图代码的情况下,serializer.save() 在哪里被调用,序列化程序 validate 什么时候在没有调用 serializer.is_valid() 的情况下运行?

【问题讨论】:

    标签: django serialization django-rest-framework


    【解决方案1】:

    AccountViewSet 类的create() 方法中,当序列化程序验证通过时,您将创建Account 实例。相反,您应该致电serializer.save()

    如果您查看BaseSerializer 类中的save() 方法,您会发现它调用create()update() 方法,具体取决于模型实例是正在创建还是更新。由于您没有在AccountViewSet.create() 方法中调用serializer.save(),因此不会调用AccountSerializer.create() 方法。希望这能回答您的第一个问题。

    第二个问题的答案也是缺少serializer.save()。将&lt;&lt; what goes here??? &gt;&gt; 替换为serializer.save()。这(正如我上面解释的),将调用AccountSerializer.update() 方法。

    【讨论】:

    • 谢谢,我会试试的。我没有在 create 中调用 save ,因为它会在不散列密码的情况下创建帐户,导致它根本无法工作。
    【解决方案2】:

    AccountViewSet

    你不需要 .create().update() 方法,从你的例子 - 现有的应该足够了

    get_permissions() - 第一个“如果”打开你的系统太宽恕我直言应该被删除 - 你允许任何人做POST - “又名”创建新帐户,这没关系,但其他一切(如GETPUT) - 只允许帐户所有者或(如果需要!)注册用户使用

    AccountSerializer

    • API 将在验证失败时返回 HTTP400
    • 确保id 字段为只读,您不希望有人覆盖现有用户
    • 现有的create() 方法可以删除,但我认为你的应该是这样的:

      def create(self, validated_data):
          instance = super(AccountSerializer, self).create(validated_data)
          instance.set_password(validated_data['password'])
          instance.save()
          return instance
      
    • 现有的update() 方法...不确定你想要什么,但是:

      • 第一行是允许用户更改其用户名,无需验证,例如用户名是唯一的,更重要的是,您根本不检查从请求传递的username 字段中的内容,它甚至可能是空字符串或无 - 只有在缺少密钥时才会调用dictionary.get 的回退字典,
      • 如果 username 的唯一性将在 db 级别进行验证(模型字段定义中的 unique=True) - 您将收到奇怪的异常,而不是 API 的漂亮错误消息)
      • 接下来是密码验证(再次) - 刚刚在 validate 方法中测试过
      • 设置用户密码后保存实例.. 第二次 - 也许值得优化它并且只保存一次?
      • 如果您不允许更新所有字段,最好将update_fields 传递给save() 以限制更新哪些字段?
    • validation - 只需添加username 验证;)

    只是为了快速理解 DRF(非常简单)-Serializers 是 API 的 FormsViewSets - 通用 Views,渲染器是 templates(决定如何显示数据)

    --编辑--

    关于视图集的更多项目,ModelViewSet 包括:

    • mixins.CreateModelMixin - 调用 validate & create -> serializer.save -> serializer.create
    • mixins.RetrieveModelMixin - “获取”实例
    • mixins.UpdateModelMixin - 调用验证和更新/部分更新 -> serializer.save -> serializer.update
    • mixins.DestroyModelMixin - 调用实例删除
    • mixins.ListModelMixin - “获取”实例列表(浏览)

    【讨论】:

    • 我编辑了问题以包括您建议的更改以及关于您提议的更改的一些小问题。 - set_password 不会被 create 调用吗?如果没有视图代码,serializer.save() 和 serializer.is_valid() 是否仍会被调用,何时调用?如果没有,何时调用 update/create/validate?
    • 最后一个问题,我应该在序列化程序的 Meta 中将 id 字段设置为只读吗?或者其他地方。
    • 我查看了django源码,set_password在create或者update的时候没有被调用,但是可以在form.save()之前调用,然后设置新密码会包含在整个表单中.save()
    • 当你的序列化程序被表单数据调用时——验证被调用,然后(取决于将它绑定到实例——创建或更新)——查看 ModelViewsSet 源
    • 有两种方式 - 在元或字段定义(或重新定义现有的) - 您可以使用其中任何一种
    猜你喜欢
    • 2018-10-04
    • 2017-07-28
    • 2015-06-29
    • 1970-01-01
    • 2016-11-29
    • 2015-10-14
    • 1970-01-01
    • 1970-01-01
    • 2014-08-08
    相关资源
    最近更新 更多