Django Rest Framework - create objects passed as a list attribute of nested object
django, django-rest-framework
Solution
DRF currently (version 2.3.13) has no built-in functionality to create nested relations. However, it is quite straight-forward to accomplish this by overriding `create` in a `ListCreateView`:
class ComponentList(generics.ListCreateAPIView):
model = Component
serializer_class = ComponentSerializer
def create(self, request, *args, **kwargs):
data = request.DATA
# note transaction.atomic was introduced in Django 1.6
with transaction.atomic():
component = Component(
profit_and_loss=data['component_comments'],
name=data['name']
)
component.clean()
component.save()
for volume in data['volumes']:
ComponentProductionVolume.objects.create(
component=component,
offset=volume['offset'],
volume=volume['volume']
)
serializer = ComponentSerializer(component)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED,
headers=headers)
Note
The above code uses `transaction.atomic` which was introduced in Django 1.6. It comes in handy in this case as it rolls back changes if something goes awry. See the Django documentation about transactions for more info:
https://docs.djangoproject.com/en/dev/topics/db/transactions/
Also, this example creates one `Component` instance, but creating several can be accomplished by either modifying the client to send one Component POST request at a time or modifying the above code.
Hope this helps!
Problem
Xzibit jokes aside, here's my model: ``` from django.db import models class ProjectProfitAndLoss(models.Model): pass class Component(models.Model): profit_and_loss = models.ForeignKey(ProjectProfitAndLoss, related_name='components') name = models.CharField(max_length=250) class ComponentProductionVolume(models.Model): component = models.ForeignKey(Component, related_name='volumes') offset = models.IntegerField() volume = models.DecimalField(max_digits=16, decimal_places=4) ``` Serializers: ``` from rest_framework import serializers class ComponentProductionVolumeSerializer(serializers.ModelSerializer): class Meta: model = ComponentProductionVolume class ComponentSerializer(serializers.ModelSerializer): volumes = ComponentProductionVolumeSerializer(many=True, allow_add_remove=True) class Meta: model = Component class ProjectProfitAndLossSerializer(serializers.ModelSerializer): components = ComponentSerializer(many=True, allow_add_remove=True) class Meta: model = ProjectProfitAndLoss ``` What I'm trying to do is post Components to be created as a list along with their ComponentProductionVolumes - also as lists. So my json looks something like this: ``` [ { "name": "component 1", "profit_and_loss": 3, "volumes": [ { "offset": 0, "volume": 2 }, { "offset": 1, "volume": 3 }, { "offset": 2, "volume": 2 }, ] }, { "name": "component 2" "profit_and_loss": 3, "volumes": [ { "offset": 0, "volume": 4 }, { "offset": 1, "volume": 2 }, { "offset": 2, "volume": 5 }, ] } ] ``` Unfortunately, what I'm getting back is a validation error: ``` components: [{volumes:[{component:[This field is required.]},{volumes:[{component:[This field is required.]} ... /* error repeated for each volume sent */ ]}] ``` If I understand correctly, this errors tell me to include component id in each volume I send. But since I want DRF to create Components along with their volumes, this is not possible, because Components don't exist yet. What would be the best approach to make DRF create components, and then ComponentProductionVolumes?