将数据从原始用户模型迁移到 Django 1.8 中的自定义用户模型

Migrate data from original User model to Custom User model in django 1.8

我创建了一个自定义用户模型。 auth 数据库中已经有用户。所以,我使用数据迁移将数据迁移到我的自定义用户模型。

这是我在自动迁移文件(我从here中找到的)中进行数据迁移的方式:

已更新

from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

    dependencies = [
        ('userauth', '0002_auto_20150721_0605'),
    ]

    operations = [
        migrations.RunSQL('INSERT INTO userauth_userauth SELECT * FROM auth_user'),
        migrations.RunSQL('INSERT INTO userauth_userauth_groups SELECT * FROM auth_user_groups'),
        migrations.RunSQL('INSERT INTO userauth_userauth_user_permissions SELECT * FROM auth_user_user_permissions'),
    ]

models.py

class UserManager(BaseUserManager):
        def _create_user(self, username, email, password, is_staff, is_superuser, **extra_fields):
            now = timezone.now()
            if not username:
              raise ValueError(_('The given username must be set'))
            email = self.normalize_email(email)
            user = self.model(username=username, email=email,
                     is_staff=is_staff, is_active=False,
                     is_superuser=is_superuser, last_login=now,
                     date_joined=now, **extra_fields)
            user.set_password(password)
            user.save(using=self._db)
            if not is_staff:
                group = Group.objects.get(name='normal')
                user.groups.add(group)
            return user

        def create_user(self, username, email=None, password=None, **extra_fields):
            return self._create_user(username, email, password, False, False,
                     **extra_fields)

        def create_superuser(self, username, email, password, **extra_fields):
            user=self._create_user(username, email, password, True, True,
                         **extra_fields)
            user.is_active=True
            user.save(using=self._db)
            return user


    class UserAuth(AbstractBaseUser, PermissionsMixin):
        #original fields
        username = models.CharField(_('username'), max_length=30, unique=True,
        help_text=_('Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters'),
        validators=[
          validators.RegexValidator(re.compile('^[\w.@+-]+$'), _('Enter a valid username.'), _('invalid'))
        ])
        first_name = models.CharField(_('first name'), max_length=30, blank=True, null=True)
        last_name = models.CharField(_('last name'), max_length=30, blank=True, null=True)
        email = models.EmailField(_('email address'), max_length=255, unique=True)
        is_staff = models.BooleanField(_('staff status'), default=False,
        help_text=_('Designates whether the user can log into this admin site.'))
        is_active = models.BooleanField(_('active'), default=False,
        help_text=_('Designates whether this user should be treated as active. Unselect this instead of deleting accounts.'))
        date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
      #additional fields
        full_name =  models.CharField(_('Full Name'), max_length=600, blank=True, null=True)
        profileImage = models.ImageField(upload_to="upload",blank=True,null=True,
        help_text = _("Please upload your picture"))
        BioUser =  models.TextField(blank=True,null=True)
        Social_link = models.URLField(blank=True,null=True)

        objects = UserManager()
        USERNAME_FIELD = 'username'
        REQUIRED_FIELDS = []

        class Meta:
            verbose_name = _('user')
            verbose_name_plural = _('users')

        def get_full_name(self):
            return self.full_name

        def get_short_name(self):
            return self.first_name

        def email_user(self, subject, message, from_email=None):
            send_mail(subject, message, from_email, [self.email])

迁移后的问题,我无法创建新用户。我收到此错误:

duplicate key value violates unique constraint "userauth_userauth_pkey" DETAIL: Key (id)=(3) already exists.

似乎 table 不同步。我该如何解决这个问题?

您使用的是什么数据库?听起来您的数据库有一个主键计数器,它生成的 ID 类似于 3。由于您使用主键创建了新行,因此您可能需要手动重置数据库计数器。有关 postgres 中的示例,请参阅 How to reset postgres' primary key sequence when it falls out of sync?

在 Postgres 上,Django 处理为其数据库记录创建唯一主键的方式是使用 database sequence 从中获取新的主键。查看我自己的数据库,我发现如果我有一个名为 x 的 table,那么 Django 会在名称 x 下创建序列_id_seq。所以你的 userauth_userauth table 的顺序是 userauth_userauth_id_seq.

现在,您执行迁移的方式是使用原始 SQL 语句。这完全绕过了 Django 的 ORM,这意味着新 table 的序列没有被迁移所触及。在执行这样的原始迁移之后,您应该做的是将主键序列设置为一个不会与数据库中已有内容冲突的数字。从this answer借用,那么你应该发出:

select setval('userauth_userauth_id_seq', max(id)) 
       from userauth_userauth;

并对其他 table 执行相同类型的操作:如果他们的 id 字段,则将他们自己的序列设置为最大值。 (如果你想知道,下一个将使用的值将通过 nextval 获得,并且会比调用 nextval 之前的序列值多 1。)

在评论中,您想知道为什么创建新用户最终会奏效。可能发生的事情是您正在尝试创建新用户并且它是这样的:

  1. Django 从适当的序列中获得了一个新的主键。这里序列递增。

  2. Django 试图保存新用户,但失败了,因为它在上一步中获取的数字不是唯一的。

如果你这样做的次数足够多,你的序列将在每次尝试时递增,因为不管事务,Postgres 不会回滚序列。 documentation 表示:

Important: Because sequences are non-transactional, changes made by setval are not undone if the transaction rolls back.

所以最终,序列会增加超过 table 中已有的最大主键,并且从那一点开始它会起作用。