Search code examples
jsondjangovalidationserializationdjango-rest-viewsets

How can I serialize a list of objects and return the object if validated?


I have the below in one of my viewset classes:

    serializer = ResponsesSerializer(data=queryset, many=True)
    serializer.is_valid(raise_exception=True)
    validated_data = serializer.validated_data

    response = Response(validated_data, status=status.HTTP_200_OK)
    return response

For testing purposes, queryset is the below list of objects that is being passed to my ResponsesSerializer, where only the first object has a record key:

queryset = [
                {
                    "C_Ethnicity.r1": 0,
                    "C_Ethnicity.r2": 0,
                    "EaseOfFind.r1": 0,
                    "EaseOfFind.r2": 1,
                    "Leg.r1": 0,
                    "Leg.r2": 0,
                    "record": 17
                },
                {
                    "C_Ethnicity.r1": 0,
                    "C_Ethnicity.r2": 0,
                    "EaseOfFind.r1": 0,
                    "EaseOfFind.r2": 1,
                    "Leg.r1": 1,
                    "Leg.r2": 0,
                },
                    {
                    "C_Ethnicity.r1": 0,
                    "C_Ethnicity.r2": 0,
                    "EaseOfFind.r1": 0,
                    "EaseOfFind.r2": 1,
                    "Leg.r1": 1,
                    "Leg.r2": 0,
                }
            ]

In the response, the serializer expectedly raises a validation error for the last two objects without a record key, but I don't know why the first object is then empty:

[
    {},
    {
        "non_field_errors": [
            "Missing 'record' key in dictionary."
        ]
    },
    {
        "non_field_errors": [
            "Missing 'record' key in dictionary."
        ]
    }
]

This is the ResponsesSerializer:

class ResponsesSerializer(serializers.Serializer):
    def to_internal_value(self, data):
        return data

    def validate(self, data):
        if 'record' not in data:
            raise serializers.ValidationError("Missing 'record' key in dictionary.")
        return data

I'm new to serializers, and in this case I just want to add validation criteria that each object in the list can be tested for, i.e having a record key or a certain length of items, and ignoring any other keys.


Solution

  • but I don't know why the first object is then empty.

    The serializer validates all elements in the list, and only is valid in case all items are valid.

    If one or more items are invalid, it returns the errors. It does that with a list of dictionaries where for each item in the given data, it has an item in the error list. This makes sense since one probably wants to know for which item which errors apply. So the first dictionary contains errors for the first item.

    Since the first item thus contains no errors, it is an empty dictionary, meaning that the first item is valid, but the rest is not.

    It probably makes a lot of sense to only insert data if all items match, since one usually wants operations to be atomically: either all succeed (together) or none at all.

    I just want to add validation criteria that each object in the list can be tested for, i.e having a record key or a certain length of items.

    Typically you don't implement validate yourself. You add fields to the serializer, like:

    class ResponsesSerializer(serializers.Serializer):
        record = serializer.CharField(required=True)

    this will then check if there is indeed a record field that is a string.

    or a certain length of items.

    In that case you can work with a ListSerializer, which is essentially what a many=True does. You can easily construct this with:

    serializer = ResponsesSerializer(many=True, min_length=5, max_length=5)

    this will require that there are exactly five items in the request.