python - Django customize validation error message - Stack Overflow

admin2025-04-17  3

I have a following serializer definitions that validate request payload:

# diagnosis serializer
class ICD10Serializer(serializers.Serializer):
    icd_10 = serializers.IntegerField(required=True, allow_null=False)

class DetailsSerializer(serializers.Serializer):
    diagnosis_details = ICD10Serializer(many=True)

class ChartUpdateSerializer(serializers.Serializer):
    diagnosis = DetailsSerializer(many=True)

Its usage:

payload = ChartUpdateSerializer(data=request.data)
if not payload.is_valid():
   raise serializers.ValidationError(payload.errors)

This throws validation error message in the following format:

{
    "diagnosis": [
        {
            "diagnosisDetails": [
                {}, <- valid
                {}, <- valid
                {}, <- valid
                {}, <- valid
                {
                    "icd10": [
                        "This field may not be null."
                    ]
                }
            ]
        }
    ]
}

Here {} is also shown for valid ones. Can we simply raise validation error for invalid ones? Or better even if we can know which field and the message so custom message can be generated.

I have a following serializer definitions that validate request payload:

# diagnosis serializer
class ICD10Serializer(serializers.Serializer):
    icd_10 = serializers.IntegerField(required=True, allow_null=False)

class DetailsSerializer(serializers.Serializer):
    diagnosis_details = ICD10Serializer(many=True)

class ChartUpdateSerializer(serializers.Serializer):
    diagnosis = DetailsSerializer(many=True)

Its usage:

payload = ChartUpdateSerializer(data=request.data)
if not payload.is_valid():
   raise serializers.ValidationError(payload.errors)

This throws validation error message in the following format:

{
    "diagnosis": [
        {
            "diagnosisDetails": [
                {}, <- valid
                {}, <- valid
                {}, <- valid
                {}, <- valid
                {
                    "icd10": [
                        "This field may not be null."
                    ]
                }
            ]
        }
    ]
}

Here {} is also shown for valid ones. Can we simply raise validation error for invalid ones? Or better even if we can know which field and the message so custom message can be generated.

Share Improve this question edited Jan 31 at 9:02 Abdul Aziz Barkat 21.9k8 gold badges71 silver badges89 bronze badges asked Jan 31 at 8:39 AzimaAzima 4,15115 gold badges58 silver badges113 bronze badges 1
  • How will you know which one it is? Since there are here five diagnosis elements, if we move it to the top, we thus don't know if the first ones invalid, or the fifth one. – willeM_ Van Onsem Commented Jan 31 at 8:45
Add a comment  | 

1 Answer 1

Reset to default 0

If you really need it, you can use, for example, a script like this:

def remove_empty_objects(errors, _parent_pos=None):
    def filter_empty_list_objects():
        for pos, list_value in enumerate(value, 1):
            if not list_value:
                continue

            if isinstance(list_value, ErrorDetail):
                msg = f' Init array position: {_parent_pos!r}'
                yield ErrorDetail(
                    string=str(list_value) + msg,
                    code=list_value.code,
                )
            else:
                yield remove_empty_objects(errors=list_value, _parent_pos=pos)

    for key, value in errors.items():
        if isinstance(value, list):
            errors[key] = [*filter_empty_list_objects()]
    return errors

This script will recursively go through all objects and remove empty values. I also added a message indicating the position of the object in the source array (note that here the position number starts with 1, not 0.) to get rid of the problem @willeM_ Van Onsem wrote about in the comments. But you can generally override the messages completely if you need to. I haven't tested this script extensively, but for the case you provided, it will work. Here's an example:

errors = {
    'diagnosis': [
        {'diagnosis_details': [
            {}, 
            {'icd_10': [ErrorDetail(string='This field may not be null.', code='null')]}, 
            {'icd_10': [ErrorDetail(string='This field may not be null.', code='null')]}, 
            {}, 
            {},
        ]}, 
        {'diagnosis_details': [
            {}, 
            {'icd_10': [ErrorDetail(string='This field may not be null.', code='null')]},
        ]}
    ]
}

result = remove_empty_objects(errors=errors)

{'diagnosis': [{'diagnosis_details': [{'icd_10': [ErrorDetail(string='This field may not be null. Init array position: 2', code='null')]},
                                      {'icd_10': [ErrorDetail(string='This field may not be null. Init array position: 3', code='null')]}]},
               {'diagnosis_details': [{'icd_10': [ErrorDetail(string='This field may not be null. Init array position: 2', code='null')]}]}]}

UPDATED

Two more ways can be implemented, the first option will find full paths for non-empty objects:

def find_not_empty_objects_keys(errors, *positions):  
    def find_objects_full_paths():  
        for pos, list_value in enumerate(value):  
            if isinstance(list_value, ErrorDetail):  
                yield positions + (key, pos)  
            elif list_value:  
                yield from find_not_empty_objects_keys(  
                    list_value,  
                    *positions, key, pos,  
                )  
  
    result = []  
    for key, value in errors.items():  
        if isinstance(value, list):  
            result.extend(find_objects_full_paths())  
    return result

The second option will find full paths for only empty objects:

def find_empty_objects_keys(errors, *positions):  
    def find_objects_full_paths():  
        for pos, list_value in enumerate(value):  
            if not list_value:  
                yield positions + (key, pos)  
            elif not isinstance(list_value, ErrorDetail):  
                yield from find_empty_objects_keys(  
                    list_value,  
                    *positions, key, pos,  
                )  
  
    result = []  
    for key, value in errors.items():  
        if isinstance(value, list):  
            result.extend(find_objects_full_paths())  
    return result

Then for the errors above, this will return the following results:

empty_objects = find_empty_objects_keys(errors=errors)
#  [('diagnosis', 0, 'diagnosis_details', 0), ('diagnosis', 0, 'diagnosis_details', 3), ('diagnosis', 0, 'diagnosis_details', 4), ('diagnosis', 1, 'diagnosis_details', 0)]

not_empty_objects = find_not_empty_objects_keys(errors=errors)
#  [('diagnosis', 0, 'diagnosis_details', 1, 'icd_10', 0), ('diagnosis', 0, 'diagnosis_details', 2, 'icd_10', 0), ('diagnosis', 1, 'diagnosis_details', 1, 'icd_10', 0)]

And then, for example, you can do something with empty or non-empty objects, thanks to the fact that lists and dictionaries support the universal __getitem__ method, for example, filter and/or modify:

for keys in empty_objects:  
    init_errors_object = errors  
    for key in keys:  
        init_object = init_object[key]
    init_errors_object['icd10'] = ['This field is valid.']
转载请注明原文地址:http://anycun.com/QandA/1744873944a88838.html