BenwJ
BenwJ

Reputation: 11

Cannot assign "<User: user.name>: must be a "User" instance

I am having an issue writing a custom django migration where I am trying to set a field value for a model to a user. The model in question is shown below (CustomerMachine). This model uses the django-simple-history module to track changes in model instances. I am attempting to query the instance history in the migration and set the review_submitter value to the last user which edited the instance. The result of the history query history_user returns a <class 'django.contrib.auth.models.User'> type, but when I try to set the review_submitter to that value I get the following error: ValueError: Cannot assign "<User: user.name>": "CustomerMachine.review_submitter" must be a "User" instance. Any insight into whats going on here?

simplified class example

class CustomerMachine(models.Model):
    review_submitter = models.ForeignKey(settings.AUTH_USER_MODEL, default=None)
    history = HistoricalRecords()

custom migration

from __future__ import unicode_literals

from __future__ import absolute_import
from django.conf import settings
from django.db import migrations, models
from django.contrib.auth.models import User

from acceptance.models import CustomerMachine

def set_submitter(apps, schema_editor):
    machines = apps.get_model('acceptance', 'CustomerMachine')
    for machine in machines.objects.all():
        history = CustomerMachine.history.filter(serial=machine.serial)
        if history:
            history_user = history.last().history_user
            machine.review_submitter = history_user
            machine.save()

def reverse_func(apps, schema_editor):
        pass  # code for reverting migration, if any

class Migration(migrations.Migration):

    dependencies = [
        ('acceptance', '0031_auto_20200914_1611'),
    ]

    operations = [
        migrations.RunPython(set_submitter, reverse_func),
    ]

migration 0031_auto_20200914_1611

from __future__ import unicode_literals

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
        ('acceptance', '0030_auto_20191218_1927'),
    ]

    operations = [
        migrations.AddField(
            model_name='customermachine',
            name='review_submitter',
            field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
        ),
        migrations.AddField(
            model_name='historicalcustomermachine',
            name='review_submitter',
            field=models.ForeignKey(blank=True, db_constraint=False, default=None, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL),
        ),
    ]

Upvotes: 0

Views: 979

Answers (1)

Dmitry Wojciechowski
Dmitry Wojciechowski

Reputation: 590

The problem is that you are trying to import model into the migration directly:

from acceptance.models import CustomerMachine

In migrations you can only get models like that:

machines = apps.get_model('acceptance', 'CustomerMachine')

This model can be used later:

history = machines.history.filter(serial=machine.serial)

instead of:

history = CustomerMachine.history.filter(serial=machine.serial)

The thing is that in migrations you don't have access to classic models with their state and when you do machines = apps.get_model('acceptance', 'CustomerMachine') you are getting <class '__fake__.CustomerMachine'> which is not your CustomerMachine (you can check with pdb in you migration). And when you are trying mixing you standard models with the "fake" equivalents you have the errors like you described.

Upvotes: 0

Related Questions