drf源码_数据检验

1、内置校验源码

class BaseSerializer(Field):
   def __init__(self, instance=None, data=empty, **kwargs):
        self.instance = instance
        if data is not empty:
            self.initial_data = data
            
    def is_valid(self, *, raise_exception=False):
          assert hasattr(self, 'initial_data'),
            if not hasattr(self, '_validated_data'):
                try:
                    self._validated_data = self.run_validation(self.initial_data)
                except ValidationError as exc:
                    self._validated_data = {}
                    self._errors = exc.detail
                else:
                    self._errors = {}

            if self._errors and raise_exception:
                raise ValidationError(self.errors)

            return not bool(self._errors)
    
class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
      def run_validation(self, data=empty):
            (is_empty_value, data) = self.validate_empty_values(data)
            if is_empty_value:
                return data

            value = self.to_internal_value(data)
            try:
                self.run_validators(value)
                value = self.validate(value)
                assert value is not None, '.validate() should return the validated data'
            except (ValidationError, DjangoValidationError) as exc:
                raise ValidationError(detail=as_serializer_error(exc))

            return value
        
        
    def to_internal_value(self, data):
        """
        Dict of native values <- Dict of primitive datatypes.
        """
        if not isinstance(data, Mapping):
            message = self.error_messages['invalid'].format(
                datatype=type(data).__name__
            )
            raise ValidationError({
                api_settings.NON_FIELD_ERRORS_KEY: [message]
            }, code='invalid')

        ret = OrderedDict()
        errors = OrderedDict()
        fields = self._writable_fields

        for field in fields:
            validate_method = getattr(self, 'validate_' + field.field_name, None)
            primitive_value = field.get_value(data)
            try:
                validated_value = field.run_validation(primitive_value)
                if validate_method is not None:
                    validated_value = validate_method(validated_value)
            except ValidationError as exc:
                errors[field.field_name] = exc.detail
            except DjangoValidationError as exc:
                errors[field.field_name] = get_error_detail(exc)
            except SkipField:
                pass
            else:
                set_value(ret, field.source_attrs, validated_value)

        if errors:
            raise ValidationError(errors)

        return ret


class Field:    
    def validate_empty_values(self, data):
        if self.read_only:
            return (True, self.get_default())

        if data is empty:
            if getattr(self.root, 'partial', False):
                raise SkipField()
            if self.required:
                self.fail('required')
            return (True, self.get_default())

        if data is None:
            if not self.allow_null:
                self.fail('null')
            # Nullable `source='*'` fields should not be skipped when its named
            # field is given a null value. This is because `source='*'` means
            # the field is passed the entire object, which is not null.
            elif self.source == '*':
                return (False, None)
            return (True, None)

        return (False, data)

1.1 校验的使用

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers


class InfoSerializer(serializers.Serializer):
    title = serializers.CharField(required=True, max_length=20, min_length=6)
    order = serializers.IntegerField(required=False, max_value=100, min_value=10)
    level = serializers.ChoiceField(choices=[("1", "高级"), (2, "中级")])


class InfoView(APIView):
    def post(self, request):
        ser = InfoSerializer(data=request.data)
        if ser.is_valid():
            return Response(ser.validated_data)
        else:
            return Response(ser.errors)

2、正则校验

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
# 
from django.core.validators import RegexValidator, EmailValidator


class InfoSerializer(serializers.Serializer):
    # required 非空校验
    # max_length 最大长度校验
    # min_length 最小长度校验
    title = serializers.CharField(required=True, max_length=20, min_length=6)
    order = serializers.IntegerField(required=False, max_value=100, min_value=10)
    # choices 1和2 可以写成字符串和数字,最后都会变为字符串
    level = serializers.ChoiceField(choices=[("1", "高级"), (2, "中级")])

     # EmailField 也是校验形式一种方式,等同于CharField 中加上参数 EmailValidator
    # email = serializers.EmailField()
    email = serializers.CharField(validators=[EmailValidator(message="邮箱格式错误")])
	# 内置正则校验,message代表校验异常时抛出的信息
    # RegexValidator, EmailValidator都是从django中直接调用使用的
    more = serializers.CharField(validators=[RegexValidator(r"\d+", message="格式错误")])

    code = serializers.CharField()


class InfoView(APIView):
    def post(self, request):
        ser = InfoSerializer(data=request.data)
        if ser.is_valid():
            return Response(ser.validated_data)
        else:
            return Response(ser.errors)

3、钩子校验

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework import exceptions


class InfoSerializer(serializers.Serializer):
    title = serializers.CharField(required=True, max_length=20, min_length=6)
    order = serializers.IntegerField(required=False, max_value=100, min_value=10)
    code = serializers.CharField()

    def validate_code(self, value):
        print(value)
        if len(value) > 6:
            raise exceptions.ValidationError("字段钩子校验失败")
        return value
	
    # 额外校验,有些场景需要对字段组合校验,比如多个字段需要满足什么规则,所以用
    # 这个方法
    def validate(self, attrs):
        print("validate=", attrs)
        
        # api_settings.NON_FIELD_ERRORS_KEY,额外校验时,NON_FIELD_ERRORS_KEY
        # 这个值为报错的键值,我们可以根据这个值NON_FIELD_ERRORS_KEY,去寻找对应的报		错信息
        # raise exceptions.ValidationError("全局钩子校验失败")
        return attrs


