新的Django South迁移破坏了早期的迁移

时间:2013-11-28 00:15:47

标签: django unit-testing migration django-south

甲。总结:

成功运行新迁移(向现有表添加新列)后,在同一个表(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。您的新列的模型代码是什么样的?

我向publisher_idusers添加了games列,如下所示:

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


3。南方生成了什么代码?

以下是已开始失败的现有迁移:

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:什么,对吧?

2 个答案:

答案 0 :(得分:0)

您能提供由南方生成的完整迁移文件吗?

修改

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

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

进入单独的迁移。要创建空白迁移,请使用其中forward&的任何代码的迁移文件。 backward种方法,使用python manage.py datamigration app_name file_name并在转发方法中添加上述代码。

我希望这对你有用..

答案 1 :(得分:0)

问题是迁移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,否则将使用当前架构。这就是South /数据库看到新publisher_id列的原因。

该块应该是:

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