django ListSerializer和Foreign Key,is_valid执行N+1查询

mtb9vblg  于 2023-04-22  发布在  Go
关注(0)|答案(2)|浏览(118)

我正在尝试改进我的序列化器,以便能够用最少的查询创建多个对象。因此,我实现了一个ListSerializer,它将批量创建对象,而不是在每个对象上调用保存。
下面是我当前的代码:

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被调用时,它试图验证每个对象的外键网关,因此获取相关的外键。
然后我试图删除该字段上的验证并自己验证它,但它不会改变任何东西...
我没有找到任何例子,任何想法?
谢谢!

rdlzhqv9

rdlzhqv91#

好吧,所以我最终做了,我不确定这是否是最好的方法,但它似乎有效:

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)
fwzugrvs

fwzugrvs2#

我知道这个问题已经存在了很长一段时间,但我有同样的问题,我找了几天的解决方案,最后我找到了另一个解决方案,为我工作。
我把它留在这里,以防它对某些人有帮助,这样它就不再对每个关系进行查询,现在它只是对所有关系的查询,在to_internal_value中它验证外键。

class GatewayTechnicalLogListSerializer(serializers.ModelSerializer):
    ...
    gateway_id = serializers.PrimaryKeyRelatedField(queryset = Gateway.objects.all(), source='gateway', write_only=True)
    ...

    def __init__(self, *args, **kwargs):
        self.gateways_ids = Gateway.objects.all().values_list('id', flat=True)
        super().__init__(*args, **kwargs)

    def to_internal_value(self, data):
        gateway_id = data.pop('gateway_id', None)

        if gateway_id is not None:
            if not gateway_id in self.gateways_ids:
                raise serializers.ValidationError({
                    'gateway_id': 'Gateway does not exist'
                })
        return super().to_internal_value(data)

相关问题