Renaming auth_user breaks migrations on fresh setup

泄露秘密 提交于 2019-12-05 05:39:57

Based on the issues you have mentioned having, the approach I would first try is to modify the migration that performs the table rename to check whether the rename should be performed. Unfortunately South does not readily cooperate with this kind of check. Most higher-level operations completely abort a migration if they fail. However, you can use db.execute and it will raise an exception if it fails. Something like:

from django.db.utils import ProgrammingError
from south.db import db

exists = False
db.start_transaction()
try:
    # Will fail if the destination table does not exist. 
    # Any typo here will yield incorrect results. Be careful.
    db.execute("select count(*) from auth_user")
    # If we get here, the table exists
    exists = True
except ProgrammingError:
    pass

# Always end the transaction we started, rollback or commit shouldn't matter.
db.rollback_transaction()

if exists:
    db.rename_table...
else:
    # The table does not exist, create new one.
    db.create_table...

My tests show that it is always possible to catch the errors raised by South's database calls. However, South does not clean up after an SQL error. (This is what I initially missed in the first version of this answer.) So even if the exception is caught, the next SQL operation that will start will find that there the connection is in an error state. In other words, the operation that occurs after the operation that failed will fail because the previous operation fails. This is the reason for the db.start_transaction() and db.rollback_transaction() calls. This makes the operation end cleanly even if there was an SQL error.

I also had problems trying to follow the same instructions like you did, but I chose to fix it another way. I created my model (called UserProfile) like this:

class UserProfile(AbstractUser):
    # Fields
    ...
    class Meta:
        swappable = 'AUTH_USER_MODEL'
        db_table = 'auth_user'

This way, running syncdb will no longer cause problems, because your table is named properly. However, I don't remember exactly all the steps I took when I did this, so it might need a bit more setup.

With the ideas from the other answers presented here, this is a solution that works:

def forwards(self, orm):
    if 'auth_user' not in db.execute('SHOW TABLES'):
        db.create_table('app_user', (
            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
            ('password', self.gf('django.db.models.fields.CharField')(max_length=128)),
            ('last_login', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
            ('is_superuser', self.gf('django.db.models.fields.BooleanField')(default=False)),
            ('username', self.gf('django.db.models.fields.CharField')(unique=True, max_length=30)),
            ('first_name', self.gf('django.db.models.fields.CharField')(max_length=30, blank=True)),
            ('last_name', self.gf('django.db.models.fields.CharField')(max_length=30, blank=True)),
            ('email', self.gf('django.db.models.fields.EmailField')(max_length=75, blank=True)),
            ('is_staff', self.gf('django.db.models.fields.BooleanField')(default=False)),
            ('is_active', self.gf('django.db.models.fields.BooleanField')(default=True)),
            ('date_joined', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
        ))
        db.send_create_signal(app', ['User'])
    else:
        db.rename_table('auth_user', 'app_user')
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!