上的Django Rest Framework ModelSerializer Set属性

2024-06-06 19:06:33 发布

您现在位置:Python中文网/ 问答频道 /正文

最初创建对象时,我使用当前登录的用户分配模型字段“owner”。

模型:

class Account(models.Model):

    id = models.AutoField(primary_key=True)
    owner = models.ForeignKey(User)
    name = models.CharField(max_length=32, unique=True)
    description = models.CharField(max_length=250, blank=True)

要设置所有者的序列化程序:

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Account
        fields = ('name', 'description')

    def restore_object(self, attrs, instance=None):
        instance = super().restore_object(attrs, instance)

        request = self.context.get('request', None)
        setattr(instance, 'owner', request.user)

        return instance

系统中的其他用户可以更新另一个帐户对象,但所有权应保留在原始用户手中。显然,上面的内容打破了这一点,因为在使用当前登录的用户进行更新时,所有权将被覆盖。

所以我把它更新成这样:

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Account
        fields = ('name', 'description')

    def restore_object(self, attrs, instance=None):
        new_instance = False
        if not instance:
            new_instance = True

        instance = super().restore_object(attrs, instance)

        # Only set the owner if this is a new instance
        if new_instance:
            request = self.context.get('request', None)
            setattr(instance, 'owner', request.user)

        return instance

这是推荐的做这种事情的方法吗?我看不出别的办法,但到目前为止我的经验非常有限。

谢谢

从评论@zaphod100.10的答案开始。或者,在视图代码中(删除了上述序列化程序中的自定义restore_object方法):

def post(self, request, *args, **kwargs):

    serializer = self.get_serializer(data=request.DATA, files=request.FILES)

    if serializer.is_valid():
        serializer.object.owner = request.user
        self.pre_save(serializer.object)
        self.object = serializer.save(force_insert=True)
        self.post_save(self.object, created=True)
        headers = self.get_success_headers(serializer.data)

        return Response(serializer.data, status=status.HTTP_201_CREATED,
                        headers=headers)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Tags: instance用户selfnonetruegetreturnobject
3条回答

这里应该划分责任,因为序列化器/视图只接收/清理数据并确保提供了所有需要的数据,那么相应地设置owner字段应该是模型的责任。将这两个目标分开是很重要的,因为模型可能会从其他地方更新(比如从管理表单)。

视图.py

class AccountCreateView(generics.CreateAPIView):
    serializer_class = serializers.AccountSerializer
    permission_classes = (permissions.IsAuthenticated,)

    def post(self, request, *args, **kwargs):
        # only need this
        request.data['owner'] = request.user.id
        return super(AccountCreateView, self).post(request, *args, **kwargs)

模型.py

class Account(models.Model):
    # The id field is provided by django models.
    # id = models.AutoField(primary_key=True)

    # you may want to name the reverse relation with 'related_name' param.
    owner = models.ForeignKey(User, related_name='accounts') 
    name = models.CharField(max_length=32, unique=True)
    description = models.CharField(max_length=250, blank=True)

    def save(self, *args, **kwargs):
        if not self.id:
            # only triggers on creation
            super(Account, self).save(*args, **kwargs)

        # when updating, remove the "owner" field from the list
        super(Account, self).save(update_fields=['name', 'description'], *args, **kwargs)

基本上,您希望在创建时设置所有者,而不是在后续更新时设置所有者。为此,我认为你应该在后视图中设置所有者。我认为这样更合乎逻辑,更有力。更新是通过PUT视图完成的,因此您的数据应该始终正确,因为在更新时,如果所有者在PUT上不可编辑,则无法更改所有者。

为了创建视图,您可以使用DRF的基于类的通用视图。按原样使用RetrieveUpdateDeleteView。For ListCreateView重写post方法。使用django模型表单验证数据并创建帐户实例。

您必须复制request.DATA dict并插入“owner”作为当前用户。

POST方法的代码可以是:

def post(self, request, *args, **kwargs):
    data = deepcopy(request.DATA)
    data['owner'] = request.user
    form = AccountForm(data=data)
    if form.is_valid():
        instance = form.save(commit=false)
        instance.save()
        return Response(dict(id=instance.pk), status=status.HTTP_201_CREATED)
    return Response(form.errors, status=status.HTTP_400_BAD_REQUEST)

使用pre_save的潜在其他选项,我认为它似乎只用于此类事情。

class AccountList(generics.ListCreateAPIView):
    serializer_class = serializers.AccountSerializer
    permission_classes = (permissions.IsAuthenticated)

    def get_queryset(self):
        """
        This view should return a list of all the accounts
        for the currently authenticated user.
        """
        user = self.request.user
        return models.Account.objects.filter(owner=user)

    def pre_save(self, obj):
        """
        Set the owner of the object to the currently logged in user as this
        field is not populated by the serializer as the user can not set it
        """

        # Throw a 404 error if there is no authenticated user to use although
        # in my case this is assured more properly by the permission_class
        # specified above, but this could be any criteria.
        if not self.request.user.is_authenticated():
            raise Http404()

        # In the case of ListCreateAPIView this is not necessary, but
        # if doing this on RetrieveUpdateDestroyAPIView then this may
        # be an update, but if it doesn't exist will be a create. In the
        # case of the update, we don't wish to overwrite the owner.
        # obj.owner will not exist so the way to test if the owner is
        # already assigned for a ForeignKey relation is to check for
        # the owner_id attribute
        if not obj.owner_id:
            setattr(obj, 'owner', self.request.user)

我认为这是pre_save的目的,它非常简洁。

相关问题 更多 >