[Django Learning] (9) Custom Validator_Single Field_Multi-Field Validation_Model Serializer Class

 Previously, I learned that serializers are used in view sets for serialization and deserialization operations. To define serializer classes, you need to inherit the base class of Serializer or a subclass of Serializer;

This time we will learn how to customize the validator, how to perform single-field or multi-field validation, and finally use the model serializer initially

1. Custom Validator

For example, define a function that checks whether the project name field contains "project":

def is_contain_project_word(value):
    if "项目" not in value:
        raise serializers.ValidationError("项目名称里必须包含'项目'")
  • When defining field attributes, the validators parameter is sometimes used;
  • The validators parameter can only be a list or element, and each element in the list or tuple is a constraint
  • You can use UniqueValidator to specify the unique constraint condition, the first parameter is the query set object, and the message keyword parameter can specify the specific error message
  • If the verification fails, a serializers.ValidationError object must be returned, and specific error message can be specified

 Add a custom validation function to the list of validators:

class ProjectSerializer(serializers.Serializer):
    id = serializers.IntegerField(label="id主键", help_text="id主键", read_only=True)
    name = serializers.CharField(label="接口名称", help_text="接口名称", max_length=10, min_length=5,
                                 validators=[UniqueValidator(ProjectsModel.objects.all(), message="项目名称不可重复!"),
                                             is_contain_project_word])

2. Single field verification

In addition to the custom validator above that can perform single-field validation, we can also use validate_ as a prefix in the serializer class , and then add the method defined by the field name to validate a single field

    def validate_name(self, value):
        if not value.endswith("项目"):
            return serializers.ValidationError("项目名称必须以'项目'结尾!")
        return value

The above is to verify the name field to verify whether it ends with "item" 

  • If the verification is passed, the value after the verification must be returned (such as the return value above), if not returned, the value received later will be None, and an exception will be generated and an error will be reported
  • If the verification fails, call serializers.ValidationError and return a custom error message (for example, the above "The project name must end with 'Project'!")

3. Multi-field verification

  • Multiple fields can be validated in the validate method
  • The second parameter is the data after the above verification is passed (dictionary type)
  • Must return the value after the verification is passed
    def validate(self, attrs):
        project_leaders=attrs.get('leaders')
        project_name=attrs.get('name')
        if '花生' not in project_leaders or len(project_name) <= len(project_leaders):
            raise serializers.ValidationError('项目负责人名称必须得包含“花生”并且项目名称的长度要超过项目负责人名称')
        return attrs

serializers serializers.py

def is_container_project_word(value):
    if '项目' not in value:
        raise serializers.ValidationError('项目名称中必须得包含“项目”')

# class ValidateProject:
#     def __call__(self, *args, **kwargs):
#         pass


class ProjectSerializer(serializers.Serializer):

    name = serializers.CharField(label='项目名称', help_text='项目名称', min_length=5, max_length=10, write_only=True,
                                 validators=[UniqueValidator(Projects.objects.all(), message='项目名不能重复'),
                                             is_container_project_word],
                                 error_messages={'min_length': '项目名称不能少于5位',
                                                 'max_length': '项目名称不能超过10位', 'required': '项目名称为必传参数'})
    leader = serializers.CharField(label='项目负责人', help_text='项目负责人',
                                   min_length=5, max_length=8)
    desc = serializers.CharField(label='简要描述', help_text='简要描述', allow_blank=True, allow_null=True,
                                 default='')
    # token = serializers.CharField(read_only=True)
    def validate_name(self, value):
        if not value.endswith('项目'):
            raise serializers.ValidationError('项目名称必须得以“项目”结尾')
        return value

    def validate(self, attrs):
        leader_name = attrs.get('leader')
        project_name = attrs.get('name')
        if '花生' not in leader_name or len(project_name) <= len(leader_name):
            raise serializers.ValidationError('项目负责人名称必须得包含“花生”并且项目名称的长度要超过项目负责人名称')
        return attrs

    def create(self, validated_data):
        pro = Projects.objects.create(**validated_data)
        return pro

    def update(self, instance, validated_data):
        instance.name = validated_data.get('name')
        instance.leader = validated_data.get('leader')
        instance.desc = validated_data.get('desc')
        instance.save()
        instance.token = 'shisdhisfhishfisfh'
        return instance

 4. Optimization (view set and model class serializer)

Because many operations in the view set are repeated, such as serialization and deserialization operations; the fields and corresponding parameters in the serializer also need to be set repeatedly, which virtually increases the amount of code

 So this time we will optimize and upgrade the serializer and view set to reduce the amount of code

4.1 Optimized view set views.py

class ProjectDetailView(View):
    def get_object(self, pk):
        ret = {
            "msg": "参数异常",
            "code": 0
        }

        # 数据校验(校验项目id是否存在)
        try:
            return Projects.objects.get(pk=pk)
        except Exception:
            raise Http404

    def get(self, request, pk):

        pro = self.get_object(pk)
        serializer_obj = ProjectSerializer(instance=pro)
        return JsonResponse(serializer_obj.data, json_dumps_params={"ensure_ascii": False})

    def put(self, request, pk):
        ret = {
            "msg": "参数异常",
            "code": 0
        }

        pro = self.get_object(pk)

        json_data = request.body.decode('utf-8')
        try:
            python_data = json.loads(json_data)
        except json.JSONDecodeError:
            return JsonResponse(ret, status=400, json_dumps_params={"ensure_ascii": False})

        # a.在创建序列化器对象时,如果同时给instance与data传参,那么序列化器对象调用save()方法时,会自动调用update()方法
        serializer_obj1 = ProjectSerializer(instance=pro, data=python_data)
        try:
            serializer_obj1.is_valid(raise_exception=True)
        except:
            ret.update(serializer_obj1.errors)
            return JsonResponse(ret, status=400, json_dumps_params={"ensure_ascii": False})

        # 更新数据
        # a.如果给save传递参数,会自动将参数合并到validated_data中
        # b.如果传递重复参数,那么会将之前的前端传递的参数覆盖
        #serializer_obj1.save(age=18, name='唯一Love')
        serializer_obj1.save()

        return JsonResponse(serializer_obj1.data, json_dumps_params={"ensure_ascii": False})

    def delete(self, request, pk):
        ret = {
            "msg": "参数异常",
            "code": 0
        }

        pro = self.get_object(pk)
        pro.delete()

        # 返回数据
        return JsonResponse(None, safe=False, status=204)


