I'm doing a HTTP PUT call to update the data of an object with a nested relationship, and I'm met by the following error:
HTTP 400 Bad Request
"AttributeChoice with this slug already exists."
The reason why this is confusing is because I'm doing a HTTP PUT
call and I expect it to treat it as an UPDATE
and not a CREATE
.
My Models look like this:
class Attribute(models.Model):
name = models.CharField(max_length=100)
text_input = models.BooleanField(default=False)
slug = models.SlugField(unique=True)
class AttributeChoice(models.Model):
attribute = models.ForeignKey(Attribute)
value = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
My Serializers look like this:
class AttributeChoiceSerializer(serializers.ModelSerializer):
class Meta:
model = AttributeChoice
fields = '__all__'
extra_kwargs = {'id': {'read_only': False}}
class AttributeSerializer(serializers.ModelSerializer):
attributechoice_set = AttributeChoiceSerializer(many=True)
class Meta:
model = Attribute
fields = ('id', 'name', 'text_input', 'slug', 'attributechoice_set')
def update(self, instance, validated_data):
choice_data = validated_data.pop('attributechoice_set')
for choice in choice_data:
# If id is within the call, then update the object with matching id
if 'id' in choice:
try:
choice_obj = AttributeChoice.objects.get(pk=choice['id'])
choice_obj.value = choice['value']
choice_obj.slug = choice['slug']
choice_obj.attribute = instance
# If ID is not found, then create a new object
except AttributeChoice.DoesNotExist:
choice_obj = AttributeChoice(**choice)
# If no ID within the call, create a new object.
else:
choice_obj = AttributeChoice(**choice)
choice_obj.save()
return instance
Debug:
Even if I remove the update()
function, I still get the same error. I believe the error is reported from when .is_valid()
is called in the ViewSet. So it's not the update()
that causes it.
Also, if I remove attributechoice_set = AttributeChoiceSerializer(many=True)
and just include the attributechoice_set
in the fields = ()
, the error disappears, but I need that line for the rest of the code to work.