最初创建对象时,我使用当前登录的用户来指定模型字段' 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)
答案 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)
责任应该在这里拆分,因为序列化程序/视图只接收/清理数据并确保提供所有需要的数据,那么它应该是模型有责任相应地设置所有者字段。将这两个目标分开是很重要的,因为模型可能会从其他地方更新(例如来自管理员表单)。
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)