'Getting objects before validating data Django REST

I have to create a new Chat object using this view:

class ChatListCreateView(ListCreateAPIView):
    permission_classes = [IsAuthenticated]
    serializer_class = ChatSerializer

    def get_queryset(self):
        data = Chat.objects.filter(
            Q(employees=self.request.user) | Q(created_by=self.request.user)).distinct()
        return data

The serializer it uses is:

class ChatSerializer(serializers.ModelSerializer):
    created_by = SimpleEmployeeSerializer(read_only=True)
    employees = SimpleEmployeeSerializer(many=True, read_only=True)
    title = serializers.CharField(max_length=255)

    def create(self, validated_data):
        """
        Creates a new Chat and adds the m2m employees to it
        """
        # Create and save the chat
        chat = Chat.objects.create(
            created_by=self.context['request'].user,
            title=validated_data['title'],
        )

        # Add the employees to the chat
        validated_employees = validated_data.pop('employees')
        for user_id in validated_employees:
            employee = Employee.objects.get(id=user_id)
            chat.employees.add(employee)

        return chat

My issue is that the SimpleEmployeeSerializer expects a user object but I am submitting an array of employees as such:

{
    "title": "fwef",
    "employees": [
        {
            "id": "8"
        },
        {
            "id": "30"
        }
    ]
}

What method can I implement to get the objects from these IDs before validation?



Solution 1:[1]

I think you need to add the employee_ids field in the serializer.

class ChatSerializer(serializers.ModelSerializer):
    created_by = SimpleEmployeeSerializer(read_only=True)
    employees = SimpleEmployeeSerializer(many=True, read_only=True)        
    title = serializers.CharField(max_length=255)
    employee_ids = serializers.ListField(
        child=serializers.IntegerField(), write_only=True
    )

def create(self, validated_data):
    """
    Creates a new Chat and adds the m2m employees to it
    """
    employee_ids = validated_data.pop("employee_ids")

    # Create and save the chat
    chat = Chat.objects.create(
        created_by=self.context['request'].user,
        title=validated_data['title'],
    )

    # Add the employees to the chat one by one
    for user_id in employee_ids:
        try:
            employee = Employee.objects.get(id=user_id)
            chat.employees.add(employee)
        except Employee.DoesNotExist:
            continue
    
    # Or you can add multiple objects at once
    # chat.employees.set(employee_ids)

    return chat

And you can upload json data as follows.

{
    "title": "fwef",
    "employees": [
        8, 30
    ]
}

Solution 2:[2]

Hello David you can take advantage of override the validate method in the serializer you have to do something like the following:

class ChatSerializer(serializers.ModelSerializer):
    created_by = SimpleEmployeeSerializer(read_only=True)
    employees = SimpleEmployeeSerializer(many=True, read_only=True)
    title = serializers.CharField(max_length=255)
    
    def validate_employees(self, value):
        value= transform_ids_in_objects(value) 
        # handle validation logic here 
        return value

    def create(self, validated_data):
        chat = Chat.objects.create(
            created_by=self.context['request'].user,
            title=validated_data['title'],
        )
        # ... More code here
        return chat

Documentation about validate method Something important about validate method is that this method is called before create method.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 David Lu
Solution 2 David Alford