How to auto create foreingKey object (python with django)
Question:
I’m creating a simple rest api
This is my actual result from devices, i create first the group and options, and then associate, but i want to create the options at the same time i create the device.
My result from devices right now
[
{
"id_device": "a099d2ce-812b-4d85-8c8b-cfe71057fbe7",
"group": {
"id_group": "39407ef6-1a3e-4965-9b61-96f6c40b76b3",
"name": "Cozinha"
},
"options": [
{
"id_option": "1b383a37-5229-4ae0-9649-f76aa32eeda0",
"name": "1",
"identifier": "POWER1"
},
{
"id_option": "4ff5406a-8c7b-4517-9ec0-14fd4f68f30b",
"name": "2",
"identifier": "POWER2"
},
{
"id_option": "a541216d-f509-4461-85ca-444491ac9217",
"name": "3",
"identifier": "POWER3"
},
{
"id_option": "3debe828-edd6-4d83-bfd8-2776a1594380",
"name": "4",
"identifier": "POWER4"
}
],
"name": "Interruptor 4",
"identifier": "identifiertest"
},
]
my models.py
from uuid import uuid4
from django.db import models
class Options(models.Model):
id_option = models.UUIDField(primary_key=True, default=uuid4, editable=False)
name = models.CharField(max_length=255)
identifier = models.CharField(max_length=255)
class Meta:
ordering = ['name']
def __str__(self):
return self.name
class Groups(models.Model):
id_group = models.UUIDField(primary_key=True, default=uuid4, editable=False)
name = models.CharField(max_length=255)
class Meta:
ordering = ['name']
def __str__(self):
return self.name
class Devices(models.Model):
id_device = models.UUIDField(primary_key=True, default=uuid4, editable=False)
name = models.CharField(max_length=255)
identifier = models.CharField(max_length=255)
options = models.ManyToManyField(Options)
group = models.ForeignKey(Groups, on_delete=models.CASCADE, default="")
viewsets
from rest_framework import viewsets
from devices.api import serializers
from devices import models
class DevicesViewsets(viewsets.ModelViewSet):
serializer_class = serializers.DevicesSerializer
queryset = models.Devices.objects.all()
class OptionsViewsets(viewsets.ModelViewSet):
serializer_class = serializers.OptionsSerializer
queryset = models.Options.objects.all()
class GroupsViewsets(viewsets.ModelViewSet):
serializer_class = serializers.GroupsSerializer
queryset = models.Groups.objects.all()
serializers
from rest_framework import serializers
from devices import models
class OptionsSerializer(serializers.ModelSerializer):
class Meta:
model = models.Options
fields = '__all__'
class GroupsSerializer(serializers.ModelSerializer):
class Meta:
model = models.Groups
fields = '__all__'
class DevicesSerializer(serializers.ModelSerializer):
group = GroupsSerializer()
options = OptionsSerializer(many=True)
class Meta:
model = models.Devices
fields = '__all__'
I send this to create the device
{
"group": {
"name": "newgroup"
},
"options": [
{
"name": "teste1",
"identifier": "teste1"
}
],
"name": "teste1",
"identifier": "teste1"
}
but get this error:
AssertionError at /devices/
The .create()
method does not support writable nested fields by default.
Write an explicit .create()
method for serializer devices.api.serializers.DevicesSerializer
, or set read_only=True
on nested serializer fields.
Request Method: POST
Request URL: http://127.0.0.1:8000/devices/
Django Version: 4.1.7
Exception Type: AssertionError
Exception Value:
The .create()
method does not support writable nested fields by default.
Write an explicit .create()
method for serializer devices.api.serializers.DevicesSerializer
, or set read_only=True
on nested serializer fields.
I know the response is here, but i dont know how to do it.
I try some solutions from internet but none with success, so can someone help me?
Answers:
You are right, the answer is in there. Basically you have to provide a def create(self, validated_data)
method to your DevicesSerializer
class.
Here is something that could help:
class DevicesSerializer(serializers.ModelSerializer):
group = GroupsSerializer()
options = OptionsSerializer(many=True)
class Meta:
model = models.Devices
fields = '__all__'
def create(self, validated_data):
options_data = validated_data.pop("options")
device = models.Devices.objects.create(**validated_data) # or you can manually pass the fields/data as kwargs
for option_data in options_data:
models.Options.objects.create(device=device, **option_data)
return device
Basically, you need to manually save the base object (Device
) and then manually iterate over the options data and create each of them pointing to the newly created device
.
serializer.py
class DevicesSerializer(serializers.ModelSerializer):
group = GroupsSerializer()
options = OptionsSerializer(many=True)
class Meta:
model = models.Devices
fields = '__all__'
def create(self, validated_data):
group_data = validated_data.pop('group')
options_data = validated_data.pop('options')
group, _ = models.Groups.objects.get_or_create(**group_data)
options = [models.Options.objects.create(**option_data) for option_data in options_data]
device = models.Devices.objects.create(group=group, **validated_data)
device.options.set(options)
return device
viewsets.py:
from rest_framework import viewsets
from devices.api import serializers
from devices import models
class DevicesViewsets(viewsets.ModelViewSet):
serializer_class = serializers.DevicesSerializer
queryset = models.Devices.objects.all()
def perform_create(self, serializer):
serializer.save()
class OptionsViewsets(viewsets.ModelViewSet):
serializer_class = serializers.OptionsSerializer
queryset = models.Options.objects.all()
class GroupsViewsets(viewsets.ModelViewSet):
serializer_class = serializers.GroupsSerializer
queryset = models.Groups.objects.all()
I’m creating a simple rest api
This is my actual result from devices, i create first the group and options, and then associate, but i want to create the options at the same time i create the device.
My result from devices right now
[
{
"id_device": "a099d2ce-812b-4d85-8c8b-cfe71057fbe7",
"group": {
"id_group": "39407ef6-1a3e-4965-9b61-96f6c40b76b3",
"name": "Cozinha"
},
"options": [
{
"id_option": "1b383a37-5229-4ae0-9649-f76aa32eeda0",
"name": "1",
"identifier": "POWER1"
},
{
"id_option": "4ff5406a-8c7b-4517-9ec0-14fd4f68f30b",
"name": "2",
"identifier": "POWER2"
},
{
"id_option": "a541216d-f509-4461-85ca-444491ac9217",
"name": "3",
"identifier": "POWER3"
},
{
"id_option": "3debe828-edd6-4d83-bfd8-2776a1594380",
"name": "4",
"identifier": "POWER4"
}
],
"name": "Interruptor 4",
"identifier": "identifiertest"
},
]
my models.py
from uuid import uuid4
from django.db import models
class Options(models.Model):
id_option = models.UUIDField(primary_key=True, default=uuid4, editable=False)
name = models.CharField(max_length=255)
identifier = models.CharField(max_length=255)
class Meta:
ordering = ['name']
def __str__(self):
return self.name
class Groups(models.Model):
id_group = models.UUIDField(primary_key=True, default=uuid4, editable=False)
name = models.CharField(max_length=255)
class Meta:
ordering = ['name']
def __str__(self):
return self.name
class Devices(models.Model):
id_device = models.UUIDField(primary_key=True, default=uuid4, editable=False)
name = models.CharField(max_length=255)
identifier = models.CharField(max_length=255)
options = models.ManyToManyField(Options)
group = models.ForeignKey(Groups, on_delete=models.CASCADE, default="")
viewsets
from rest_framework import viewsets
from devices.api import serializers
from devices import models
class DevicesViewsets(viewsets.ModelViewSet):
serializer_class = serializers.DevicesSerializer
queryset = models.Devices.objects.all()
class OptionsViewsets(viewsets.ModelViewSet):
serializer_class = serializers.OptionsSerializer
queryset = models.Options.objects.all()
class GroupsViewsets(viewsets.ModelViewSet):
serializer_class = serializers.GroupsSerializer
queryset = models.Groups.objects.all()
serializers
from rest_framework import serializers
from devices import models
class OptionsSerializer(serializers.ModelSerializer):
class Meta:
model = models.Options
fields = '__all__'
class GroupsSerializer(serializers.ModelSerializer):
class Meta:
model = models.Groups
fields = '__all__'
class DevicesSerializer(serializers.ModelSerializer):
group = GroupsSerializer()
options = OptionsSerializer(many=True)
class Meta:
model = models.Devices
fields = '__all__'
I send this to create the device
{
"group": {
"name": "newgroup"
},
"options": [
{
"name": "teste1",
"identifier": "teste1"
}
],
"name": "teste1",
"identifier": "teste1"
}
but get this error:
AssertionError at /devices/
The.create()
method does not support writable nested fields by default.
Write an explicit.create()
method for serializerdevices.api.serializers.DevicesSerializer
, or setread_only=True
on nested serializer fields.
Request Method: POST
Request URL: http://127.0.0.1:8000/devices/
Django Version: 4.1.7
Exception Type: AssertionError
Exception Value:
The.create()
method does not support writable nested fields by default.
Write an explicit.create()
method for serializerdevices.api.serializers.DevicesSerializer
, or setread_only=True
on nested serializer fields.
I know the response is here, but i dont know how to do it.
I try some solutions from internet but none with success, so can someone help me?
You are right, the answer is in there. Basically you have to provide a def create(self, validated_data)
method to your DevicesSerializer
class.
Here is something that could help:
class DevicesSerializer(serializers.ModelSerializer):
group = GroupsSerializer()
options = OptionsSerializer(many=True)
class Meta:
model = models.Devices
fields = '__all__'
def create(self, validated_data):
options_data = validated_data.pop("options")
device = models.Devices.objects.create(**validated_data) # or you can manually pass the fields/data as kwargs
for option_data in options_data:
models.Options.objects.create(device=device, **option_data)
return device
Basically, you need to manually save the base object (Device
) and then manually iterate over the options data and create each of them pointing to the newly created device
.
serializer.py
class DevicesSerializer(serializers.ModelSerializer):
group = GroupsSerializer()
options = OptionsSerializer(many=True)
class Meta:
model = models.Devices
fields = '__all__'
def create(self, validated_data):
group_data = validated_data.pop('group')
options_data = validated_data.pop('options')
group, _ = models.Groups.objects.get_or_create(**group_data)
options = [models.Options.objects.create(**option_data) for option_data in options_data]
device = models.Devices.objects.create(group=group, **validated_data)
device.options.set(options)
return device
viewsets.py:
from rest_framework import viewsets
from devices.api import serializers
from devices import models
class DevicesViewsets(viewsets.ModelViewSet):
serializer_class = serializers.DevicesSerializer
queryset = models.Devices.objects.all()
def perform_create(self, serializer):
serializer.save()
class OptionsViewsets(viewsets.ModelViewSet):
serializer_class = serializers.OptionsSerializer
queryset = models.Options.objects.all()
class GroupsViewsets(viewsets.ModelViewSet):
serializer_class = serializers.GroupsSerializer
queryset = models.Groups.objects.all()