class InfoView(APIView):
    def post(self, request):
        ser = InfoSerializer(data=request.data)
        if ser.is_valid():
            return Response(ser.validated_data)
        else:
            return Response(ser.errors)

4、Model校验

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework import exceptions
from api import models
from django.core.validators import RegexValidator


class RoleSerializer(serializers.ModelSerializer):
    more = serializers.CharField(required=True)

    class Meta:
        model = models.Role
        fields = ["title", "order", "more"]
        # 用ModelSerializer时,我们需要内置校验和正则校验时,可以在Meta元信息里
        # 定义参数
        extra_kwargs = {
            # 字段名 : 校验规则
            # 其他的 required=True, max_length=20, min_length=6 等这些校验
            # 规则都可以通过这种字典的形式,输入校验规则
            "title": {"validators": [RegexValidator(r"\d+", message="格式错误")]},
            "order": {"min_value": 5},
        }

    def validate_more(self, value):
        return value

    def validate(self, attrs):
        return attrs


class InfoView(APIView):
    def post(self, request):
        ser = RoleSerializer(data=request.data)
        if ser.is_valid():
            return Response(ser.validated_data)
        else:
            return Response(ser.errors)

5、校验+保存

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework import exceptions
from api import models
from django.core.validators import RegexValidator


class RoleSerializer(serializers.ModelSerializer):
    more = serializers.CharField(required=True)

    class Meta:
        model = models.Role
        fields = ["title", "order", "more"]
        extra_kwargs = {
            "title": {"validators": [RegexValidator(r"\d+", message="格式错误")]},
            "order": {"min_value": 5},
        }

    def validate_more(self, value):
        return value

    def validate(self, attrs):
        return attrs


class InfoView(APIView):
    def post(self, request):
        ser = RoleSerializer(data=request.data)
        if ser.is_valid():
            ser.validated_data.pop("more")
            # 通过save,序列化类可以直接將validated_data保存到数据库
            #  对于保存时,少字段的情况,比如有些字段不需要客户提交的,所以
            #  validated_data 中会少这个字段的值,这个时候我们需要在
            # save中赋值 ser.save(字段名=xxxx)来保存,要不然后会报错
            # 自增id客户没有提交时,我们应该也不需要赋值(后面需要确认)
            
            # 对于保存多字段的情况,有时候我们对客户展示的字段是数据库没有的
            # 那么获得的数据就会多字段,这种情况跟django不一样,不会剔除
            # 要自己手动剔除 比如  ser.validated_data.pop("more")
            instance = ser.save()  # ser.save(v1=123,v2=234)
            print(instance)
            # 校验后的字段值 都在validated_data 字典中 跟django中的
            # cleaned_data 是一样的
            return Response(ser.validated_data)
        else:
            return Response(ser.errors)

6、 校验+保存+FK+M2M

FK
# 1、当我们提交数据时,涉及到外键字段时,该字段会自动校验,看该数据是否在主表中内的数据,校验通过后会保存到数据库
# 2、如果想要更加复杂的校验,可以自定义钩子方法validatae_外键字段,此时里面传入的value
# 值是一个 主表对象,可以通过 对象.xxx来进行更复杂的校验
# 3、如果我们在Fields中直接写入 外键_id 字段时,传过来数据
# 此时modelSerializer 不会直接生成对应的关系,不能成功保存到
# 数据库
# 4、如果保存 外键_id  可以通过 外键_id = Serializer.字段类型() 然后再加入Fields中,这个时候提交depart_id时可以成功
# 5、post请求的字段是外键的 字段名 不是外键_id

ManytoMany校验
# 1、当我们提交数据时,涉及到关联字段时,该字段会自动校验,看该数据是否在关联表中内的数据,校验通过后会保存到数据库
# 2、如果想要更加复杂的校验,可以自定义钩子方法validatae_关联字段,此时里面传入的value
# 值是一个列表,列表中的元素是关联表的对象,可以循环来校验
# post提交请求的字段是 多对多的字段名
# 3、manytomany 字段自定制校验,可以 通过 Serializer.ListField()或者 Serializer.DictField() [manytomany 自定义字段时只能通过这两种方式定义,更多是ListField] 自定义字段xxx,然后把xxx加入到Fields中,通过validatae_xxx可以自定义校验方法,但是xxx是无法正常保存到数据库,保存到数据库的时候需要删除,如果要想要自定义校验规则并且保存到数据库,需要自定义字段名跟多对多字段名一致,然后返回的是对应的
# 条件的queryset对象 可以正常保存到数据库

from django.db import models


class Role(models.Model):
    title = models.CharField(verbose_name="标题", max_length=32)
    order = models.IntegerField(verbose_name="顺序")


class Tag(models.Model):
    caption = models.CharField(verbose_name="名称", max_length=32)


