django rest framework update method in serializer, instance is not saved immediately

牧云@^-^@ 提交于 2019-12-12 21:32:50

问题


The instance to be updated has instance.email=abc@mail.com.

email to be updated or changed to xyz@mail.com

UserUpdateSerializer's update method.

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

I am using celery to send an email to the user when given a user_id to the method as:

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.

The instance is saved with instance.save(), where email is updated from abc@mail.com to xyz@mail.comand then the instance's id is passed as a parameter to a shared_task method to send a mail. But thought the email appears updated finally. The User instance obtained from the user_id inside the send_activation_mail(user_id): appears to have not updated and the mail is sent to the previous email.


回答1:


Is validated_data the same as self.validated_data?

Because i see that you use them exchangeable




回答2:


The instance.save() hasn't committed to the database yet. And before that the celery task send_activation_mail.delay(instance.id) has been called resulting in the getting the previous instance than the required updated instance.

So to overcome this we should be using @transaction.atomic and transaction.on_commit i.e,

from django.db import transaction

@transaction.atomic
def myFunction():
    user = User.objects.get(pk=1).update(email="xyz@email.com")
    transaction.on_commit(lambda: my_task.delay(user.pk))

@transaction.atomic decorator will commit the transaction when the view returns, or roll back if the view raises an exception.

transaction.on_commit is a callback to launch a task once all transactions have been committed successfully.

on_commit is available in Django 1.9 and above



来源:https://stackoverflow.com/questions/52791225/django-rest-framework-update-method-in-serializer-instance-is-not-saved-immedia

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!