标签归档:django-south

使用Django / South重命名模型的最简单方法?

问题:使用Django / South重命名模型的最简单方法?

我一直在South的网站,Google和SO上寻找答案,但是找不到简单的方法来做到这一点。

我想使用South重命名Django模型。说您有以下几点:

class Foo(models.Model):
    name = models.CharField()

class FooTwo(models.Model):
    name = models.CharField()
    foo = models.ForeignKey(Foo)

并且您想要将Foo转换为Bar,即

class Bar(models.Model):
    name = models.CharField()

class FooTwo(models.Model):
    name = models.CharField()
    foo = models.ForeignKey(Bar)

为简单起见,我只是尝试将名称从更改FooBar,但现在忽略其中的foo成员FooTwo

使用South进行此操作最简单的方法是什么?

  1. 我可能可以进行数据迁移,但这似乎很复杂。
  2. 编写一个自定义迁移,例如db.rename_table('city_citystate', 'geo_citystate'),但是在这种情况下我不确定如何修复外键。
  3. 您知道一种更简单的方法吗?

I’ve been hunting for an answer to this on South’s site, Google, and SO, but couldn’t find a simple way to do this.

I want to rename a Django model using South. Say you have the following:

class Foo(models.Model):
    name = models.CharField()

class FooTwo(models.Model):
    name = models.CharField()
    foo = models.ForeignKey(Foo)

and you want to convert Foo to Bar, namely

class Bar(models.Model):
    name = models.CharField()

class FooTwo(models.Model):
    name = models.CharField()
    foo = models.ForeignKey(Bar)

To keep it simple, I’m just trying to change the name from Foo to Bar, but ignore the foo member in FooTwo for now.

What’s the easiest way to do this using South?

  1. I could probably do a data migration, but that seems pretty involved.
  2. Write a custom migration, e.g. db.rename_table('city_citystate', 'geo_citystate'), but I’m not sure how to fix the foreign key in this case.
  3. An easier way that you know?

回答 0

为了回答您的第一个问题,简单的模型/表重命名非常简单。运行命令:

./manage.py schemamigration yourapp rename_foo_to_bar --empty

(更新2:尝试--auto,而不是--empty避免低于警告感谢@KFB的提示。)

如果您使用的是南方的旧版本,则需要startmigration而不是schemamigration

然后手动编辑迁移文件,如下所示:

class Migration(SchemaMigration):

    def forwards(self, orm):
        db.rename_table('yourapp_foo', 'yourapp_bar')


    def backwards(self, orm):
        db.rename_table('yourapp_bar','yourapp_foo')   

您可以使用db_table模型类中的Meta选项来更简单地完成此操作。但是每次这样做,都增加了代码库的旧版权重-类名与表名不同会使代码难以理解和维护。为了清楚起见,我完全支持进行这样的简单重构。

(更新)我刚刚在生产环境中尝试过此操作,并在应用迁移时收到一个奇怪的警告。它说:

The following content types are stale and need to be deleted:

    yourapp | foo

Any objects related to these content types by a foreign key will also
be deleted. Are you sure you want to delete these content types?
If you're unsure, answer 'no'.

我回答“不”,一切似乎都很好。

To answer your first question, the simple model/table rename is pretty straightforward. Run the command:

./manage.py schemamigration yourapp rename_foo_to_bar --empty

(Update 2: try --auto instead of --empty to avoid the warning below. Thanks to @KFB for the tip.)

If you’re using an older version of south, you’ll need startmigration instead of schemamigration.

Then manually edit the migration file to look like this:

class Migration(SchemaMigration):

    def forwards(self, orm):
        db.rename_table('yourapp_foo', 'yourapp_bar')


    def backwards(self, orm):
        db.rename_table('yourapp_bar','yourapp_foo')   

You can accomplish this more simply using the db_table Meta option in your model class. But every time you do that, you increase the legacy weight of your codebase — having class names differ from table names makes your code harder to understand and maintain. I fully support doing simple refactorings like this for the sake of clarity.

