正在跳过对同一行进行更新时的django序列化程序唯一验证



我正在使用django序列化程序进行验证。代码如下所示。相同的代码用于创建和更新时的验证检查。但是,在更新时,应跳过对某些字段的唯一验证检查(例如:电子邮件(。因为在更新时将使用该行。

from rest_framework import serializers
from rest_framework.validators import UniqueValidator, UniqueTogetherValidator
from dashboard.models import Users
class UserSerializer(serializers.Serializer):
username = serializers.CharField(max_length=200, required=True, validators=[UniqueValidator(queryset=Users.objects.all())])
email = serializers.EmailField(validators=[UniqueValidator(queryset=Users.objects.all())])
def validate_username(self, value):
if len(value) < 6:
raise serializers.ValidationError('Username must have at least 3 characters')
return value
def validate_email(self, value):
if len(value) < 3:
raise serializers.ValidationError('Username must have at least 3 characters')
return value
def validate(self, data):
return data

在这里,我使用的是UniqueValidator,除了同一行之外,更新验证检查应该跳过它。

使用serializers.Serializer

可以将unique=False设置为email = serializers.EmailField(unique=False)然后将您自己的逻辑实现为

我正在编码这样的东西(还没有测试(

def validate(self, data):
email = data.get("email")
username = data.get("username")
# checking if it's update
is_update = User.objects.filter(username=username)
if not is_update:
# cheking if email exists
is_unique = User.objects.filter(email=email).exists()
if len(email) < 6:
raise serializers.ValidationError("The email len should be greater than 6 char.")
elif not is_unique:
raise serializers.ValidationError(f"The e-mail address {email} is already being used")
if len(username) < 6:
raise serializers.ValidationError("Username must have at least 3 characters")
return data

我想这是一个与模型相关的任务。因此,我使用ModelSerializer而不是普通的Serializer。基本上,createvsupdate及其相应的验证都会被基于一个属性的序列化程序阻止。属性的名称为实例。如果此属性为None,则序列化程序认为它是一个新值,因此执行create操作;如果该值不是None,并且instance的值是有效的模型对象,则序列化器执行update操作。

这里需要注意的是,相应的验证也是基于"创建"或"更新"操作运行。

对于像您这样的情况,您所需要做的就是用正确的模型对象填充实例属性值。这是DRF-BaseSerializer类的构造函数方法。

def __init__(self, instance=None, data=empty, **kwargs):
self.instance = instance
if data is not empty:
self.initial_data = data
self.partial = kwargs.pop('partial', False)
self._context = kwargs.pop('context', {})
kwargs.pop('many', None)
super().__init__(**kwargs)

在这里,您可以很容易地看到,当您的视图调用序列化程序类时,如果您在instance参数上设置一些值,它将填充instance属性,然后序列化程序将执行update相关操作。如果不在instance参数上设置任何值,则序列化程序将执行create相关操作。

为了更好地理解,我在这里为您介绍DRF ModelViewset的create((update((方法。看看这里的序列化程序类绑定有什么不同。

DRF ModelViewset的create((方法

def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

和DRF ModelViewset的update((方法

def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {}
return Response(serializer.data)

仔细查看create((方法的第一行和update((法的第三行。

create((方法中,序列化程序类仅使用请求.data实例化,并且在实例参数上不发送任何内容。因此,instance属性为None,序列化程序正在执行create相关操作。

update((方法上,序列化程序类用一个名为instance的变量以及request.data实例化。并检查update((方法第2行更新方法是如何获得instance变量值的。因此,现在序列化程序类instance属性不是None,因此它正在执行update相关操作。

这里实例的值必须是一个有效的模型对象。

现在将所有这些概念汇总在一起。我假设您的API是一个POST请求API,如果表上不存在您的电子邮件检查的其他实例,它将创建一个新实例,并且如果发现任何具有此电子邮件地址的实例,则它将执行更新操作。

def create(self, request, *args, **kwargs):
try:
instance = self.model.objects.get(email=request.data['email'])
serializer = self.get_serializer(instance, data=request.data, partial=False)
except self.model.DoesNotExist:
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

在这里,你可以看到,如果找到了一个具有电子邮件地址的实例,它会更新它,如果没有找到,它会创建一个新的实例。

警告:访问原始请求正文数据是危险的。为了确保安全,您可以在拨打数据库

更多详细信息,请查看DRF包的Github源代码。

您应该为Update操作编写另一个serializer。

最新更新