Django Rest Framework ModelSerializer在create上设置属性

时间:2014-04-22 09:59:21

标签: python django serialization django-rest-framework

最初创建对象时,我使用当前登录的用户来指定模型字段' 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)

3 个答案:

答案 0 :(得分:2)

基本上,您希望将所有者设置为创建而不是后续更新。为此,我认为你应该在POST视图中设置所有者。我认为这种方式更具逻辑性和稳健性。更新是通过PUT视图完成的,因此您的数据应始终是正确的,因为如果所有者在PUT上不可编辑,则无法更新所有者。

为了制作视图,您可以使用DRF的基于通用类的视图。按原样使用RetrieveUpdateDeleteView。对于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)

答案 1 :(得分:1)

使用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的目的,而且非常简洁。

答案 2 :(得分:0)

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

views.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)

models.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)