(update) I just tried this in production, and got a strange warning when I went to apply the migration. It said:

The following content types are stale and need to be deleted:

    yourapp | foo

Any objects related to these content types by a foreign key will also
be deleted. Are you sure you want to delete these content types?
If you're unsure, answer 'no'.

I answered “no” and everything seemed to be fine.


回答 1

进行更改models.py,然后运行

./manage.py schemamigration --auto myapp

检查迁移文件时,您会看到它删除了一个表并创建了一个新表。

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Deleting model 'Foo'                                                                                                                      
        db.delete_table('myapp_foo')

        # Adding model 'Bar'                                                                                                                        
        db.create_table('myapp_bar', (
        ...
        ))
        db.send_create_signal('myapp', ['Bar'])

    def backwards(self, orm):
        ...

这不是您想要的。而是编辑迁移,使其看起来像:

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Renaming model from 'Foo' to 'Bar'                                                                                                                      
        db.rename_table('myapp_foo', 'myapp_bar')                                                                                                                        
        if not db.dry_run:
            orm['contenttypes.contenttype'].objects.filter(
                app_label='myapp', model='foo').update(model='bar')

    def backwards(self, orm):
        # Renaming model from 'Bar' to 'Foo'                                                                                                                      
        db.rename_table('myapp_bar', 'myapp_foo')                                                                                                                        
        if not db.dry_run:
            orm['contenttypes.contenttype'].objects.filter(app_label='myapp', model='bar').update(model='foo')

在没有该update语句的情况下,该db.send_create_signal调用将ContentType使用新的模型名称创建一个新的模型。但它最好只updateContentType你已经拥有的情况下有数据库对象指向它(例如,通过一GenericForeignKey)。

另外,如果您已经重命名了某些列,这些列是重命名模型的外键,请不要忘记

db.rename_column(myapp_model, foo_id, bar_id)

Make the changes in models.py and then run

./manage.py schemamigration --auto myapp

When you inspect the migration file, you’ll see that it deletes a table and creates a new one

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Deleting model 'Foo'                                                                                                                      
        db.delete_table('myapp_foo')

        # Adding model 'Bar'                                                                                                                        
        db.create_table('myapp_bar', (
        ...
        ))
        db.send_create_signal('myapp', ['Bar'])

    def backwards(self, orm):
        ...

This is not quite what you want. Instead, edit the migration so that it looks like:

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Renaming model from 'Foo' to 'Bar'                                                                                                                      
        db.rename_table('myapp_foo', 'myapp_bar')                                                                                                                        
        if not db.dry_run:
            orm['contenttypes.contenttype'].objects.filter(
                app_label='myapp', model='foo').update(model='bar')

    def backwards(self, orm):
        # Renaming model from 'Bar' to 'Foo'                                                                                                                      
        db.rename_table('myapp_bar', 'myapp_foo')                                                                                                                        
        if not db.dry_run:
            orm['contenttypes.contenttype'].objects.filter(app_label='myapp', model='bar').update(model='foo')

In the absence of the update statement, the db.send_create_signal call will create a new ContentType with the new model name. But it’s better to just update the ContentType you already have in case there are database objects pointing to it (e.g., via a GenericForeignKey).

Also, if you’ve renamed some columns which are foreign keys to the renamed model, don’t forget to

db.rename_column(myapp_model, foo_id, bar_id)

回答 2

南方本身不能做-怎么知道这Bar代表Foo过去?我将为此编写自定义迁移。您可以ForeignKey像上面所做的那样更改in代码,然后只是重命名适当的字段和表的一种情况,您可以根据需要进行任何操作。

最后,您真的需要这样做吗?我还不需要重命名模型-模型名称只是实现细节-特别是考虑到verbose_nameMeta选项的可用性。

South can’t do it itself – how does it know that Bar represents what Foo used to? This is the sort of thing I’d write a custom migration for. You can change your ForeignKey in code as you’ve done above, and then it’s just a case of renaming the appropriate fields and tables, which you can do any way you want.

