Nested serializers change JSON field to array of strings from array of models

Right now, I'm developing a social media app where each user creates an account and must fill out a form specifying their basic info and I am storing that information using Django models. I have two fields "Personality traits" and "Interests" where each user can have multiple interests and personality traits, to model this I'm using a foreign key relationship. My models.py looks like this:

class ListingAccount(models.Model):
    first_name = models.CharField(max_length=30, null=True)
    last_name = models.CharField(max_length=30, null=True)
    email = models.EmailField(max_length=254, null=True)
    date_of_birth = models.DateField(max_length=8, null=True)
    occupation = models.CharField(max_length=30, null=True)
    age_range = models.CharField(max_length=7, null=True)
    tell_us_about_yourself = models.TextField(null=True)
    created = models.DateTimeField(auto_now_add=True) # unsure if needed

class PersonalTrait(models.Model):
    trait = models.CharField(max_length=200, null = True)
    listing_account = models.ForeignKey(ListingAccount, related_name='personal_traits', on_delete=models.CASCADE, null=True)

class Interest(models.Model):
    interest = models.CharField(max_length=200, null=True)
    listing_account = models.ForeignKey(ListingAccount, related_name='interests', on_delete=models.CASCADE, null=True)

My serializers.py looks like this:

class PersonalTraitsSerializer(serializers.ModelSerializer):
    class Meta:
        model = PersonalTrait
        fields = ['trait']

class InterestsSerializer(serializers.ModelSerializer):
    class Meta:
        model = Interest
        fields = ['interest']

class ListingAccountSerializer(serializers.ModelSerializer):
    personal_traits = PersonalTraitsSerializer(many=True)
    interests = InterestsSerializer(many=True)
    class Meta:
        model = ListingAccount
        fields = ['id', 'first_name', 'last_name', 'email', 'date_of_birth',
                  'occupation', 'age_range', 'tell_us_about_yourself', 'created',
                  'personal_traits', 'interests']
    def create(self, validated_data):
        interests_data = validated_data.pop('interests') 
        personal_traits_data = validated_data.pop('personal_traits')
        listing_account = ListingAccount.objects.create(**validated_data)
        Interest.objects.create(listing_account = listing_account, **interests_data)
        PersonalTrait.objects.create(listing_account = listing_account, **personal_traits_data)
        for interest_data in interests_data:
            Interest.objects.create(listing_account = listing_account, **interest_data)
        for personal_trait_data in personal_traits_data:
            PersonalTrait.objects.create(listing_account = listing_account, **personal_trait_data)
        return listing_account

As of right now once I make a post request the JSON is formatted in this form

    {
        "id": 14,
        "first_name": "WEW",
        "last_name": "WWEEEEEEE",
        "email": "fgWEGEEWGWEG@gmail.com",
        "date_of_birth": null,
        "occupation": "typical enjoyer",
        "age_range": "18-20",
        "tell_us_about_yourself": "k",
        "created": "2023-01-18T22:33:17.455085Z",
        "personal_traits": [
            {
                "trait": "happy"
            },
            {
                "trait": "few"
            }
        ],
        "interests": [
            {
                "interest": "skateboarding"
            }
        ]
    }

I was wondering how I can alter my seralizers.py such that instead of interests and personal_traits being formatted such that its an array of strings formatted such as this instead:

    {
        "id": 14,
        "first_name": "WEW",
        "last_name": "WWEEEEEEE",
        "email": "fgWEGEEWGWEG@gmail.com",
        "date_of_birth": null,
        "occupation": "typical enjoyer",
        "age_range": "18-20",
        "tell_us_about_yourself": "k",
        "created": "2023-01-18T22:33:17.455085Z",
        "personal_traits": ["Happy", "Sad"],
        "interests": ["skateboarding", "Snowboarding"]
    }

Using a nested ModelSerializer as a field (as in interests = InterestSerializer(many=True)) will produce a JSON object, not a string -- because it's designed to serialize the structured data in models.

Since you want your values to be strings, you need a StringRelatedField serializer (https://www.django-rest-framework.org/api-guide/relations/#stringrelatedfield).

You will also need to ensure that a __str__ method is defined on the models you want to serialize as strings, as this is how StringRelatedField serializes those objects.

So you can get rid of your PersonalTraitSerializer and InterestSerializer, and instead use this:

class ListingAccountSerializer(serializers.ModelSerializer):
    personal_traits = serializers.StringRelatedSerializer(many=True)
    interests = serializers.StringRelatedSerializer(many=True)

    class Meta:
        ...

And you'd want to add those __str__ methods to the models:

class PersonalTrait(models.Model):
    trait = models.CharField(max_length=200, null = True)
    listing_account = models.ForeignKey(ListingAccount, related_name='personal_traits', on_delete=models.CASCADE, null=True)

    def __str__(self):
        return f"{self.trait}"


class Interest(models.Model):
    interest = models.CharField(max_length=200, null=True)
    listing_account = models.ForeignKey(ListingAccount, related_name='interests', on_delete=models.CASCADE, null=True)

    def __str__(self):
        return f"{self.interest}"

Two other options:

Back to Top