fougerejo
fougerejo

Reputation: 51

Django get_or_create race condition when creating related objects

I have an ExtendedUser model like this, that just points to Django's User model:

class ExtendedUser(models.Model):
    user = models.OneToOneField(User)

When a request comes in, I want to get or create an User and its related ExtendedUser if the user that I'm looking for doesn't exist. I have this code :

def get_or_create_user(username):
    with transaction.atomic():
        user, created = User.objects.get_or_create(
            username=username,
            defaults={
                'first_name': "BLABLA",
            },
        )

        if created:
             extended_user = ExtendedUser()
             extended_user.user = user
             extended_user.save()

    return user

I wrap it inside a transaction, to be sure I don't create an User but not its associated ExtendedUser.

But, when two requests come in simultaneously that will create the same user, this fails from time to time with an IntegrityError, for both requests. So I end up with no user being created..

IntegrityError: (1062, "Duplicate entry 'BLABLABLA' for key 'username'")

Note that I use MySQL, but I force READ COMMITTED isolation level.

What do I do wrong ? How should I handle this ? The models need to stay as they are.

Upvotes: 1

Views: 1198

Answers (1)

Dalvtor
Dalvtor

Reputation: 3286

Maybe you should solve this problem using signals.

After a User is saved, a signal will be fired. It is in the signal handler where you should create your ExtendedUser.

from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        ExtendedUser.objects.create(user=instance)

Upvotes: 1

Related Questions