Django

[DRF] prefetch_related와 SerializerMethodField

behonestar 2016. 12. 16. 21:15

N:M 관계의 두 모델이 있다. Device 모델이 Group 모델에 대해 realted_name='devices'라고 정의하였기 때문에, Group Object는 'devices'라는 이름으로 관련된 Device 객체들에 접근할 수 있다.


class Group(models.Model):

    name = models.CharField(max_length=32, null=True)


class Device(models.Model):

    name = models.CharField(max_length=32, null=True)

    owner = models.ForeignKey(UserModel, on_delete=models.CASCADE)

    group = models.ManyToManyField(Group, related_name='devices')


Group을 Serializer하면서 관련된 Device들과 Device의 Owner의 정보까지 한 번에 Serializer하려고 한다. 


그러려면 해당 필드들을 Nested Serializer로 정의해야 한다. GroupSerializer 수행 시 1+N Select 증상이 발생하지 않도록 하려면 관련 객체들을 모두 Prefetch 해둬야 한다.


queryset = Group.objects.filter(name='my_group')

queryset = queryset.prefetch_related('devices')

queryset = queryset.prefetch_related('devices__owner')

        

serializer = GroupSerializer(queryset, many=True)

print(serializer.data)


Group Serializer의 구현을 보면... queryset에 관련 객체들이 prefetch 되어있기 때문에 DeviceSerializer와 UserSerializer 수행 시 별도로 select를 수행하지 않는다.


class GroupSerializer(serializers.ModelSerializer):

    devices = DeviceSerializer(many=True)


    class Meta:

        model = Group

        fields = ('id', 'name', 'devices')


class DeviceSerializer(serializers.ModelSerializer):

    owner = UserSerializer(many=True)


    class Meta:

        model = Device

        fields = ('id', 'name', 'owner')


serializer.SerializerMethodField를 사용하여 prefetch된 데이터를 가공하려는 경우를 보자.

마찬가지로 group.devices.all()을 호출했지만 실제로는 select가 발생하지 않고 prefetch된 데이터를 사용한다.


class GroupSerializer(serializers.ModelSerializer):

    devices = serializers.SerializerMethodField('get_devices_prefetch_related')


    def get_devices_prefetch_related(self, group):

        devices = group.devices.all()

        datas = []

        for device in devices: 

            data = {'id': device.id, 'name':device.name}

            datas.append(data)

        return datas


    class Meta:

        model = Group

        fields = ('id', 'name', 'devices')