Getting objects before validating data Django REST

Question:

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?

Asked By: David Alford

||

Answers:

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.

Answered By: allexiusw

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
    ]
}
Answered By: Metalgear