Вложенные сериализаторы изменяют поле JSON на массив строк из массива моделей

На данный момент я разрабатываю приложение для социальных сетей, где каждый пользователь создает аккаунт и должен заполнить форму, указав свою основную информацию, и я храню эту информацию с помощью моделей Django. У меня есть два поля "Personality traits" и "Interests", где каждый пользователь может иметь несколько интересов и черт характера, для моделирования этого я использую отношения внешнего ключа. Мой models.py выглядит следующим образом:

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)

Мой serializers.py выглядит следующим образом:

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

На данный момент, когда я делаю запрос на пост, JSON формируется в таком виде

    {
        "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"
            }
        ]
    }

Мне интересно, как я могу изменить мой файл seralizers.py так, чтобы вместо интересов и personal_traits он был отформатирован так, чтобы его массив строк был отформатирован следующим образом:

    {
        "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"]
    }

Использование вложенного ModelSerializer в качестве поля (как в interests = InterestSerializer(many=True)) приведет к созданию объекта JSON, а не строки - потому что он предназначен для сериализации структурированных данных в моделях.

Поскольку вы хотите, чтобы ваши значения были строками, вам нужен StringRelatedField сериализатор (https://www.django-rest-framework.org/api-guide/relations/#stringrelatedfield).

Вам также необходимо убедиться, что для моделей, которые вы хотите сериализовать как строки, определен метод __str__, поскольку именно так StringRelatedField сериализуются эти объекты.

Так что вы можете избавиться от PersonalTraitSerializer и InterestSerializer, и вместо этого использовать следующее:

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

    class Meta:
        ...

И вы захотите добавить эти __str__ методы к моделям:

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}"

Два других варианта:

  • Используйте поля SerializerMethodField (https://www.django-rest-framework.org/api-guide/fields/#serializermethodfield), и определите желаемое поведение в методах на ListingAccountSerializer. Но это поле только для чтения и не будет работать для создания объекта.
  • Продолжайте использовать ваши пользовательские классы сериализаторов для PersonalTrait и Interest, но переопределите метод to_representation в каждом из них, чтобы вернуть строку вместо диктанта (https://www.django-rest-framework.org/api-guide/serializers/#overriding-serialization-and-deserialization-behavior). Мне это не нравится, потому что это нарушает основные ожидания относительно поведения ModelSerializer, и уже есть сериализатор, который делает то, что вам нужно. Я бы рассмотрел этот вариант, только если вам нужно, чтобы __str__ представление классов вашей модели отличалось от того, что вы хотите получить в сериализаторе.
Вернуться на верх