序列化程序中的django rest框架更新方法,实例不会立即保存

Posted

技术标签:

【中文标题】序列化程序中的django rest框架更新方法,实例不会立即保存【英文标题】:django rest framework update method in serializer, instance is not saved immediately 【发布时间】:2019-03-18 09:00:55 【问题描述】:

要更新的实例有instance.email=abc@mail.com

要更新或更改为xyz@mail.com的电子邮件

UserUpdateSerializer的更新方法。

def update(self, instance, validated_data):
        email_updated=False
        email = self.validated_data["email"]
        print(instance.email) #abc@email.com
        if email!=instance.email:
            if User.objects.filter(email=email).exists():
                raise serializers.ValidationError("email is not available")
            else:
                email_updated=True
        instance.__dict__.update(**validated_data)
        instance.save() # instance is saved.
        print(instance.email) #xyz@email.com
        if email_updated:
            task_send_activation_mail.delay(instance.id)#this one here
        print(instance.email) #xyz@email.com
        return instance

当给方法一个 user_id 时,我正在使用 celery 向用户发送电子邮件:

from `celery` import shared_task

@shared_task
def send_activation_mail(user_id):
    from project.models import User
    user = User.objects.get(pk=user_id)
    subject = 'Activate Your '+DOMAIN_SHORT_NAME+' Account'
    message = get_template('registration/account_activation_email.html').render(
        'domain_url': DOMAIN_URL,
        'domain': DOMAIN,
        'domain_short_name': DOMAIN_SHORT_NAME,
        'domain_full_name': DOMAIN_FULL_NAME,
        'domain_email': DOMAIN_EMAIL,
        'domain_support_email': DOMAIN_SUPPORT_EMAIL,
        'domain_support_url': DOMAIN_SUPPORT_URL,
        'mobile_support': MOBILE_SUPPORT,
        'user': user,
        'uid': urlsafe_base64_encode(force_bytes(user.pk)).decode(),
        'token': account_activation_token.make_token(user),
    )
    user.email_user(subject, DOMAIN_FULL_NAME +' ', html_message=message)
    return user.email #"abc@email.com" is printed as celery output.

实例用instance.save()保存,其中emailabc@mail.com更新为xyz@mail.com,然后实例的id作为参数传递给shared_task方法以发送邮件。但认为电子邮件似乎终于更新了。从send_activation_mail(user_id):内部的user_id获取的User实例似乎没有更新,邮件发送到之前的email

【问题讨论】:

每次在打印电子邮件值之前使用instance.refresh_from_db(),因为目前您没有检查电子邮件是否保存到数据库,您只需在模型实例上打印缓存的电子邮件 @KamilNiski 是正确的。即使您可以将 instance.email 传递给 send_activation_mail() 方法,而不是查询 shared_task @KamilNiski 是的。该实例似乎尚未保存到数据库。调用保存后如何强制它立即保存?在整个update 方法执行之后,实例已经被保存。但不是instance.save(0 @Shameless 看起来您的问题不在您提供的代码中。我怀疑instance.__dict__.update(**validated_data) 会导致错误。现在我建议粘贴settings.py 并使用ipdb 和手动调试。可能我们没有足够的信息来解决这个问题。 【参考方案1】:

instance.save() 尚未提交到数据库。在此之前,已经调用了 celery 任务 send_activation_mail.delay(instance.id),导致获取的实例不是所需的更新实例。

所以为了克服这个问题,我们应该使用@transaction.atomictransaction.on_commit

从 django.db 导入事务 @transaction.atomic 定义我的函数(): 用户 = User.objects.get(pk=1).update(email="xyz@email.com") transaction.on_commit(lambda: my_task.delay(user.pk))

<b>@transaction.atomic</b> 装饰器将在视图返回时提交事务,或者在视图引发异常时回滚。

<b>transaction.on_commit</b> 是在所有事务都成功提交后启动任务的回调。

<b>@987654321@</b> 在 Django 1.9 及更高版本中可用

【讨论】:

【参考方案2】:

validated_dataself.validated_data一样吗?

因为我看到你使用它们是可交换的

【讨论】:

以上是关于序列化程序中的django rest框架更新方法,实例不会立即保存的主要内容,如果未能解决你的问题,请参考以下文章

Django Rest Framework,如何更新序列化程序中的嵌套值

Django rest 框架序列化程序返回一个列表而不是 json

Django rest框架嵌套序列化程序创建方法

嵌套序列化程序 django rest 框架中的上下文

django rest框架在序列化程序创建方法中获取请求

从 django rest 框架中的序列化程序发送自定义错误响应?