Django rest framework - обработка вложенных полей в JSON
В django REST framework на API приходит такой JSON:
{
"field1": "value1",
"field2": 975.0,
"supefield": [
{
"s_field1": 123.0,
"s_field2": "s_value1",
"megafield": {
"m_fieldID": "00000000-0000-0000-0000-000000000000",
"m_field_name": "MegaName1"
}
},
{
"s_field1": 456.0,
"s_field2": "s_value2",
"megafield": {
"m_fieldID": "11111111-1111-1111-1111-111111111111",
"m_field_name": "MegaName2"
}
}
]
}
Файл models.py:
class Table0(models.Model):
field1 = models.CharField(max_length=10)
field2 = models.DecimalField(max_digits=6, decimal_places=2)
k_tableS = models.ManyToManyField('TableM', through='TableS')
class TableS(models.Model):
s_field1 = models.DecimalField(max_digits=6, decimal_places=2)
s_field2 = models.CharField(max_length=10)
k_megafield = models.ForeignKey('TableM', on_delete=models.CASCADE, related_name='TableM')
k_table0 = models.ForeignKey('Table0', on_delete=models.CASCADE, related_name='Table0')
class TableM(models.Model):
m_fieldID = models.UUIDField()
m_field_name = models.CharField(max_length=10)
Файл serializers.py:
class TableSSerializer(serializers.ModelSerializer):
class Meta:
model = TableS
fields = '__all__'
class TableMSerializer(serializers.ModelSerializer):
class Meta:
model = TableM
fields = '__all__'
class Table0Serializer(serializers.ModelSerializer):
supefield = TableSSerializer()
megafield = TableMSerializer()
class Meta:
model = Table0
fields = '__all__'
Файл urls.py:
my_rout = routers.DefaultRouter()
my_rout.register(r'super', Table0ViewSet)
urlpatterns = [
path('api/v1/', include(my_rout.urls))
]
Файл views.py:
class Table0ViewSet(viewsets.ModelViewSet):
queryset = Table0.objects.all()
serializer_class = Table0Serializer
def create(self, request, *args, **kwargs):
breakpoint()
supefields_data = request.data.pop('supefield')
serializer = Table0Serializer(data=request.data)
serializer.is_valid(raise_exception=True)
table0 = serializer.save()
for s_data in supefields_data:
megafield_data = s_data.pop('megafield')
megafield_serializer = TableMSerializer(data=megafield_data)
megafield_serializer.is_valid(raise_exception=True)
megafield = megafield_serializer.save()
s_data['k_megafield'] = megafield.id
s_d_serializer = TableSSerializer(data=s_data)
s_d_serializer.is_valid(raise_exception=True)
supefield = s_d_serializer.save()
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
Тут .create не работает, потому, что я пытаюсь записать данные в Table0 до того, как запишу связанные данные в TableM и TableS. Вот и думаю, как праильно? Толи можели поменять, толи всё вместе...
Ответы (1 шт):
Воссоздал небольшой пример:
Создаем модель Галактика, и привязываем к ней модель Звезд по manytomany
models
class Planet(models.Model):
name = models.CharField(max_length=100)
class Star(models.Model):
name = models.CharField(max_length=100)
planets = models.ManyToManyField(
Planet, through='StarPlanet', blank=True)
class Galaxie(models.Model):
name = models.CharField(max_length=100)
stars = models.ManyToManyField(
Star, through='GalaxieStar',blank=True,)
class GalaxieStar(models.Model):
star = models.ForeignKey(Star, on_delete=models.CASCADE)
galaxie = models.ForeignKey(Galaxie, on_delete=models.CASCADE)
class StarPlanet(models.Model):
planet = models.ForeignKey(Planet, on_delete=models.CASCADE)
star = models.ForeignKey(Star, on_delete=models.CASCADE)
views
class GalaxieViewSet(viewsets.ModelViewSet):
queryset = Galaxie.objects.all()
serializer_class = GalaxieSerializer
serializers
class PlanetSerializer(serializers.ModelSerializer):
class Meta:
model = Planet
fields = ('id', 'name')
class StarSerializer(serializers.ModelSerializer):
planets = PlanetSerializer(many=True, read_only=True)
class Meta:
model = Star
fields = ('id', 'name', 'planets')
class GalaxieSerializer(serializers.ModelSerializer):
stars = StarSerializer(many=True, read_only=True)
class Meta:
model = Galaxie
fields = ('id', 'name', 'stars')
def validate(self, data):
stars = self.initial_data.pop('stars')
data['stars'] = stars
return data
def create(self, validated_data):
stars = validated_data.pop('stars')
galaxie = Galaxie.objects.create(**validated_data)
stars_ids = []
for star in stars:
planets = star.pop('planets')
star = Star.objects.create(**star)
planets_ids = [Planet.objects.create(**planet).id for planet in planets]
star.planets.set(planets_ids)
stars_ids.append(star.id)
galaxie.stars.set(stars_ids)
return galaxie
В методе validate сериализатора достаем данные о звездах из initial_data и добавляем их в data. Затем в методе create достаем звезды из validated_data и создаем объект галактики без них. В цикле достаем данные о планетах и создаем звезды без них, затем создаем планеты и присваиваем их id - звездам star.planets.set(planets_ids). И в конце присваиваем id звезд - галактике galaxie.stars.set(stars_ids) И на вот такой json:
{
"name": "Andromeda",
"stars": [
{
"name": "Stephenson 2-18",
"planets": [
{
"name": "Mars"
},
{
"name": "Earth"
}
]
},
{
"name": "UY Щита",
"planets": [
{
"name": "Jupiter"
},
{
"name": "Saturn"
}
]
},
{
"name": "NML Лебедя",
"planets": []
}
]
}
получаем:
{
"id": 1,
"name": "Andromeda",
"stars": [
{
"id": 1,
"name": "Stephenson 2-18",
"planets": [
{
"id": 1,
"name": "Mars"
},
{
"id": 2,
"name": "Earth"
}
]
},
{
"id": 2,
"name": "UY Щита",
"planets": [
{
"id": 3,
"name": "Jupiter"
},
{
"id": 4,
"name": "Saturn"
}
]
},
{
"id": 3,
"name": "NML Лебедя",
"planets": []
}
]
}
Надеюсь объяснил понятно =)
UPD Добавил доп. уровень вложенности: модели планет