Home > Software engineering >  Passing nested data to Django ModelSerializer
Passing nested data to Django ModelSerializer

Time:05-04

I'm wanting to know how you would pass nested data to a ModelSerializer if the child of the nested data is not a model on its own.

The data that I'm working with looks like this:

{
  'leadId': 12345,
  'updateTime': 1651250096821,
  'changeInfo': {
    'oldstage': 'New Leads',
    'newstage': 'Attempting Contact'
  }
}

From previous experience, I know that if I was only working with the leadId and the updateTime, my serializer would look like this:

class LogSerializer(serializers.ModelSerializer):
    leadId = serializers.IntegerField(source="lead_id")
    updateTime = serializers.IntegerField(source="update_time")

    class Meta:
        model = Log
        fields = ["leadId", "updateTime"]

Which would then make it possible to do this:

data = {
    'leadId': 12345,
    'updateTime': 1651250096821
}
serializer = LogSerializer(data=data)
serializer.is_valid()
serializer.save()

If I'm not wanting to turn changeInfo into its own model, is it possible to map the fields to the nested data? Something that might look like this (but this obviously doesn't work):

class LogSerializer(serializers.ModelSerializer):
    leadId = serializers.IntegerField(source="lead_id")
    updateTime = serializers.IntegerField(source="update_time")
    oldstage = serializers.IntegerField(source="oldstage")
    newstage = serializers.IntegerField(source="newstage")


    class Meta:
        model = Log
        fields = ["leadId", "updateTime", "oldstage", "newstage]

CodePudding user response:

As mentioned in the comments, a SerializerMethodfield is a good way to go:

serializers.py

class LogSerializer(...):

    ...
    changeInfo = serializers.SerializerMethodField()

    def get_changeInfo(self, obj): return {
        "leadId" : obj.lead_id,
        "updateTime": obj.update_time
    }

    class Meta:
        fields = ["changeInfo", ...]
        ...

CodePudding user response:

You can use a custom serializer for your changeInfo field (you don't need to create a model for that):

class ChangeInfoSerializer(serializers.Serializer):
    oldstage = serializers.CharField(max_length=100) # Set max_length to a value that suits your needs
    newstage = serializers.CharField(max_length=100)

    def create(self, validated_data):
        pass

    def update(self, instance, validated_data):
        pass


class LogSerializer(serializers.ModelSerializer):
    leadId = serializers.IntegerField(source="lead_id")
    updateTime = serializers.IntegerField(source="update_time")
    changeInfo = ChangeInfoSerializer(required=False) # Change to required=True if you want this field to be mandatory
    

    class Meta:
        model = Log
        fields = ["leadId", "updateTime", "changeInfo"]

    def create(self, validated_data):
        change_info = validated_data.pop('changeInfo')
        log = Log.objects.create(**validated_data)
        
        # Here you can use change_info['oldstage'] and change_info['newstage'] if 'changeInfo' is sent (otherwise you'll get a KeyError)
        # You should add your custom logic here, dont forget to call log.save() if you make any updates to the log created
        
        return log

    def update(self, instance, validated_data):
        change_info = validated_data.pop('changeInfo')
        
        instance.lead_id = validated_data.get('leadId', instance.lead_id)
        instance.update_time = validated_data.get('updateTime', instance.update_time)
        
        # Here you can use change_info['oldstage'] and change_info['newstage'] if 'changeInfo' is sent (otherwise you'll get a KeyError)
        
        instance.save()
        
        return instance
  • Related