ListSerializer и Foreign Key, is_valid при выполнении N+1 запроса
Я пытаюсь улучшить свой сериализатор, чтобы иметь возможность создавать несколько объектов с минимальными запросами. Поэтому я реализовал ListSerializer, который будет массово создавать объекты вместо вызова save для каждого объекта.
Вот мой текущий код:
class GatewayTechnicalLogListSerializer(serializers.ListSerializer):
gateway = serializers.IntegerField(required=True)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.gateways_ids: dict = {}
for gat_tech_log in self.initial_data:
self.gateways_ids[gat_tech_log['gateway']] = True
self.gateways_ids = Gateway.objects.filter(
id__in=self.gateways_ids.keys()
).only('id').values_list('id', flat=True)
def validate(self, attrs):
if attrs.gateway not in self.gateways_ids.keys():
raise serializers.ValidationError('Gateway does not exists.')
return attrs
def create(self, validated_data):
gw_tech_logs_o = [GatewayTechnicalLog(**item) for item in validated_data]
res = GatewayTechnicalLog.objects.bulk_create(gw_tech_logs_o)
return res
class GatewayTechnicalLogSerializer(serializers.ModelSerializer):
class Meta:
model = GatewayTechnicalLog
fields = '__all__'
list_serializer_class = GatewayTechnicalLogListSerializer
Моя проблема в том, что когда вызывается метод is_valid, он пытается проверить шлюз внешнего ключа для каждого объекта и таким образом получает связанный внешний ключ.
Я пытаюсь удалить валидацию этого поля и проверить его самостоятельно, но это ничего не меняет...
Я не нашел ни одного примера этого, есть идеи?
Спасибо!
Ок, так что я закончил тем, что сделал, я не уверен, что это лучший подход, но он, кажется, работает:
class GatewayTechnicalLogListSerializer(serializers.ListSerializer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.gateways_ids: dict = {}
for gw_tech_log in self.initial_data:
self.gateways_ids[gw_tech_log['gateway']] = True
self.gateways_o = Gateway.objects.filter(
id__in=self.gateways_ids.keys()
)
self.gateways_ids = list(self.gateways_o.values_list('id', flat=True))
def validate(self, attrs):
# Validating because validation was removed
for gw_tech_log in attrs:
if gw_tech_log['gateway'] not in self.gateways_ids:
raise serializers.ValidationError('Gateway does not exists.')
return attrs
def create(self, validated_data):
# Bulk creating and logging after into Azure to improve performance
gw_tech_logs_o: list = []
for item in validated_data:
gateway_id: int = item.pop('gateway')
item['gateway'] = next(
gateway_o for gateway_o in self.gateways_o if gateway_o.id == gateway_id
)
gw_tech_logs_o.append(GatewayTechnicalLog(**item))
res = GatewayTechnicalLog.objects.bulk_create(gw_tech_logs_o)
return res
class GatewayTechnicalLogSerializer(serializers.ModelSerializer):
class Meta:
model = GatewayTechnicalLog
fields = '__all__'
list_serializer_class = GatewayTechnicalLogListSerializer
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Removing validation on serializer for gateway field
self.fields['gateway'] = serializers.IntegerField(required=True)
def to_representation(self, obj):
# Rolling back changes on field for representation
self.fields['gateway'] = serializers.PrimaryKeyRelatedField(required=True, queryset=obj.gateway)
return super(GatewayTechnicalLogSerializer, self).to_representation(obj)