Reputation: 1793
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?
Upvotes: 4
Views: 3784
Reputation: 8117
Answer for updating question context
Currently in DRF 3.1, this functionality is supported, you can see full docs here.
Upvotes: 3
Reputation: 10167
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)
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!
Upvotes: 4