【问题标题】:Creating and saving foreign key objects using a SlugRelatedField使用 SlugRelatedField 创建和保存外键对象
【发布时间】:2015-01-18 12:47:53
【问题描述】:

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

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."]}

【问题讨论】:

    标签: django django-rest-framework


    【解决方案1】:

    The SlugRelatedField 由 Django REST 框架提供,与许多相关字段一样,旨在与已存在的对象一起使用。由于您要引用已存在的对象或需要创建的对象,因此您将无法按原样使用它。

    您将需要一个自定义的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 = CreatableSlugRelatedField(
            many=True,
            slug_field='phone',
            queryset=primitives.Phone.objects.all()
        )
    
        class Meta:
            model = Merchant
            fields = (
                'merchant_id',
                'name',
                'is_active',
                'phones',
            )
    

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

    【讨论】:

    • 谢谢!它成功了。现在看起来很明显,它期望一个 SlugRelatedField 已经创建。
    • 这是一个很好的解决方案。如果有人希望进一步定制它,请通过此链接:django-rest-framework.org/api-guide/fields 例如,我发现添加 required=True 和 allow_null=True,使它成为适合我的解决方案。
    • 您能否在示例中添加必要的导入语句?特别是smart_textObjectDoesNotExist
    • 我也在努力编写一个涵盖这两个异常的测试。鉴于您使用的是get_or_create,如何提高ObjectDoesNotExist
    • 使用from django.core.exceptions import ObjectDoesNotExist导入ObjectDoesNotExist
    【解决方案2】:

    以 Kevin 的回答为基础,由于不使用 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')
    

    请注意,相关对象中唯一的必填字段应该是 slug 字段。

    【讨论】:

      【解决方案3】:

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

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

      如果您仍然遇到问题,请确保发布数据包含必填字段。您可以为此使用ipdb

      【讨论】:

      • 嘿。我已经添加了请求和响应。我正在传递电话号码(据我所知,它应该进入电话模型的“电话”字段)。
      • 看起来您正在views.py 中尝试不正确的object.get。也发表你的看法。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-03-10
      • 2017-08-18
      • 2021-10-30
      • 2021-08-14
      • 1970-01-01
      • 2017-06-03
      相关资源
      最近更新 更多