Вложенные сериализаторы изменяют поле 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__
представление классов вашей модели отличалось от того, что вы хотите получить в сериализаторе.