Finally, do you really need to do this? I’ve yet to need to rename models – model names are just an implementation detail – particularly given the availability of the verbose_name Meta option.


回答 3

我遵循了上面Leopd的解决方案。但是,这并没有更改型号名称。我在代码中手动更改了它(在相关模型中也将其称为FK)。并进行了另一个南迁,但带有–fake选项。这使得模型名称和表名称相同。

刚意识到,可以先从更改模型名称开始,然后在应用迁移文件之前编辑迁移文件。干净得多。

I followed Leopd’s solution above. But, that did not change the model names. I changed it manually in the code (also in related models where this is referred as FK). And done another south migration, but with –fake option. This makes model names and table names to be same.

Just realized, one could first start with changing model names, then edit the migrations file before applying them. Much cleaner.


Django-如何使用South重命名模型字段?

问题:Django-如何使用South重命名模型字段?

我想更改模型中特定字段的名称:

class Foo(models.Model):
    name = models.CharField()
    rel  = models.ForeignKey(Bar)

应更改为:

class Foo(models.Model):
    full_name     = models.CharField()
    odd_relation  = models.ForeignKey(Bar)

使用South进行此操作最简单的方法是什么?

I would like to change a name of specific fields in a model:

class Foo(models.Model):
    name = models.CharField()
    rel  = models.ForeignKey(Bar)

should change to:

class Foo(models.Model):
    full_name     = models.CharField()
    odd_relation  = models.ForeignKey(Bar)

What’s the easiest way to do this using South?


回答 0

您可以使用该db.rename_column功能。

class Migration:

    def forwards(self, orm):
        # Rename 'name' field to 'full_name'
        db.rename_column('app_foo', 'name', 'full_name')




    def backwards(self, orm):
        # Rename 'full_name' field to 'name'
        db.rename_column('app_foo', 'full_name', 'name')

表的第一个参数db.rename_column是表名,因此记住Django如何创建表名很重要:

Django会自动从您的模型类的名称和包含该表的应用程序中获取数据库表的名称。通过将模型的“应用程序标签”(即您在manage.py startapp中使用的名称)连接到模型的类名称,并在其之间加下划线,来构造模型的数据库表名称。

在你有一个多措辞,骆驼套管型号名称的情况下,如项目项,表名会app_projectitem(即下划线不会之间插入projectitem即使它们是骆驼式大小写)。

You can use the db.rename_column function.

class Migration:

    def forwards(self, orm):
        # Rename 'name' field to 'full_name'
        db.rename_column('app_foo', 'name', 'full_name')




    def backwards(self, orm):
        # Rename 'full_name' field to 'name'
        db.rename_column('app_foo', 'full_name', 'name')

The first argument of db.rename_column is the table name, so it’s important to remember how Django creates table names:

Django automatically derives the name of the database table from the name of your model class and the app that contains it. A model’s database table name is constructed by joining the model’s “app label” — the name you used in manage.py startapp — to the model’s class name, with an underscore between them.

In the case where you have a multi-worded, camel-cased model name, such as ProjectItem, the table name will be app_projectitem (i.e., an underscore will not be inserted between project and item even though they are camel-cased).


回答 1

