Creating and saving foreign key objects using a SlugRelatedField

You have to specify a value for phone field of the object Phone. If you want create phone object without specifying value for the field phone then you have to enable null and blank fields.

phone = models.CharField(max_length=255,null=true,blank=true)

If you still experience problems, make sure the post data contains the required fields. You can use ipdb for this.


Building on Kevin's answer, a little cleaner IMO due to not using get_or_create and [0]

class CreatableSlugRelatedField(serializers.SlugRelatedField):
    def to_internal_value(self, data):
        try:
            return self.get_queryset().get(**{self.slug_field: data})
        except ObjectDoesNotExist:
            return self.get_queryset().create(**{self.slug_field: data})  # to create the object
        except (TypeError, ValueError):
            self.fail('invalid')

Note the only required field in the related object should be the slug field.


The SlugRelatedField provided by Django REST framework, like many of the related fields, is designed to be used with objects that already exist. Since you are looking to reference objects which already exist, or object which need to be created, you aren't going to be able to use it as-is.

You will need a custom SlugRelatedField that creates the new object when one doesn't exist.

class CreatableSlugRelatedField(serializers.SlugRelatedField):
    
    def to_internal_value(self, data):
        try:
            return self.get_queryset().get_or_create(**{self.slug_field: data})[0]
        except ObjectDoesNotExist:
            self.fail('does_not_exist', slug_name=self.slug_field, value=smart_text(data))
        except (TypeError, ValueError):
            self.fail('invalid')

class MerchantSerializer(serializers.ModelSerializer):
    phones = CreatableSlugRelatedField(
        many=True,
        slug_field='phone',
        queryset=primitives.Phone.objects.all()
    )

    class Meta:
        model = Merchant
        fields = (
            'merchant_id',
            'name',
            'is_active',
            'phones',
        )

By switching to get_or_create, the phone number object will be created if one doesn't already exist. You may need to tweak this if there are additional fields that have to be created on the model.