迁移简介

迁移

将迁移添加到应用

如果应用程序已经有模型和数据库表,但是没有迁移,则需要将其转换为使用迁移:

python manage.py makemigrations your_app_label

执行python manage.py migrate - fake-initial 。Django将检测到一个初始迁移要创建的表已经存在,并将标记为已应用迁移。(没有--fake-initial标志,migrate命令将错误输出,因为要创建的表已存在

 

数据迁移

使用迁移来更改数据库中的数据,如果需要,还可以与模式一起使用。

更改数据的迁移通常称为“数据迁移”;他们最好写成单独的迁移,坐在模式迁移。

创建空的迁移文件:

python manage.py makemigrations --empty yourappname

打开生成的迁移文件:

from django.db import models, migrations

class Migration(migrations.Migration):

    dependencies = [
        ('yourappname', '0001_initial'),
    ]

    operations = [
    ]

创建一个新函数,并使用RunPythonRunPython需要一个可调用作为其参数,它需要两个参数 - 第一个是一个app registry,它将所有模型的历史版本加载到其中以匹配历史记录迁移就位,第二个是SchemaEditor,您可以使用它手动实现数据库模式更改(但要小心,这样做可能会使迁移自动检测器混乱)

用first_name和last_name的组合值填充新的name字段,使用历史模型并在行上进行迭代:

def combine_names(apps, schema_editor):
# We can't import the Person model directly as it may be a newer
    MyModel = apps.get_model('demo1', 'MyModel')
    for m in MyModel.objects.all():
        m.name = '%s %s' % (m.first_name, m.last_name)
        m.save()
class Migration(migrations.Migration):
    dependencies = [
        ('demo1', '0001_initial'),
    ]
    operations = [
        migrations.RunPython(combine_names),
    ]

正常运行python manage.py migrate与其他迁移一起放置

通过其他应用访问模型

在下面的示例中,我们在app1中有一个迁移,需要使用app2中的模型。我们不关心move_m1的细节,而是需要从两个应用程序访问模型的事实。因此,我们添加了一个依赖关系,指定app2的最后一次迁移:

class Migration(migrations.Migration):

    dependencies = [
        ('app1', '0001_initial'),
        # added dependency to enable using models from app2 in move_m1
        ('app2', '0004_foobar'),
    ]

    operations = [
        migrations.RunPython(move_m1),
    ]

 

模式操作

自定义编写迁移文件API

CreateModel

class CreateModel(namefieldsoptions=Nonebases=Nonemanagers=None)

在项目历史中创建一个新模型,并在数据库中创建一个相应的表来匹配它。

name是model的名称,如写在models.py文件中。

fields是(field_name, field_instance)的2元组的列表。字段实例应为未绑定字段(因此只有models.CharField(),而不是一个字段从另一个模型)。

options是来自模型的Meta类的值的可选字典。

bases是此模型继承的其他类的可选列表;它可以包含类对象以及格式为"appname.ModelName"的字符串,如果你想依赖另一个模型(所以你从历史版本继承)。如果没有提供,它默认只继承标准的models.Model。

managers获取(manager_name, manager_instance)的2元组列表。列表中的第一个管理器将成为迁移期间此模型的默认管理器。

migrations.CreateModel(
    name='Author',
    fields=[
        ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
        ('name', models.CharField(max_length=50, unique=True)),
        ('email', models.EmailField(max_length=254, unique=True)),
    ],
),

更多参考官方文档

RunSQL

class RunSQL(sqlreverse_sql=Nonestate_operations=Nonehints=None)

允许在数据库上运行任意SQL - 对于Django不能直接支持的数据库后端的更高级功能(如部分索引)非常有用。

sql和reverse_sql(如果提供)应为在数据库上运行的SQL字符串。在大多数数据库后端(除PostgreSQL之外),Django将在执行它们之前将SQL拆分为单独的语句。这需要安装sqlparse Python库。

详细参考官方文档

 

RunPython

class RunPython(codereverse_code=Noneatomic=Truehints=None)

在历史上下文中运行自定义Python代码。code(和reverse_code如果提供)应该是接受两个参数的可调用对象;第一个是包含与项目历史中的操作位置匹配的历史模型的django.apps.registry.Apps的实例,第二个是SchemaEditor的实例。

详细参考官方文档

 

模式编辑器

没看懂是干嘛的,后面有时间在研究研究

官方文档

 

编写数据库迁移

 

数据迁移和多数据库

在使用多个数据库时,需要解决是否针对某个特定数据库运行迁移。比如说你 只是 想在特定的数据库上做迁移。

为此你可以在RunPython中通过查看schema_editor.connection.alias 属性来检查数据库连接别名:

from django.db import migrations

def forwards(apps, schema_editor):
    if not schema_editor.connection.alias == 'default':
        return
    # Your migration code goes here

class Migration(migrations.Migration):

    dependencies = [
        # Dependencies to other migrations
    ]

    operations = [
        migrations.RunPython(forwards),
    ]

New in Django 1.8.

你也可以提供一个提示作为 **hints参数传递到数据库路由的allow_migrate() 方法:

myapp/dbrouters.py

class MyRouter(object):

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        if 'target_db' in hints:
            return db == hints['target_db']
        return True

然后,要在你的迁移中利用,执行以下操作:

from django.db import migrations

def forwards(apps, schema_editor):
    # Your migration code goes here

class Migration(migrations.Migration):

    dependencies = [
        # Dependencies to other migrations
    ]

    operations = [
        migrations.RunPython(forwards, hints={'target_db': 'default'}),
    ]

如果你的RunPython或者RunSQL操作只对一个模型有影响,最佳实践是将model_name作为提示传递,使其尽可能对路由可见。这对可复用的和第三方应用极其重要。

添加唯一字段的迁移

当数据表存在记录,又不能删除表的情况下,添加唯一字段会出现错误

class MyModel(models.Model):
    # ...
    uuid = models.UUIDField(default=uuid.uuid4, unique=True)  # 新增字段

运行 makemigrations 命令,生成迁移文件0003_mymodel_uuid.py:

from __future__ import unicode_literals
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
    dependencies = [
        ('demo1', '0002_auto_20170818_1216'),
    ]
    operations = [
        migrations.AddField(
            model_name='mymodel',
            name='uuid',
            field=models.UUIDField(default=uuid.uuid4, unique=True),
        ),
    ]

如果现在运行migrate命令会报错,我这里出现的错误是Duplicate KeyError

修改刚刚生成的迁移文件0003_mymodel_uuid.py:

  • 从已生成的迁移类中复制,添加第二个AddField操作,并改为AlterField
  • 在第一个AddField操作中,把unique=True改为 null=True,这会创建一个中间的null字段。
  • 在两个操作之间,添加一个RunPythonRunSQL操作为每个已存在的行生成一个唯一值(例如UUID)
def gen_uuid(apps, schema_editor):
    MyModel = apps.get_model('demo1', 'MyModel')
    for row in MyModel.objects.all():
        row.uuid = uuid.uuid4()
        row.save()

class Migration(migrations.Migration):

    dependencies = [
        ('demo1', '0002_auto_20170818_1216'),
    ]
    # 1.先增加uuid字段,可以为空
    operations = [
        migrations.AddField(
            model_name='mymodel',
            name='uuid',
            field=models.UUIDField(default=uuid.uuid4, null=True),
        ),
        # omit reverse_code=... if you don't want the migration to be reversible.
        # 2.调用gen_uuid 为该字段所有记录赋值更新并保存到数据库
        migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop),
        # 3.修改字段属性,增加unique=True
        migrations.AlterField(
            model_name='mymodel',
            name='uuid',
            field=models.UUIDField(default=uuid.uuid4, unique=True),
        ),
    ]

使用migrate命令应用迁移。

注意

如果你在这个迁移运行时让对象被创建,就会产生竞争条件(race condition)。在AddField之后, RunPython之前创建的对象会覆写他们原始的uuid。

 

转载于:https://my.oschina.net/acutesun/blog/1515182

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值