这是我的工作:

  1. 在模型中更改列名(在本示例中为myapp/models.py
  2. ./manage.py schemamigration myapp renaming_column_x --auto

注意renaming_column_x可以是任何您喜欢的东西,它只是给迁移文件起一个描述性名称的一种方式。

这将为您生成一个名为的文件myapp/migrations/000x_renaming_column_x.py,该文件将删除您的旧列并添加一个新列。

修改此文件中的代码,以将迁移行为更改为简单的重命名:

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Renaming column 'mymodel.old_column_name' to 'mymodel.new_column_name'
        db.rename_column(u'myapp_mymodel', 'old_column_name', 'new_column_name')

    def backwards(self, orm):
        # Renaming column 'mymodel.new_column_name' to 'mymodel.old_column_name'
        db.rename_column(u'myapp_mymodel', 'new_column_name', 'old_column_name')

Here’s what I do:

  1. Make the column name change in your model (in this example it would be myapp/models.py)
  2. Run ./manage.py schemamigration myapp renaming_column_x --auto

Note renaming_column_x can be anything you like, it’s just a way of giving a descriptive name to the migration file.

This will generate you a file called myapp/migrations/000x_renaming_column_x.py which will delete your old column and add a new column.

Modify the code in this file to change the migration behaviour to a simple rename:

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Renaming column 'mymodel.old_column_name' to 'mymodel.new_column_name'
        db.rename_column(u'myapp_mymodel', 'old_column_name', 'new_column_name')

    def backwards(self, orm):
        # Renaming column 'mymodel.new_column_name' to 'mymodel.old_column_name'
        db.rename_column(u'myapp_mymodel', 'new_column_name', 'old_column_name')

回答 2

我不知道db.rename列,听起来很方便,但是在过去,我将新列添加为一个schemamigration,然后创建了一个datamigration将值移到新字段中,然后创建了另一个schemamigration以删除旧列。

I didn’t know about db.rename column, sounds handy, however in the past I have added the new column as one schemamigration, then created a datamigration to move values into the new field, then a second schemamigration to remove the old column


回答 3

Django 1.7引入了Migrations,所以现在您甚至不需要安装额外的软件包即可管理迁移。

要重命名模型,您需要首先创建空迁移:

$ manage.py makemigrations <app_name> --empty

然后,您需要像这样编辑迁移代码:

from django.db import models, migrations

class Migration(migrations.Migration):

dependencies = [
    ('yourapp', 'XXXX_your_previous_migration'),
]

operations = [
    migrations.RenameField(
        model_name='Foo',
        old_name='name',
        new_name='full_name'
    ),
    migrations.RenameField(
        model_name='Foo',
        old_name='rel',
        new_name='odd_relation'
    ),
]

然后,您需要运行:

$ manage.py migrate <app_name>

Django 1.7 introduced Migrations so now you don’t even need to install extra package to manage your migrations.

To rename your model you need to create empty migration first:

$ manage.py makemigrations <app_name> --empty

Then you need to edit your migration’s code like this:

from django.db import models, migrations

class Migration(migrations.Migration):

dependencies = [
    ('yourapp', 'XXXX_your_previous_migration'),
]

operations = [
    migrations.RenameField(
        model_name='Foo',
        old_name='name',
        new_name='full_name'
    ),
    migrations.RenameField(
        model_name='Foo',
        old_name='rel',
        new_name='odd_relation'
    ),
]

And after that you need to run:

$ manage.py migrate <app_name>

回答 4

只需更改模型并makemigrations在1.9中运行

Django自动检测到您已删除并创建了一个字段,并询问:

Did you rename model.old to model.new (a IntegerField)? [y/N]

同意,就可以创建正确的迁移。魔法。

Just change the model and run makemigrations in 1.9

Django automatically detects that you’ve deleted and created a single field, and asks:

Did you rename model.old to model.new (a IntegerField)? [y/N]

Say yes, and the right migration gets created. Magic.


回答 5

  1. south在项目设置文件中添加到已安装的应用程序。
  2. 注释掉添加/修改的字段/表。
  3. $ manage.py Schemamigration <app_name> --initial
  4. $ manage.py migrate <app_name> --Fake
  5. 取消注释该字段并写入修改后的字段
  6. $ manage.py Schemamigration --auto
  7. $ manage.py migrate <app_name>

如果使用的是“ pycharm”,则可以使用“ ctrl + shift + r”代替“ manage.py”,并使用“ shift”作为参数。

  1. Add south to your installed apps in project setting file.
  2. Comment out the added/modified field/table.
  3. $ manage.py Schemamigration <app_name> --initial
  4. $ manage.py migrate <app_name> --Fake
  5. Un-comment the field and write the modified one
  6. $ manage.py Schemamigration --auto
  7. $ manage.py migrate <app_name>

If you are using ‘pycharm’, then you can use ‘ctrl+shift+r’ instead of ‘manage.py’ , and ‘shift ‘ for parameters.