使用SlugRelatedField创建和保存外键对象

时间:2015-01-18 12:47:53

标签: django django-rest-framework

我刚开始使用Django REST框架而且我在保存外键方面遇到了麻烦。我有一个Merchant模型和一个Phone模型。 Phone具有Merchant的外键。向POST发出Merchant请求时,我想为请求中提供的号码创建Phone个对象。但是当我提供电话号码时,它会给我以下错误

  

手机= 0123456789的对象不存在。

我只是想让它自己创建Phone对象。以下是我使用的模型:

class Merchant(models.Model):
    merchant_id       = models.CharField(max_length=255)
    name              = models.CharField(max_length=255)
    is_active         = models.BooleanField(default=True)

    class Meta:
        managed = True
        db_table = 'merchant'

    # Managers
    objects = models.Manager()
    active = managers.ActiveManager()

class Phone(models.Model):
    phone      = models.CharField(max_length=255)
    merchant   = models.ForeignKey('merchant.Merchant',
                                    related_name='phones',
                                    blank=True,
                                    null=True)

    class Meta:
        managed = True
        db_table = 'phone'

以下是我使用

的视图和序列化程序
class MerchantSerializer(serializers.ModelSerializer):
    phones = serializers.SlugRelatedField(
        many=True,
        slug_field='phone',
        queryset=primitives.Phone.objects.all())

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

class MerchantViewSet(viewsets.ModelViewSet):
    queryset = Merchant.active.all()
    serializer_class = MerchantSerializer

以下是我的请求正文:

{
    "merchant_id": "emp011",
    "name": "Abhinav",
    "is_active": true,
    "phones": [
        "0123456789",
        "9876543210"
    ]
}

以下是回复:

400错误请求

{"phones":["Object with phone=0123456789 does not exist."]}

3 个答案:

答案 0 :(得分:28)

与许多相关领域一样,Django REST框架提供的

The SlugRelatedField旨在与已经存在的对象一起使用。由于您希望引用已存在的对象或需要创建的对象,因此您无法按原样使用它。

当一个人不存在时,你需要一个自定义SlugRelatedField来创建新对象。

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 = CreateableSlugRelatedField(
        many=True,
        slug_field='phone',
        queryset=primitives.Phone.objects.all()
    )

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

通过切换到get_or_create,如果尚未存在电话号码对象,则会创建电话号码对象。如果必须在模型上创建其他字段,则可能需要调整此项。

答案 1 :(得分:2)

基于凯文的答案,由于不使用get_or_create[0]

,因此IMO更加简洁

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')

请注意,相关对象中唯一需要的字段应该是“子弹”字段。

答案 2 :(得分:1)

您必须为对象Phone的phone字段指定值。如果要创建电话对象而不指定现场电话的值,则必须启用空字段和空白字段。

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

如果您仍遇到问题,请确保帖子数据包含必填字段。您可以使用ipdb