13

I'm trying to add a new user and then associate it to a subset of existing records.

For example, imagine I have this app model:

class Foo(Model):
    user = model.ForeignKey(User, default=None, on_delete=models.CASCADE)

In my migration I have:

from django.contrib.auth.models import User    

def add_user(apps, schema_editor):
    anon = User.objects.create_user(username='anonymous')
    anon.save()

    Foo = apps.get_model('myapp', 'Foo')
    foo_to_change = Foo.objects.filter(user_id=1)

    for f in foo_to_change:
        f.user = anon
        f.save()    

When I run this I get this error:

ValueError: Cannot assign "<User: anonymous>": "Foo.user" must be a "User" instance.

I thought the problem might be that I'm using the User model directly and the docs say not to do this. So I changed the top part to:

User = apps.get_model('auth', 'User')
anon = User.objects.create_user(username='anonymous')
anon.save()

Now I get a different error:

AttributeError: type object 'User' has no attribute 'normalize_username'

What am I doing wrong? How do I add a new user in a data migration?

EDIT: I'm using sqlite as DB

Ben
  • 10,931
  • 9
  • 38
  • 47
  • the create user takes 3 required args. username, email, password. What you are doing attaches an instance of anonymous user which is not possible – coderDude Oct 29 '18 at 06:21
  • @coderDude in Django 2,1 create_user has only one required arg: username. The others are definitely optional. The docs specifically say that passing in None for the password will call set_unusable_password() which is what I want. – Ben Oct 29 '18 at 14:06
  • 2
    You should first use https://docs.djangoproject.com/fr/2.1/topics/auth/customizing/#django.contrib.auth.get_user_model instead of `apps.get_model('auth', 'User')` – Antwane Oct 29 '18 at 14:12
  • @antwane Thanks for the link. However, it doesn't work and gives me the ValueError that I see when I try using User directly – Ben Oct 29 '18 at 14:37
  • 1
    Try to do this instead: `def add_user(apps, schema_editor): apps.get_model('myapp', 'Foo').objects.filter(user_id=1).update(user=User.objects.create_user(username='anonymous')) ` – trinchet Nov 01 '18 at 15:58
  • 1
    @trinchet That worked! Please create a real answer so I can accept it. Do you know why this works? – Ben Nov 01 '18 at 16:22

1 Answers1

11

You can do something like:

def add_user(apps, schema_editor): 
    apps.get_model('myapp', 'Foo').objects.filter(user_id=1).update(
        user=apps.get_model('auth', 'User').objects.create_user(username='anonymous')
    )

This works because in this case "no type is checked", the query will use a reference to the entry in the DB instead: the ID of the user.

trinchet
  • 6,753
  • 4
  • 37
  • 60
  • moreover it works because in migrations, you can't directly access the current version of the model in case it is different from the version of the model at the time of the migration. using `apps.get_model()` accesses the correct version of the model – rchurch4 Nov 01 '18 at 18:50
  • appreciate this was a while ago, but could you elaborate a bit on why this works? would really appreciate it – Preston Nov 14 '19 at 16:22
  • 2
    shoud use `create` and not `creater_user` because the latter will fire `UserManager` validation which is not available during migrations. – Adrien H Feb 25 '20 at 12:11