簡體   English   中英

Django Rest Framework ModelSerializer在create上設置屬性

[英]Django Rest Framework ModelSerializer Set attribute on create

最初創建對象時,我使用當前登錄的用戶來指定模型字段“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)

基本上,您希望將所有者設置為創建而不是后續更新。 為此,我認為你應該在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)

使用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的目的,它非常簡潔。

責任應該在這里拆分,因為序列化程序/視圖只接收/清理數據並確保提供所有需要的數據,然后相應地設置所有者字段應該是模型的責任。 將這兩個目標分開是很重要的,因為模型可能會從其他地方更新(例如來自管理員表單)。

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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM