【问题标题】:New Django South migration breaks an earlier one新的 Django South 迁移打破了之前的迁移
【发布时间】:2013-12-13 20:50:40
【问题描述】:

A.摘要:

在成功运行新迁移(将新列添加到现有表)后,在同一个表 (0010) 上运行的早期迁移失败并显示:

Creating test database for alias 'default'...
Got an error creating the test database: database "test_ymc_platform" already exists

Type 'yes' if you would like to try deleting the test database 'test_ymc_platform', or 'no' to cancel:

当我回复 yes:

Error in migration: ymc:0010_auto__add_field_user_registered_at
DatabaseError: column users.publisher_id does not exist
LINE 1: ...dress", "users"."gender", "users"."developer_id", "users"."publisher_id...
                                                             ^



B.详情:


1。这些迁移是如何运行的?

它们由 Django 的单元测试运行。 As you know, South runs "every migration every time you run your tests."


2。迁移历史

我有(除其他外)三张桌子:usersgamespublishers。直到迁移0025,他们彼此没有任何关系。在迁移0025 中,我添加了一个外键,其中users.publisher_idgames.publisher_id 引用publishers

当我创建并运行 0025 时,我在 0024 并且一切都成功迁移。然后我运行了我的单元测试并得到了上述提示和错误消息。所以0025-->0024 有效,但0009-->0010 失败。

我目前的迁移历史是这样的:

$ ./manage.py migrate ymc --list

 ymc
  (*) 0001_initial
   ...
  (*) 0004_auto__add_field_user_gender
   ...
  (*) 0010_auto__add_field_user_registered_at
   ...
  (*) 0025_auto__add_field_user_publisher__add_field_game_publisher

0004 被包含在内是为了证明 South 可以在 users 表上运行迁移而不会阻塞。也许0010 有什么特别之处。


2。新列的模型代码是什么样的?

我向usersgames 添加了publisher_id 列,如下所示:

# Same for both 'users' and 'games'
publisher = models.ForeignKey('ymc.Publisher', blank=True, null=True, default=None)


3。 South 生成了什么代码?

这是开始失败的现有迁移:

0010_auto__add_field_user_registered_at:

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Adding field 'User.registered_at'
        db.add_column('users', 'registered_at',
                      self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True),
                      keep_default=False)

        users = User.objects.all()
        for user in users:
            user.registered_at = user.joined_at
            user.save()

    def backwards(self, orm):
        # Deleting field 'User.registered_at'
        db.delete_column('users', 'registered_at')


    models = {
        ...
        'ymc.user': {
            'Meta': {'object_name': 'User', 'db_table': "'users'"},
            'access_token': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
            'access_token_expiry': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
            'developer': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['ymc.Developer']", 'null': 'True', 'blank': 'True'}),
            'email': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
            'facebook_access_token': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
            'facebook_id': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
            'forgot_password_token': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
            'forgot_password_token_expiry': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
            'games': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['ymc.Game']", 'null': 'True', 'through': "orm['ymc.UsersGames']", 'blank': 'True'}),
            'gender': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}),
            'is_admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'joined_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
            'last_login': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
            'password': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
            'registered_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
            'salt': ('django.db.models.fields.CharField', [], {'max_length': '128', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
            'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
        },
        ...
    }


这是新的迁移,虽然成功应用,但会导致 0010 阻塞。

