djangomigrationdjango-migrationsdata-migrationdjango-custom-user

How to create *working* data migration using relation to custom User in Django?


I've just started a new project and I'm using custom User model (from django-authtools). Everything works perfect until I got tired of constantly creating example data after wiping the db and decided to add a data migration for development purposes. Mind you, I specifically don't want to use fixtures for this.

I have a ContactPerson model that references settings.AUTH_USER_MODEL and I'm using apps.get_model() calls in the migration. Everything by the books (or so it seems):

# clients/models.py
...

class Client(models.Model):
    name = models.CharField(max_length=128)

class ContactPerson(models.Model):
    client = models.ForeignKey(Client, on_delete=models.CASCADE)
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="as_contact_person",
    )

# migration
from django.contrib.auth import get_user_model
from django.db import migrations

def create_example_data(apps, schema_editor):
    Client = apps.get_model("clients", "Client")
    ContactPerson = apps.get_model("clients", "ContactPerson")
    User = get_user_model()

    jim_user = User.objects.create_user(
        email="j.halpert@dundermifflin.com", password="*", name="Jim Halpert"
    )
    dunder_mifflin = Client.objects.create(
        name="Dunder Mifflin, Inc.",
    )
    ContactPerson.objects.create(
        client=dunder_mifflin,
        user=jim_user,
    )

class Migration(migrations.Migration):
    dependencies = [
        ("clients", "0001_initial"),
    ]

    operations = [
        migrations.RunPython(create_example_data)
    ]

But trying to run the migration I get this error:

ValueError: Cannot assign "<User: Jim Halpert j.halpert@dundermifflin.com>": "ContactPerson.user" must be a "User" instance.

Other migrations are fresh and the built-in User model had never been used. I've also checked that the User model in the migration is of the proper class.

This is driving me crazy as I've run out of ideas how to debug/fix this and go on with the project. Using the exact same code in console works OK. What can I do to finally fix this and have my data migration?


Solution

  • After some more experiments I finally fixed this.

    Turns out you cannot create User and then reference it in the same migration AND you have to use different methods of getting the User model (!)

    What I did was to split the data migration into two parts: first create the user in one function and then create the ContactPerson in another:

    # migration
    
    from django.conf import settings
    from django.contrib.auth import get_user_model
    from django.db import migrations
    
    
    def create_users(apps, schema_editor):
        User = get_user_model()  # IMPORTANT!
    
        User.objects.create_user(
            email="j.halpert@dundermifflin.com", password="*", name="Jim Halpert"
        )
    
    def create_contacts(apps, schema_editor):
        Client = apps.get_model("clients", "Client")
        ContactPerson = apps.get_model("clients", "ContactPerson")
        User = apps.get_model(settings.AUTH_USER_MODEL)  # IMPORTANT!
    
        jim_user = User.objects.get(email="j.halpert@dundermifflin.com")
        dunder_mifflin = Client.objects.create(
            name="Dunder Mifflin, Inc.",
        )
        ContactPerson.objects.create(
            client=dunder_mifflin,
            user=jim_user,
        )
    
    class Migration(migrations.Migration):
        dependencies = [
            ("clients", "0001_initial"),
        ]
    
        operations = [
            migrations.RunPython(create_users),
            migrations.RunPython(create_contacts),
        ]
    
    

    ...and this works like charm.

    Posting questions on Stack really does help :D