class UserInfo(models.Model):
    name = models.CharField(verbose_name="姓名", max_length=32)
    gender = models.SmallIntegerField(verbose_name="性别", choices=((1, "男"), (2, "女")))
    role = models.ForeignKey(verbose_name="角色", to="Role", on_delete=models.CASCADE)
    ctime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True)

    tags = models.ManyToManyField(verbose_name="标签", to="Tag")

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework import exceptions
from api import models
from django.core.validators import RegexValidator
import datetime


class UserInfoSerializer(serializers.ModelSerializer):
    more = serializers.CharField(required=True)

    class Meta:
        model = models.UserInfo
        fields = ["name", "gender", "role", "tags", "more"]
        extra_kwargs = {
            "name": {"validators": [RegexValidator(r"n-\d+", message="格式错误")]},
        }

    def validate_more(self, value):
        return value

    def validate(self, attrs):
        return attrs


class InfoView(APIView):
    def post(self, request):
        ser = UserInfoSerializer(data=request.data)
        if ser.is_valid():
            ser.validated_data.pop("more")
            instance = ser.save(ctime=datetime.datetime.now())
            print(instance)
            # return Response(ser.validated_data)
            return Response("成功")
        else:
            return Response(ser.errors)

7、钩子 Create

自定义钩子方法

#如果我们有需求,用户提交的是gender:1  返回的是 gender:男
# 如果用SerializerMethodField方法去自定义钩子get_xxx,在实例化该类时,会初始化
read_only参数为True,也就是SerializerMethodField这个字不需要接收值,然后这个时候把
gender放在Fields这个字段,这个时候gender会跟数据库原有字段冲突会报错,所以用自定义get_xx 不符合要求,如果跟数据库字段同名,不需要提交数据,只做序列化是不会报错

# 用Charfield重新gender 字段也不可以,还是只是会显示1
# 可以自定义钩子方法类,然后被继承


# 下面代码就解决问题了,自定义 nb_xxxx 钩子方法
class NbHookSerializer(object):

    def to_representation(self, instance):
        ret = OrderedDict()
        fields = self._readable_fields

        for field in fields:
            # 此处为拓展的钩子方法类,instacne代表obj对象
            # 妙哉
            if hasattr(self, 'nb_%s' % field.field_name):
                value = getattr(self, 'nb_%s' % field.field_name)(instance)
                ret[field.field_name] = value
            else:
                try:
                    attribute = field.get_attribute(instance)
                except SkipField:
                    continue

                check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
                if check_for_none is None:
                    ret[field.field_name] = None
                else:
                    ret[field.field_name] = field.to_representation(attribute)

        return ret

    
class SbModelSerializer(NbHookSerializer, serializers.ModelSerializer):
    class Meta:
        model = models.NbUserInfo
        fields = ["id", "name", "age", "gender"]
        extra_kwargs = {
            "id": {"read_only": True}
        }

    def nb_gender(self, obj):
        return obj.get_gender_display()

    def nb_name(self, obj):
        return obj.get_gender_display()

8、校验+序列化

1、如果对新增数据 校验过后,成功保存到数据库后,想要序列化返回,此时可以校验定义一个
序列化类 序列化 定义一个序列化类
2、校验可以跟序列化同时用一个序列化类,但是两者的字段是一致的
3、如果想校验和序列化 字段不一致,可以通过read_only 和 write_only控制
read_only表示序列化字段,在提交时,如果有字段设置为True,那么这个字段可以不
用提交,不用校验,知识序列化后显示,比如自增id
write_only为True时,这个字段提交时会校验,但是不会序列化返回,比如密码这种字段

不对外界展示

class UsView(APIView):
    def post(self, request, *args, **kwargs):
        # 1.获取原始数据
        # print(request.data)

        # 2.校验
        ser = UsModelSerializer(data=request.data)
        if ser.is_valid():
            print("视图", ser.validated_data)
            # ser.validated_data.pop("tags")
            # instance返回的是当前序列化类中对应表中对应的数据对象
            instance = ser.save()
            instance.id
            instance.name
            instance.age
        else:
            print("视图", ser.errors)
        return Response("成功")
    
校验和序列化 用同一个序列化类  方式1
class DpView(APIView):
    def post(self, request, *args, **kwargs):
        ser = DpModelSerializer(data=request.data)
        if ser.is_valid():
            instance = ser.save()
            print(instance)
            xx = DpModelSerializer(instance=instance)
            return Response(xx.data)

        else:
            return Response(ser.errors)
        

 校验和序列化 用同一个序列化类 方式2的写法更简单,因为当调用ser.save()时,
会自动把instance传入到DpModelSerializer中,然后调用ser.data再序列化
class DpView(APIView):
    def post(self, request, *args, **kwargs):
        ser = DpModelSerializer(data=request.data)
        if ser.is_valid():
             ser.save()
            return Response(ser.data)

        else:
            return Response(ser.errors)  

如果一个请求,即需要做 请求校验 又需要做 序列化 ,怎么搞呢?例如:新增数据。

  • 字段,可以通过read_only 、write_only、required 来设定
  • is_valid校验
  • data调用序列化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值