[Answered ]-Django bulk insert using value from parent table instead of ID

1👍

An approach I can think of is to use django’s in_bulk, to populate a dictionary that uses the asset type name as key and the asset type instance as value:

# This will create {'asset_type_name': asset_type_instance} mapping
asset_types = AssetType.objects.in_bulk(field_name='name')

Then pass it to the serializer as context:

serializer = AssetSerializer(
    data=request.data['data'], many=True, 
    context={'asset_types_map': asset_types},
)
serializer.is_valid(raise_exception=True)
serializer.save()

And then use the map on a custom create, using the asset type name in the data to get the asset type instance like this:

class AssetSerializer(serializers.ModelSerializer):
    asset_type_name = serializers.CharField(source='asset_type.name')
    
    class Meta:
        model = Asset
        fields = ("id", "asset_type_name", "asset_name", "display_name") 

    def create(self, validated_data):
        asset_type_name = validated_data.pop('asset_type_name')
        validated_data['asset_type'] = self.context['asset_types_map'].get(asset_type_name)
        return super().create(validated_data)

Haven’t tested this so might fail here and there, but the gist is here.

0👍


Answering my own question for anyone in the future having the same problem.

Heavily inspired by @bdbd’s answer but didn’t need the dictionary or the context part.

Here is the code which worked:

The viewset was fairly simple. Accept request data and pass to serializer

class AssetViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
    queryset = Asset.objects.select_related()
    serializer_class = AssetSerializer

    @action(methods=['POST'], detail=False)
    def bulk_create(self, request):
        serializer = AssetSerializer(
            data=request.data['data'], many=True)
        serializer.is_valid(raise_exception=True)
        serializer.save()

        return Response(serializer.data, status=status.HTTP_201_CREATED)

In the serializer, query the asset type object from the name and send it as a part of creating asset object.

class AssetSerializer(serializers.ModelSerializer):
    asset_type_name = serializers.CharField(source='asset_type.name')
    
    class Meta:
        model = Asset
        fields = ("id", "asset_type_name", "asset_name", "display_name")    

    def create(self, validated_data):
        asset_type = validated_data.pop('asset_type')
        asset_type = AssetType.objects.get(**asset_type)
        return Asset.objects.create(**validated_data, asset_type=asset_type)

Update

Based on the comment below by @bdbd,
this method works but in_bulk saves the number of queries to the db so that is more preferable to use.
Leaving this answer here just in case.

Leave a comment