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