[DRF] prefetch_related와 SerializerMethodField
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')