0025_auto__add_field_user_publisher__add_field_game_publisher

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Adding field 'User.publisher'
        db.add_column('users', 'publisher',
                      self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['ymc.Publisher'], null=True, blank=True),
                      keep_default=False)

        # Adding field 'Game.publisher'
        db.add_column('games', 'publisher',
                      self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['ymc.Publisher'], null=True, blank=True),
                      keep_default=False)


    def backwards(self, orm):
        # Deleting field 'User.publisher'
        db.delete_column('users', 'publisher_id')

        # Deleting field 'Game.publisher'
        db.delete_column('games', 'publisher_id')


    models = {
        ...
        'ymc.user': {
            'Meta': {'object_name': 'User', 'db_table': "'users'"},
            'access_token': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
            'access_token_expiry': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
            'developer': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['ymc.Developer']", 'null': 'True', 'blank': 'True'}),
            'email': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
            'facebook_access_token': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
            'facebook_id': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
            'forgot_password_token': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
            'forgot_password_token_expiry': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
            'games': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['ymc.Game']", 'null': 'True', 'through': "orm['ymc.UsersGames']", 'blank': 'True'}),
            'gender': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}),
            'is_admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'joined_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
            'last_login': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
            'password': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
            'publisher': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['ymc.Publisher']", 'null': 'True', 'blank': 'True'}),
            'registered_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
            'salt': ('django.db.models.fields.CharField', [], {'max_length': '128', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
            'username': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
            'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
        },

        ...
    }


4。如果您使用--verbose=3 运行测试,输出是什么?

这个:

 ...
 > ymc:0010_auto__add_field_user_registered_at
   = ALTER TABLE "users" ADD COLUMN "registered_at" timestamp with time zone NULL; []
   = ALTER TABLE "users" ALTER COLUMN "registered_at" TYPE timestamp with time zone, ALTER COLUMN "registered_at" DROP NOT NULL, ALTER COLUMN "registered_at" DROP DEFAULT; []
Error in migration: ymc:0010_auto__add_field_user_registered_at
 - Sending post_syncdb signal for ymc: ['Game', 'UsersGames', 'User', 'Developer']
DatabaseError: column users.publisher_id does not exist    
LINE 1: ...dress", "users"."gender", "users"."developer_id", "users"."p...


5。你试过什么?

  • 回滚所有内容并重试(完全相同的结果)。

  • 真希望这是一场梦。

  • 伙计……除此之外,不多。我超级困惑。我什至不 了解此错误消息的生成位置:

     LINE 1: ...dress", "users"."gender", "users"."developer_id", "users"."p...
                                                                  ^ Like, 
    

    LINE 1: 是什么,对吧?

【问题讨论】:

    标签: django unit-testing migration django-south


    【解决方案1】:

    能否提供south生成的完整迁移文件?

    编辑:

    迁移似乎是正确的,只是想看看是否有人摆弄它。但有时你必须摆弄周围的东西才能把它们弄明白。我建议您移动与模型数据相关的代码,即

    users = User.objects.all()
    for user in users:
        user.registered_at = user.joined_at
        user.save()
    

    进入单独迁移。要创建一个空白迁移,在其 forwardbackward 方法中没有任何代码的迁移文件,使用 python manage.py datamigration app_name file_name 并在 forward 方法中添加上述代码。

    我希望这对你有用..

    【讨论】:

    • 两个相关的迁移(00100025)已经非常完整了。无论如何,我在models = {...} 中添加了users 哈希,以防万一。我认为在现有迁移中发布更多迁移或更多代码的帮助有限、笨拙,并且可能对我的公司构成安全问题。
    • 感谢阿皮特。从组织上讲,您是对的,代码应该在特定于数据的迁移中。然而,仅凭这一点并不能解决问题。我下面的解决方案(使用orm)将。
    【解决方案2】:

    问题是迁移0010中的以下代码:

    from ymc.model.users import User
    ...
        users = User.objects.all()
        for user in users:
            user.registered_at = user.joined_at
            user.save()
    

    我认为这一定是由构建系统的人添加的,现在已经不在了。他聪明又厉害,但没有人是完美的。 According to the South docs on data migrations,需要使用orm.YourModel,否则使用当前schema。这就是南/数据库看到新的publisher_id 列的原因。

    该块应该是:

    users = orm.User.objects.all()
    for user in users:
        user.registered_at = user.joined_at
        user.save()
    

    【讨论】:

      猜你喜欢
      • 2014-10-29
      • 2012-08-06
      • 2012-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-12
      • 2012-10-16
      • 2016-11-21
      相关资源
      最近更新 更多