更多内容请点击 我的博客 查看,欢迎来访。
[TOC]
Django迁移:入门
原文by Daniel Hepper 翻译by StarMeow
从1.7版开始,Django就内置了对数据库迁移的支持。 在Django中,数据库迁移通常与模型密切相关:每当编写新模型时,还会生成迁移以在数据库中创建必要的表。 但是,迁移可以做得更多。
在本文中,熟悉Django迁移并学习以下内容:
- 如何在不编写任何SQL的情况下创建数据库表
- 更改模型后如何自动修改数据库
- 如何还原对数据库所做的更改
迁移解决的问题
如果不熟悉Django或Web开发,可能不熟悉数据库迁移的概念,它们带来的好处可能并不明显。
首先,让我们快速定义几个术语。 Django旨在与关系数据库一起使用,存储在PostgreSQL,MySQL或SQLite等关系数据库管理系统中。
在关系数据库中,数据以表格形式组织。数据库表具有一定数量的列,但它可以包含任意数量的行。每列都有一个特定的数据类型,如某个特定最大长度的字符串或正整数。所有表、它的列,以及其各自数据类型的描述称为数据库模式。
Django支持的所有数据库系统都使用SQL语言来创建,读取,更新和删除关系数据库中的数据。 SQL还用于创建,更改和删除数据库表本身。
直接使用SQL可能非常麻烦,因此为了让编程更轻松,Django附带了一个对象关系映射器,简称ORM。 ORM将关系数据库映射到面向对象编程的世界。可以在Python中编写Django模型,而不是在SQL中定义数据库表。使用模型定义数据库字段,这些字段对应于其在数据库表中的列。
以下是Django模型类如何映射到数据库表的示例:
但是只是在Python文件中定义一个模型类后,并不会使数据库表凭空出现。 使用数据库迁移可以创建数据库表来存储Django模型。 此外,无论何时对模型进行更改(如添加字段),也必须更改数据库。 迁移正是处理这个问题。
以下是Django迁移让编程更轻松的几种方式。
不使用SQL进行数据库更改
如果没有迁移,每次要更改模型定义时修改数据库模式时,则必须连接到数据库并键入一堆SQL命令,或者使用PHPMyAdmin等图形工具。
在Django中,迁移主要是用Python编写的,因此除非有非常高级的用法,否则不必知道任何SQL。
避免重复
创建模型后,然后编写SQL语句来其创建数据库表将是重复的。
从模型生成迁移,将不会做重复的工作。
确保模型定义和数据库模式同步
通常,有多个数据库实例,例如,团队中每个开发人员都有一个数据库,用于测试的数据库和包含实时数据的数据库。
如果没有迁移,则必须对每个数据库执行任何模式更改,并且必须跟踪已对哪个数据库进行了哪些更改。
使用Django迁移,可以轻松地使多个数据库与模型保持同步。
跟踪版本控制中的数据库模式更改
像Git这样的版本控制系统非常适合代码,但对数据库模式来说并不是那么适用。
由于迁移在Django中是纯Python,所以可以将它们像其他代码一样放在版本控制系统中。
到目前为止,可能确信迁移是一种有用且强大的工具。让我们开始学习如何使用它。
设置Django项目
在本教程中,将使用一个简单的比特币跟踪器app作为示例项目。
第一步是安装Django。 以下是使用虚拟环境在Linux或macOS X上执行此操作的方法:
$ python3 -m venv env
$ source env/bin/activate
(env) $ pip install "Django==2.1.*"
...
Successfully installed Django-2.1.3
现在已经创建了一个新的虚拟环境并将其激活,并在该虚拟环境中安装了Django。
请注意,在Windows上,将运行env/bin/activate.bat
而不是source env/bin/activate
来激活虚拟环境。
为了便于阅读,控制台示例将不包括从现在开始的提示的(env)部分。
安装Django后,可以使用以下命令创建项目:
$ django-admin.py startproject bitcoin_tracker
$ cd bitcoin_tracker
$ python manage.py startapp historical_data
将提供一个简单的项目和一个名为historical_data
的应用程序。现在这个目录结构如下:
bitcoin_tracker/
|
├── bitcoin_tracker/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
|
├── historical_data/
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations/
│ │ └── __init__.py
| |
│ ├── models.py
│ ├── tests.py
│ └── views.py
|
└── manage.py
在bitcoin_tracker
目录中,有两个子目录:用于项目配置文件的bitcoin_tracker
和包含创建的应用程序的文件的historical_data
。
现在,要创建模型,请在historical_data/models.py
中添加这个类:
class PriceHistory(models.Model):
date = models.DateTimeField(auto_now_add=True)
price = models.DecimalField(max_digits=7, decimal_places=2)
volume = models.PositiveIntegerField()
这是跟踪比特币价格的基本模型。
另外,不要忘记将新创建的应用程序添加到settings.INSTALLED_APPS
。 打开bitcoin_tracker/settings.py
并将historical_data
添加到列表INSTALLED_APPS
,如下所示:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'historical_data',
]
其他设置适用于此项目。 本教程假定项目配置为使用SQLite数据库,这是Django默认的配置。
创建迁移makemigrations
创建了模型之后,需要做的第一件事是为它创建迁移。可以用以下命令来完成:
$ python manage.py makemigrations historical_data
Migrations for 'historical_data':
historical_data/migrations/0001_initial.py
- Create model PriceHistory
注意:指定应用程序的名称
historical_data
是可选的。 不指定它会为所有应用程序创建迁移。
这将生成迁移文件,该文件指示Django如何为应用程序中定义的模型创建数据库表。 让我们再看看目录树:
bitcoin_tracker/
|
├── bitcoin_tracker/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
|
├── historical_data/
│ ├── migrations/
│ │ ├── 0001_initial.py
│ │ └── __init__.py
| |
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
|
├── db.sqlite3
└── manage.py
可以看到,migration
目录现在包含一个新文件:0001_initial.py
。
注意:可能会注意到运行
makemigrations
命令还会创建文件db.sqlite3
,其中包含SQLite数据库。
当尝试访问不存在的SQLite3数据库文件时,将自动创建它。
此行为是SQLite3独有的。 如果使用任何其他数据库后端,如PostgreSQL或MySQL,则必须在运行makemigrations
之前自己创建数据库。
可以使用dbshell
管理命令查看数据库。 在SQLite中,列出所有表的命令就是.tables
:
$ python manage.py dbshell
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .tables
sqlite>
数据库仍然是空的,因为还没有应用迁移。 键入.quit
以退出SQLite shell。
应用迁移migrate
现在已经创建了迁移,但要实际在数据库中进行任何更改,必须将其应用于管理命令migrate
:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying historical_data.0001_initial... OK
Applying sessions.0001_initial... OK
根据输出,迁移已成功应用。 但是所有其他迁移都来自哪里?
还记得设置INSTALLED_APPS
吗? 其中列出的其他一些应用程序也带有迁移功能,迁移管理命令默认情况下会为所有已安装的应用程序应用迁移。
再看一下数据库:
$ python manage.py dbshell
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .tables
auth_group django_admin_log
auth_group_permissions django_content_type
auth_permission django_migrations
auth_user django_session
auth_user_groups historical_data_pricehistory
auth_user_user_permissions
sqlite>
现在有多个表。 他们的名字可以了解他们的目的。 在上一步中生成的迁移已创建historical_data_pricehistory
表。
.schema查看创建表的SQL语句
让我们使用.schema
命令检查它:
sqlite> .schema --indent historical_data_pricehistory
CREATE TABLE IF NOT EXISTS "historical_data_pricehistory"(
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"date" datetime NOT NULL,
"price" decimal NOT NULL,
"volume" integer unsigned NOT NULL
);
.schema
命令打印出要为创建表而执行的CREATE
语句。 参数--indent
很好地格式化。 即使不熟悉SQL语法,也可以看到historical_data_pricehistory
表的模式反映了PriceHistory
模型的字段。
每个字段都有一列,主键还有一个列ID,除非在模型中明确指定主键,否则Django会自动创建。
如果再次运行migrate
命令会发生以下情况:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
No migrations to apply.
没有! Django记得已经应用了哪些迁移,并且不会尝试重新运行它们。
值得注意的是,还可以将迁移管理命令限制为单个应用程序:
$ python manage.py migrate historical_data
Operations to perform:
Apply all migrations: historical_data
Running migrations:
No migrations to apply.
可以看到,Django现在仅对historical_data
应用程序应用迁移。
当第一次运行迁移时,最好应用所有迁移,以确保数据库包含必要功能所必需的表,例如用户身份验证和会话。
改变模型
模型不是一成不变的。 随着Django项目获得更多功能,模型将会发生变化。 可以添加或删除字段或更改其类型和选项。
更改模型的定义时,还必须更改用于存储这些模型的数据库表。 如果模型定义与当前数据库模式不匹配,则很可能会遇到django.db.utils.OperationalError
。
那么如何更改数据库表? 通过创建和应用迁移。
在测试比特币跟踪器时,会发现自己犯了一个错误。 人们正在销售比特币的分数,因此字段量应为DecimalField
类型而不是PositiveIntegerField
。
让我们将模型更改为如下所示:
class PriceHistory(models.Model):
date = models.DateTimeField(auto_now_add=True)
price = models.DecimalField(max_digits=7, decimal_places=2)
volume = models.DecimalField(max_digits=7, decimal_places=3)
如果没有迁移,必须找出SQL语法将PositiveIntegerField
转换为DecimalField
。 幸运的是,Django将处理这个问题。 告诉它进行迁移:
$ python manage.py makemigrations
Migrations for 'historical_data':
historical_data/migrations/0002_auto_20181112_1950.py
- Alter field volume on pricehistory
注意:迁移文件的名称(
0002_auto_20181112_1950.py
)是基于当前时间的,如果不同系统上迁移的话,名称会有所不同。
现在将这个迁移应用到数据库:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
Applying historical_data.0002_auto_20181112_1950... OK
迁移已经成功应用,所以可以使用dbshell
验证更改是否有效果:
$ python manage.py dbshell
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .schema --indent historical_data_pricehistory
CREATE TABLE IF NOT EXISTS "historical_data_pricehistory" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"date" datetime NOT NULL,
"price" decimal NOT NULL,
"volume" decimal NOT NULL
);
如果将新模式与先前看到的模式进行比较,会注意到volume
列的类型已从integer
更改为decimal
,以反映模型中的Volume
字段从PositiveIntegerField
更改为DecimalField
。
列出迁移showmigrations
如果想知道Django项目中存在哪些迁移,则无需深入了解已安装应用程序的迁移目录。 可以使用showmigrations
命令:
$ ./manage.py showmigrations
admin
[X] 0001_initial
[X] 0002_logentry_remove_auto_add
[X] 0003_logentry_add_action_flag_choices
auth
[X] 0001_initial
[X] 0002_alter_permission_name_max_length
[X] 0003_alter_user_email_max_length
[X] 0004_alter_user_username_opts
[X] 0005_alter_user_last_login_null
[X] 0006_require_contenttypes_0002
[X] 0007_alter_validators_add_error_messages
[X] 0008_alter_user_username_max_length
[X] 0009_alter_user_last_name_max_length
contenttypes
[X] 0001_initial
[X] 0002_remove_content_type_name
historical_data
[X] 0001_initial
[X] 0002_auto_20181112_1950
sessions
[X] 0001_initial
这将列出项目中的所有应用程序以及与每个应用程序关联的迁移。 此外,它将在已经应用的迁移旁边放置一个大X
。
对于这个示例,showmigrations
命令并不是特别令人兴奋,但是当开始处理现有代码库,或在不是唯一添加迁移的团队中工作时它会派上用场,因为它可以看到团队中用的哪些文件已经应用过了。
取消应用迁移migrate
现在,已了解如何通过创建和应用迁移来更改数据库模式。 在某些时候,可能希望撤消更改并切换回早期的数据库模式,因为:
- 想要测试一位同事写的迁移
- 意识到所做的更改有问题
- 同时处理具有不同数据库更改的多个功能
- 想要还原在数据库仍具有较旧架构时创建的备份
幸运的是,迁移不一定是不可逆的。 在许多情况下,迁移的影响可以通过取消迁移来撤消。 要取消应用迁移,必须在要取消应用的迁移之前使用应用程序名称和迁移名称调用migrate
。
如果要在historical_data
应用程序中还原迁移0002_auto_20181112_1950
,则必须将0001_initial
作为参数传递给migrate
命令:
$ python manage.py migrate historical_data 0001_initial
Operations to perform:
Target specific migration: 0001_initial, from historical_data
Running migrations:
Rendering model states... DONE
Unapplying historical_data.0002_auto_20181112_1950... OK
可以看到迁移未应用,这意味着对数据库的更改已被撤消。
取消应用迁移不会删除其迁移文件。 下次运行migrate
命令时,将再次应用迁移。
警告:不要将未应用的迁移与最常用的文本编辑器中的撤消操作混淆。
并非所有数据库操作都可以完全还原。 如果从模型中删除字段,创建迁移并应用它,Django将从数据库中删除相应的列。
取消应用迁移将重新创建列,但它不会带回存储在该列中的数据!
当处理迁移名称时,Django会通过不强制拼出迁移的全名来节省一些按键。 它只需要足够的名称来识别它。
在前面的示例中,运行python manage.py migrate history_data 0001
就足够了。
命名迁移--name
在上面的示例中,Django根据时间戳提出了迁移名称 - 类似于*0002_auto_20181112_1950
。 如果对此不满意,则可以使用--name
参数提供自定义名称(不带.py
扩展名)。
要试一试,首先必须删除旧的迁移。 已经取消应用它,因此可以安全地删除该文件:
$ rm historical_data/migrations/0002_auto_20181112_1950.py
现在可以用一个更具描述性的名字重新创建它:
$ ./manage.py makemigrations historical_data --name switch_to_decimals
这将创建与之前相同的迁移,只不过新名称为0002_switch_to_decimals
结论
在本教程中介绍了相当多的内容,并学习了Django迁移的基础知识。
回顾一下,使用Django迁移的基本步骤如下所示:
- 创建或更新模型
- 运行
./manage.py makemigrations
- 运行
./manage.py migrate
以迁移所有内容或./manage.py migrate
以迁移单个应用程序 - 必要时重复
而已! 此工作流程在大多数情况下都可以工作,但如果流程没有按预期进行,还知道如何列出和取消应用迁移。
如果以前使用手写SQL创建和修改了数据库表,那么通过将此工作委派给Django迁移,现在可以提高效率。
在本系列的下一个教程中,将深入了解该主题并了解Django Migrations如何在底层工作。
Django迁移:深入
原文by Real Python 翻译by StarMeow
上面我们讨论了使用新的Django迁移系统的基础知识。
当流程无法正常工作时会发生什么? 好吧,那时可能不得不去检查以前的迁移,试图弄清楚发生了什么。 为了帮助解决这个问题,让我们深入了解一下,以便更好地了解迁移的工作原理。
迁移时如何知道要迁移的内容
试试这个。 从bitcoin_tracker
应用程序再次运行迁移(./manage.py migrate
)。 怎么了? 没有。 而这正是重点。
默认情况下,Django永远不会在同一个数据库上多次运行迁移。 这由一个名为django_migrations
的表管理,该表在第一次运行迁移时在数据库中创建。 对于每次运行或伪造的迁移,都会在表中插入一个新行。
例如,以下是运行初始迁移后表的数据:
ID | app | name | applied |
---|---|---|---|
1 | historical_payments | 0001_initial | 2014-04-16 14:12:30.839899+08 |
很有趣,因为只有一次迁移,但每次后续迁移都会添加新行。
下次运行迁移时,它将跳过数据库表中列出的迁移文件。这意味着即使手动更改迁移文件,如果数据库中已存在该条目,也会跳过该文件。
这是有道理的,因为通常不希望两次运行迁移。但是如果做了什么原因,让它再次运行的一种方法是首先从数据库中删除相应的行(注意这不是“官方推荐的方式”,但它会起作用)。在第一次运行迁移时从South升级的情况下,Django将首先检查数据库结构,如果它与迁移相同(即迁移不应用任何新的更改),那么迁移将是“伪造“意思不是真的运行,但django_migrations
表仍将更新。
相反,如果要“撤消”特定应用程序的所有迁移,可以迁移到名为zero
的特殊迁移。
例如,如果键入:
$ ./manage.py migrate historical_data zero
它将撤消historical_data
应用程序的所有迁移。 除了使用zero
;也可以使用任意迁移名称,如果该迁移过去,则数据库将回滚到该迁移的状态,或者如果尚未运行迁移则前滚。 相当强大的东西!
迁移文件
如何创建实际的迁移文件? 换句话说,运行./manage.py makemigrations
时会发生什么?
Django迁移实际上是创建一个迁移文件,该文件描述了如何在数据库中创建适当的表。 实际上,可以查看已创建的迁移文件。 别担心:这只是Python类型的文件。
不要忘记git添加新的迁移目录,以便它受版本控制。
historical_prices
应用程序现在将有一个名为/migrations
的子目录,其中该应用程序的所有迁移文件都将保存在该目录中。 我们来看看historical_data/migrations/0001_initial.py
,因为这是创建初始迁移代码的文件。 它看起来应该类似于:
# encoding: utf8
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
]
operations = [
migrations.CreateModel(
name='PriceHistory',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, primary_key=True, auto_created=True)),
('date', models.DateTimeField(auto_now_add=True)),
('price', models.DecimalField(decimal_places=2, max_digits=5)),
('volume', models.PositiveIntegerField()),
('total_btc', models.PositiveIntegerField()),
],
options={
},
bases=(models.Model,),
),
]
要使迁移工作,必须创建一个名为Migration()
的类,该类继承自django.db.migrations.Migration
。 这是迁移框架在我们要求它运行迁移时将查找并执行的类(我们稍后会这样做)。
Migration
类包含两个主要列表,依赖项dependencies
和操作operations
。
迁移依赖项
dependencies
对象是在运行此迁移之前必须运行的迁移列表。
在上面的情况下,没有必须先运行,所以没有依赖。 但是,如果有外键关系,那么必须确保在向其添加外键之前创建模型。 所以我们假设我们有另一个名为main
的应用程序,它定义了我们想要在外键中引用的表。 然后我们的依赖项列表可能如下所示:
dependencies = [
('main', '__first__'),
]
上面的依赖关系表明必须首先运行main
应用程序的迁移。
还可以依赖于特定文件,如下所示:
dependencies = [
('main', '0001_initial'),
]
这是来自main
应用程序的名为0001_initial
的文件的依赖项。
依赖关系也可以组合在一起,因此可以拥有多个依赖关系。 此功能提供了很大的灵活性,因为可以容纳依赖于不同应用程序模型的外键。 这也意味着迁移的编号(通常是0001,0002,0003,......)并不一定严格按照它们的应用顺序排列。 可以添加所需的任何依赖项,从而控制顺序,而无需重新编号所有迁移。
迁移操作
Migration()
类中的第二个列表是operations
列表。 这是要作为迁移的一部分应用的操作列表。 通常,操作可以属于以下类型之一:
-
CreateModel
:你猜对了:这会创建一个新模型。 有关示例,请参阅上面的迁移。 -
DeleteModel
:从数据库中删除一个表; 只需传入模型的名称。 -
RenameModel
:给定old_name和new_name,这将重命名模型。 -
AlterModelTable
:更改与模型关联的表的名称。 与db_table选项相同。 -
AlterUniqueTogether
:更改唯一约束。 -
AlteIndexTogether
:更改模型的自定义索引集。 -
AddField
:就像听起来一样。 这是一个例子:
migrations.AddField(
model_name='PriceHistory',
name='market_cap',
field=models.PositiveIntegerField(),
),
-
RemoveField
:我们不再需要那个字段......只需删除它即可。 -
RenameField
:给定model_name
,old_name
和new_name
,这会将old_name
字段更改为new_name
。
还有一些“特殊”操作:
-
RunSQL
:这允许传入原始SQL并将其作为模型的一部分执行。 -
RunPython
:传入一个要执行的可调用函数;对于像迁移这样的数据加载之类的东西很有用。
甚至可以编写自己的操作。 通常,当运行makemigrations
时,Django将使用需要的相应依赖项和操作创建必要的迁移。 但是,了解迁移文件本身及其工作原理可提供更大的灵活性。
示例
让我们对模型再做一些修改,看看对迁移的影响:
class PriceHistory(models.Model):
date = models.DateTimeField(auto_now_add=True)
# Bitcoin to the moon (we need more digits)
price = models.DecimalField(max_digits=8, decimal_places=2)
volume = models.PositiveIntegerField()
total_btc = models.PositiveIntegerField()
market_cap = models.PositiveIntegerField(null=True)
由于看好比特币,我们已经决定在价格领域需要更多的数字,我们还决定跟踪市值。 请注意我们如何使market_cap
字段可以为空。 如果我们没有,迁移将要求为所有现有行提供值(就像South一样):
You are trying to add a non-nullable field 'market_cap' to PriceHistory without a default;
we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows)
2) Quit, and let me add a default in models.py
现在再次运行./ management .py makmigration
将生成一个新的迁移文件0002_auto_
。
请记住,可以使用
./manage.py makemigrations --name
为迁移创建自定义名称
该文件应该是这样的:
class Migration(migrations.Migration):
dependencies = [
('historical_data', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='PriceHistory',
name='market_cap',
field=models.PositiveIntegerField(null=True),
preserve_default=True,
),
migrations.AlterField(
model_name='PriceHistory',
name='price',
field=models.DecimalField(max_digits=8, decimal_places=2),
),
]
请注意dependencies
列表,该列表声明我们必须在运行此迁移之前运行初始迁移。 此迁移还有两个操作 - AddField
,它创建我们新添加的market_cap
,以及AlterField
,它更新我们的价格字段的max_digits
。
重要的是要理解这些操作只调用迁移框架,该框架处理对settings.py
文件中定义的数据库执行各种操作。
开箱即用的迁移支持Django支持的所有标准数据库。 因此,如果坚持dependencies
项部分中列出的原语,可以手动创建所需的任何迁移,而无需担心底层SQL。 这一切都是为我们完成的。
查看迁移
即使不需要担心迁移生成的SQL,如果很好奇,Django已经为我们做好了准备——只需运行:
$ ./manage.py sqlmigration
这样做将根据settings.py
文件中的数据库列出将由指定的迁移生成的基础SQL好奇。 运行这几次,将真正开始欣赏迁移的力量!
结论
我们已经到达了另一个终点,但还有一个起点。在上一篇文章中,我们将讨论数据迁移。干杯!
数据迁移
原文by Real Python 翻译by StarMeow
再次回来。
迁移主要用于使数据库的数据模型保持最新,但数据库不仅仅是数据模型。 最值得注意的是,它也是一大堆数据。 因此,如果不讨论数据迁移,任何关于数据库迁移的讨论都是不完整的。
数据迁移定义
数据迁移在许多场景中使用。 两个非常受欢迎的是:
- 当希望加载应用程序所依赖的“系统数据”以成功运行时。
- 当对数据模型的更改强制需要更改现有数据时。
请注意,加载用于测试的虚拟数据不在上面的列表中。 可以使用迁移来执行此操作,但迁移通常在生产服务器上运行,因此可能不希望在生产服务器上创建一堆虚拟测试数据。
示例
继续之前的Django项目,作为创建一些“系统数据”的一个例子,让我们创建一些历史比特币价格。 Django迁移将帮助我们,通过创建一个空的迁移文件并将其放在正确的位置,如果我们键入:
$ ./manage.py makemigrations --empty historical_data
这应该创建一个名为historical_data/migrations/003_auto
的文件。 我们将名称更改为003_load_historical_data.py
,然后将其打开。 将拥有一个默认结构,如下所示:
# encoding: utf8
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('historical_data', '0002_auto_20140710_0810'),
]
operations = [
]
可以看到它为我们创建了一个基础结构,甚至插入了依赖项。 这很有帮助。 现在要进行一些数据迁移,请使用RunPython迁移操作:
# encoding: utf8
from django.db import models, migrations
from datetime import date
def load_data(apps, schema_editor):
PriceHistory = apps.get_model("historical_data", "PriceHistory")
PriceHistory(date=date(2013,11,29),
price=1234.00,
volume=354564,
total_btc=12054375,
).save()
PriceHistory(date=date(2012,11,29),
price=12.15,
volume=187947,
total_btc=10504650,
).save()
class Migration(migrations.Migration):
dependencies = [
('historical_data', '0002_auto_20140710_0810'),
]
operations = [
migrations.RunPython(load_data)
]
我们首先定义函数load_data
- 猜对了 - 加载数据。
对于一个真正的应用程序,我们可能想要访问
blockchain.info
并获取完整的历史价格列表,但我们只是在那里放了几个来展示迁移的工作原理。
一旦我们有了这个函数,我们可以从RunPython操作中调用它,然后当我们从命令行运行./manage.py migrate
时,将执行这个函数。
记下这条线:
PriceHistory = apps.get_model("historical_data", "PriceHistory")
在运行迁移时,获取所在迁移点相对应的PriceHistory
模型版本非常重要。在运行迁移时,模型(PriceHistory)可能会更改,例如,在后续迁移中添加或删除列。这可能会导致数据迁移失败,除非使用上面的行来获取正确的模型版本。有关这方面的更多信息,请参阅此处的评论。
这可能比运行syncdb
并让它加载一个fixture
更有用。事实上,迁移不尊重fixtures
- 这意味着他们不会像syncdb
那样自动加载它们。
这主要归功于哲学。
虽然可以使用迁移来加载数据,但它们主要是关于迁移数据和/或数据模型。我们已经展示了加载系统数据的示例,主要是因为它是如何设置数据迁移的简单说明,但数据迁移通常用于更复杂的操作,例如转换数据以匹配新数据模型。
一个例子可能是如果我们决定开始存储多个交易所的价格而不是一个,所以我们可以添加price_gox
,price_btc
等字段,然后我们可以使用迁移将所有数据从price
列移动到price_btc
列。
通常,在处理Django 1.7中的迁移时,最好将数据加载为迁移数据库的单独练习。如果确实想继续使用/加载fixture
,可以使用如下命令:
$ ./manage.py loaddata historical_data/fixtures/initial_data.json
这会将数据从fixture
加载到数据库中。
这不会像数据迁移那样自动发生(这可能是一件好事),但功能仍然存在;它没有丢失,所以如果有需要,请随时继续使用fixtures
。不同的是,现在需要时使用fixtures
加载数据。如果使用fixtures
加载单元测试的测试数据,请记住这一点。
结论
这与前两篇文章一起介绍了使用迁移时遇到的最常见方案。还有很多场景,如果很好奇并且真的想深入了解迁移,那么最好的去处(代码本身除外)就是官方文档。
请记住,在一般情况下,正在处理:
- 架构迁移:更改数据库或表的结构而不更改数据。这是最常见的类型,Django通常可以自动为我们创建这些迁移。
- 数据迁移:对数据的更改或加载新数据。 Django无法为我们生成这些。必须使用RunPython迁移手动创建它们。
因此,选择适合我们的迁移,运行makemigrations,然后确保每次更新模型时都更新迁移文件 - 这或多或少都是如此。这将允许将我们的迁移与代码一起保存在git中,并确保可以更新数据库结构而不必丢失数据。
快乐迁移!