5

I have a OneToMany relation. One Construction and many Cameras. I want to return all Building object fields in CameraSerializer

Problem

When I perform POST request (create new Camera object)

{
    "name": "CameraName",
    "url": "CameraUrl",
    "building": 2
    
}

I have an error

{
    "building": {
        "nonFieldErrors": [
            "Invalid data. Expected a dictionary, but got int."
        ]
    }
}

Reason of error -- Django expects FULL Construction object, but I want to set only ID

How can I fix the error?

models.py

class Construction(models.Model):
    """ Объект строительства"""
    developer = models.ForeignKey(
        Developer, related_name="constructions", on_delete=models.CASCADE
    )
    name = models.CharField(max_length=100)
    plan_image = models.ImageField(upload_to=name_image, blank=True, null=True)
    ...

    def __str__(self):
        return self.name

class Camera(models.Model):
    building = models.ForeignKey(
        Construction, related_name="cameras", on_delete=models.CASCADE
    )
    name = models.CharField(max_length=100)
    url = models.CharField(max_length=100)
    ...

    def __str__(self):
        return self.name

serializers.py

class ConstructionSerializer(serializers.ModelSerializer):
    coordinates = MyPointField()
    deadline = serializers.DateTimeField(format=TIME_FORMAT)
    cameras_number = serializers.SerializerMethodField()
    developer_name = serializers.SerializerMethodField()
    events = serializers.SerializerMethodField()

    class Meta:
        model = Construction
        fields = (
            'id', 'developer', 'developer_name', 'name', 'plan_image', 'address', 'coordinates', 'deadline',
            'workers_number', 'machines_number', 'cameras_number', 'events'
        )
        read_only_fields = ('workers_number', 'machines_number', 'cameras_number', 'events')

    
    def create(self, validated_data):
        instance = super().create(validated_data=validated_data)
        return instance


class CameraSerializer(serializers.ModelSerializer):
    frames = FrameSerializer(many=True, read_only=True)
    building = ConstructionSerializer()

    class Meta:
        model = Camera
        fields = (
            'id', 'building', 'name', 'url', 'zone_id_x', 'zone_id_y',
            'proc_id', 'path_frames', 'frames'
        )
        read_only_fields = ('proc_id', 'path_frames', 'frames')
    
    def create(self, validated_data):
        instance = super().create(validated_data=validated_data)
        instance.set_proc_id()
        instance.set_path_frame()
        return instance

views.py

class CameraView(viewsets.ModelViewSet):
    serializer_class = CameraSerializer
    queryset = Camera.objects.all()

    def get_camera_create_serializer(self, *args, **kwargs):
        kwargs["context"] = self.get_serializer_context()
        return self.serializer_class(*args, **kwargs)

    def create(self, request, *args, **kwargs):
        serializer = self.get_camera_create_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        response = {"result": serializer.data}
        return Response(
            response, status=status.HTTP_201_CREATED, headers=headers
        )
swor
  • 751
  • 3
  • 8
  • 21
  • 1
    This is a common scenario in DRF that occurs when creating and displaying uses the same serializer. One approach to solve this is discussed here: https://stackoverflow.com/a/41313121/6759844 – Brian Destura Jul 06 '21 at 23:47
  • 3
    Another approach is to set `building = ConstructionSerializer()` to read only, and add another field (something like `building_id`) with type `PrimaryKeyRelatedField`. When creating/POST, you can specify the building FK using `building_id` – Brian Destura Jul 06 '21 at 23:49

0 Answers0