Django Rest Framework ManytoMany Feld Serializing
Модели:
class MaterialAttribute(models.Model):
"""
Represents a material attribute.
Attributes:
name (CharField): The name of the material attribute.
"""
class Meta:
verbose_name = "Material Attribute"
verbose_name_plural = "Material Attributes"
db_table = "material_attribute"
name = models.CharField(max_length=255)
def __str__(self):
return f"{self.name}"
class MaterialType(models.Model):
"""
Represents a material type.
Attributes:
name (CharField): The name of the material type.
attributes (ManyToManyField): The attributes of the material type.
"""
class Meta:
verbose_name = "Material Type"
verbose_name_plural = "Material Types"
db_table = "material_type"
name = models.CharField(max_length=255)
attributes = models.ManyToManyField(
MaterialAttribute, related_name="material_types"
)
def __str__(self):
return f"{self.name}"
Я хочу написать MaterialTypeSerializer таким образом, чтобы для list/retrieave он возвращал Json-представление
{
"id": 1,
"name": "Test",
"attributes": [
{
"id": 1,
"name": "Format"
},
{
"id": 2,
"name": "Farbe"
}
]
}
из аттрибутов и для создания/обновления
{
"name": "Test",
"attributes": [
{
1
},
{
2
}
]
}
первичный ключ.
class MaterialAttributeSerializer(serializers.ModelSerializer):
"""
Serializer for the MaterialAttribute model.
This serializer includes the following fields:
- id: The unique identifier of the material attribute.
- name: The name of the material attribute.
All fields are read-only.
It is used to serialize minimal material attribute data.
"""
class Meta:
model = MaterialAttribute
fields = ["id", "name"]
class MaterialAttributeManyField(serializers.PrimaryKeyRelatedField):
def to_representation(self, value):
return MaterialAttributeSerializer(value).data
class MaterialTypeSerializer(serializers.ModelSerializer):
"""
Serializer for the MaterialType model.
This serializer includes the following fields:
- id: The unique identifier of the material type.
- name: The name of the material type.
- attributes: The attributes associated with the material type.
All fields are read-only.
It is used to serialize minimal material type data.
"""
attributes = MaterialAttributeManyField(
queryset=MaterialAttribute.objects.all(), many=True
)
class Meta:
model = MaterialType
fields = ["id", "name", "attributes"]
Однако это приводит к "нехешируемому типу: 'ReturnDict'"
Я стараюсь не переопределять методы создания и обновления сериализатора, чтобы сохранить простоту кода.
Если кто-то знает чистый способ решить эту проблему, я буду благодарен за это
Чтобы правильно сериализовать ManyToManyField в Django REST Framework (DRF) для операций list/retrieve и create/update, необходимо настроить свои сериализаторы для корректной обработки отношений.
serializers.py
from rest_framework import serializers
from .models import MaterialAttribute, MaterialType
class MaterialAttributeSerializer(serializers.ModelSerializer):
class Meta:
model = MaterialAttribute
fields = ["id", "name"]
class MaterialTypeSerializer(serializers.ModelSerializer):
attributes = serializers.PrimaryKeyRelatedField(queryset=MaterialAttribute.objects.all(), many=True)
class Meta:
model = MaterialType
fields = ["id", "name", "attributes"]
def to_representation(self, instance):
representation = super().to_representation(instance)
representation['attributes'] = MaterialAttributeSerializer(instance.attributes.all(), many=True).data
return representation
def create(self, validated_data):
attributes_data = validated_data.pop('attributes', [])
material_type = MaterialType.objects.create(**validated_data)
for attr_id in attributes_data:
attribute = MaterialAttribute.objects.get(id=attr_id)
material_type.attributes.add(attribute)
return material_type
def update(self, instance, validated_data):
attributes_data = validated_data.pop('attributes', [])
instance.name = validated_data.get('name', instance.name)
instance.attributes.clear()
for attr_id in attributes_data:
attribute = MaterialAttribute.objects.get(id=attr_id)
instance.attributes.add(attribute)
instance.save()
return instance
Объяснение
MaterialAttributeSerializer:
Этот сериализатор прост, он сериализует экземпляры MaterialAttribute в формат JSON с полями id и name.
MaterialTypeSerializer:
Для списков/получения он переопределяет to_representation, чтобы обеспечить пользовательскую сериализацию, в которой атрибуты представляются в виде списка словарей ({"id": 1, "name": "Format"}). Для создания/обновления он переопределяет методы create и update для обработки отношения ManyToManyField. При создании или обновлении MaterialType вы можете передавать атрибуты в виде списка идентификаторов MaterialAttribute.
Надеюсь, это поможет!
from rest_framework import serializers
from .models import Article, Tag
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ("id", "name")
class ArticleSerializer(serializers.ModelSerializer):
tags = serializers.PrimaryKeyRelatedField(many=True, queryset=Tag.objects.all())
class Meta:
model = Article
fields = ("id", "title", "tags")
def to_representation(self, instance):
representation = super().to_representation(instance)
representation["tags"] = TagSerializer(instance.tags.all(), many=True).data
return representation
Я думаю, что этот пример работает фактически так же.