class ProjectView(View):
    def get(self, request):
        # 获取所有的项目数据
        qs = Projects.objects.all()

        serializer_obj = ProjectSerializer(instance=qs, many=True)
        return JsonResponse(serializer_obj.data, json_dumps_params={"ensure_ascii": False})

    def post(self, request):
        ret = {
            "msg": "参数异常",
            "code": 0
        }
        # 获取json格式的字符串数据
        json_data = request.body.decode('utf-8')
        try:
            python_data = json.loads(json_data)
        except json.JSONDecodeError:
            return JsonResponse(ret, status=400, json_dumps_params={"ensure_ascii": False})
        # b.在创建序列化器对象时,如果只给data传参,那么序列化器对象调用save()方法时,会自动调用create()方法
        serializer_obj1 = ProjectSerializer(data=python_data)

        try:
            serializer_obj1.is_valid(raise_exception=True)
        except:
            ret.update(serializer_obj1.errors)
            return JsonResponse(ret, status=400, json_dumps_params={"ensure_ascii": False})

        serializer_obj1.save()
 
        return JsonResponse(serializer_obj1.data, json_dumps_params={"ensure_ascii": False})

 In the above code, deserialization input and serialization output

  • serialized output
    • Passing the model class object to instance will return a serializer class object,
      • 例如:serializer_obj = ProjectSerializer(instance=pro)
    • If you pass a queryset object, you need to add many=True
      • 例如:serializer_obj = ProjectSerializer(instance=pro,many=True)
    • You can use the data property of the serializer object to get the serialized data (dictionary or list of nested dictionaries)
      • For example:
            def get(self, request, pk):
                pro=self.get_object(pk)
                serializer_obj=ProjectModelSerializer(instance=pro)
                return JsonResponse(serializer_obj.data,json_dumps_params={"ensure_ascii": False})

  • deserialize input
    • Passing a dictionary type or (a list of nested dictionaries) to the data parameter will return a serializer class object
    • You must call the .is_valid() method of the serializer class object before you can start to verify the parameters. If the verification is successful, it will return True, otherwise it will return False
    • You can use the .errors property of the serializer class object to get error information (dictionary type)
    • You can use the .validated_data attribute of the serializer class object to get the data after the verification is passed
  • Create data, return model class object
    • When the model class object calls the save() method, it will automatically call the create() method
    • If the serializer object passes keyword parameters in when calling the save() method, then when the create() method is automatically called, it will be automatically merged into validated_data
    • If the save() method is not called, the create method and update method will not be called, and the output will be based on the data in validated_data (applicable to query operations)

4.2 Optimizing the serializer class

It was a serializer class before, this time it is optimized to use a model serializer class

  • You can inherit the ModelSerializer class or a subclass of ModelSerializer to create a model serializer class
  • To automatically generate serializer fields and related validation rules through the specified model class

class ProjectModelSerializer(serializers.ModelSerializer):

    # a.必须得在Meta内部类中使用model属性指定,参考的模型类
    # b.可以使用fields属性来指定哪些模型类中的字段,需要自动生成序列化器字段
    class Meta:
        model = Projects
        # c.指定所有的字段
        # fields = '__all__'
        # d.可以将需要自动生成序列化器字段的模型类字段添加到元组中
        fields = ('name', 'leader','tester')
    def validate_name(self, value):
        if not value.endswith("项目"):
            return serializers.ValidationError("项目名称必须以'项目'结尾!")
        return value
        # 如果不返回value,结果则为None

    def validate(self, attrs):
        project_leaders = attrs.get('leaders')
        project_name = attrs.get('name')
        if '花生' not in project_leaders or len(project_name) <= len(project_leaders):
            raise serializers.ValidationError('项目负责人名称必须得包含“花生”并且项目名称的长度要超过项目负责人名称')
        return attrs

    def create(self, validated_data):
        return ProjectsModel.objects.create(**validated_data)

    def update(self, instance, validated_data):
        # 修改老数据,传入新数据给对应字段
        instance.name = validated_data.get("name")
        instance.leader = validated_data.get("leader")
        instance.desc = validated_data.get("desc")
        instance.programmer = validated_data.get("programmer")
        instance.tester = validated_data.get("tester")
        instance.publish_app = validated_data.get("publish_app")
        instance.save()

        return instance

So the code in the views.py file above can also be optimized: replace calling ProjectSerializer with ProjectModelSerializer when creating a serializer object

Screenshot of debug running through:

 

 At this time, the optimization of view sets and serializers has been initially implemented, and the model serializer class will continue to be optimized later

Guess you like

Origin blog.csdn.net/weixin_43569834/article/details/131396392