如何在 ModelSerializer 的 create 方法中实现 update_or_create

Posted

技术标签:

【中文标题】如何在 ModelSerializer 的 create 方法中实现 update_or_create【英文标题】:How to implement update_or_create inside create method of ModelSerializer 【发布时间】:2019-06-07 08:06:09 【问题描述】:

代码:

类 OTP(AppModel): phone_regex = RegexValidator(regex=r'^[6789]\d9$', message="电话号码无效。") phone_number = models.CharField(validators=[phone_regex], max_length=10, unique=True) 代码 = models.CharField(max_length=255) def __str__(self): 返回 str(self.phone_number) + ": "+str(self.code) 类 OTPSerializer(serializers.ModelSerializer): code = serializers.CharField(max_length=None, required=False) 元类: 型号 = OTP 字段 = ('id', 'code', 'phone_number') read_only_fields=('id', 'code') @transaction.atomic 定义创建(自我,验证数据): phone_number = 已验证数据.pop("phone_number") otp, 创建 = OTP.objects.update_or_create( phone_number=phone_number,默认值="code": generate_otp()) 返回 otp

我正在尝试在django-rest-frameworkModelSerializercreate 方法中执行update_or_create

但是,模型OTP 中的字段phone_number 必须是unique。因此unique=True

我能够发布phone_number 并创建对象。但是,再次发布相同的phone_number 会引发错误otp with this phone number already exists,而不是在它已经存在时更新它,因为我已经覆盖了create 方法。请帮忙!

【问题讨论】:

【参考方案1】:

您可以使用Signals 以干净的方式执行此操作。简单地说,您可以将created 变量发送到您定义的receiver,并根据您的对象是否创建来处理它。在 REST 响应的情况下,只需覆盖 Serializer 中的 create 方法以根据您所处的状态返回数据,或者您在 APIView 中使用的 get/post/patch 方法不返回 serializer.data,而是返回你想要的任何东西。 以下是信号接收器的示例:

@receiver(post_save, sender=settings.OTP_MODEL)
def update(sender, instance=None, created=False, **kwargs):
    if created:
        # Do Something
    else:
        # Do Some Other thing

【讨论】:

请小心使用信号,因为它们不是原子的。您的序列化程序可以提交数据,并且信号对数据库所做的任何事情都在它自己的事务中。除非你用 django.db.transaction 中的 @atomic 来装饰视图【参考方案2】:

尝试在您的序列化程序类中而不是在模型类中设置验证器。所以让你的序列化程序类看起来像这样:

class OTPSerializer(serializers.ModelSerializer):
    code = serializers.CharField(max_length=None, required=False)
    phone_regex = RegexValidator(regex=r'^[6789]\d9$', message="phone no. is invalid.")  # add this line
    phone_number = serializers.CharField(validators=[phone_regex])  # and this line
    
    class Meta:
        model = OTP
        fields = ('id', 'code', 'phone_number')
        read_only_fields=('id', 'code')

    @transaction.atomic
    def create(self, validated_data):
        phone_number = validated_data.pop("phone_number")
        otp, created = OTP.objects.update_or_create(
            phone_number=phone_number, defaults="code": generate_otp())
        return otp

【讨论】:

【参考方案3】:

您可以使phone_number 不需要,然后手动进行检查。您会收到错误消息,因为 DRF 在您之前验证了 phone_number。因此,基本上,解决方案可能如下(仅序列化代码):

class OTPSerializer(serializers.ModelSerializer):
    code = serializers.CharField(max_length=None, required=False)
    class Meta:
        model = OTP
        fields = ('id', 'code', 'phone_number')
        read_only_fields=('id', 'code')
        extra_kwargs = 'phone_number': 'required': False

    @transaction.atomic
    def create(self, validated_data):
        phone_number = validated_data.pop("phone_number")
        otp, created = OTP.objects.update_or_create(
        phone_number=phone_number, defaults="code": generate_otp())
        return otp

【讨论】:

没有。 phone_number 是必需的。问题是:UniqueValidator 在到达 create 方法之前进行验证。

以上是关于如何在 ModelSerializer 的 create 方法中实现 update_or_create的主要内容,如果未能解决你的问题,请参考以下文章

如何在 ModelSerializer 的 create 方法中实现 update_or_create

如何保存具有关系的modelSerializer? - django

如何使用 ModelSerializer 显示所有模型字段?

Django Rest 框架,如何在 ModelSerializer 中包含“__all__”字段和相关字段?

在 DRF 3 中的 ModelSerializer 上添加非模型字段

ModelSerializer 在 Django REST 框架中非常慢