迁移相关
1 简述
迁移 是 django将你对models.py文件的修改(增添字段、增删模型 > 改变数据库表结构的代码操作)应用到数据库结构中的方式,它是自动完成的。
迁移 是数据库架构(数据库中的表格)的版本控制系统,迁移共2步,先用 makemigrations
将模型修改打包到肚里的迁移文件中, migrate
将修改同步到数据库
2 命令
makemigrations
:基于模型的修改生成迁移脚本,在当前引用的migrations
目录下migrate
:应用和撤销迁移sqlmigrate
:查看迁移脚本的内容,里面是迁移使用sql语句showmigrations
:列出项目的迁移和迁移状态
3 后端相关
3.1 mysql
- MySQL 缺乏对架构变更操作相关事务的支持,这意味着如果迁移失败,你将必须手动取消更改才能重试(无法回滚到较早的时间)
- MySQL 几乎每一次架构操作都会完全重写表,增加或删除列需要的时间与表的行数成正比,在一个有几百万行的表中添加几列,可能会让你的网站锁定十几分钟。
- 对列、表和索引的名称长度有较小限制,意味着在其他后端上创建的索引将可能无法在mysql下创建
3.2 pt sql
- 在架构支持方面是所有数据库中是最强的。
- 建议你始终使用 null=True 创建新列,因为这样可以立即添加它们(在 PostgreSQL 11 之前,添加具有默认值的列会导致表的完全重写,时间长短与表的大小成正比)
3.3 sqlite
不建议你在生产环境中运行和迁移 SQLite,
Django 自带的支持是为了让开发人员在本地计算机上使用 SQLite 来开发较不复杂的 Django 项目,而无需完整的数据库。
4 工作流程
- 修改模板(添加一个字段、删除一个模型)
- 生成迁移文件用于改变数据库表结构:
python manage.py makemigrations
- makemigrations工具开始扫描最新模型与迁移文件版本比较,基于之前的版本生成新的迁移文件。
- 改变迁移文件名:python manage.py makemigrations --name changed_my_model your_app_label
- 改变数据库表结构:
python manage.py migrate
一旦我们应该用了迁移文件,那么应该将迁移和模型的更改作为一个单一的提交,这就像版本控制系统。
- 版本控制
其他人也提交了迁移文件,编号跟我相同,django会自动线性化两个迁移。
5 事务
sqllite、ptsql中,所有迁移操作默认在一个事务中运行,通过atomic属性设置False防止迁移在事务中运行,例如:
from django.db import migrations
class Migration(migrations.Migration):
atomic = False
6 依赖
你将B应用迁移到数据库成功创建了foreignkey引用的表,而A应用要想迁移到数据库成功,会使用这张表,所以B应用的迁移在A应用之前。存在一些这样的依赖关系
7 迁移文件
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Question',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('question_text', models.CharField(max_length=200)),
('pub_date', models.DateTimeField(verbose_name='data published')),
],
),
migrations.CreateModel(
name='Choice',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('choice_text', models.CharField(max_length=200)),
('votes', models.IntegerField(default=0)),
('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='polls.question')),
],
),
]
django加载迁移文件,会在当前应用的migrations下寻找 django.db.migrations.Migration
的子类,这个子类有个固定命名 Migration
,然后
检查类的四个属性,经常用到2个:
- dependencies 所依赖的迁移列表
- operations 是列表,包含了此次迁移操作的 Operation 类。
operations 是一组指令,指导了如何变更数据库表结构。django扫描它并构建所有应用的所有架构变更,然后使用它生成架构变更的原生sql语句。django运行时
makemigrations时候,会与你上次生成文件进行比较,最终计算出你改变了什么表结构。
自定义字段(ing)
模型管理器(ing)
初始迁移(ing)
历史一致性(ing)
8 向应用添加迁移(ing)
python manage.py makemigrations your_app_label
python manage.py migrate --fake-initial
9 撤销迁移
- 撤销上一次迁移,例如输入你的迁移编号0002:
python manage.py migrate books 0002
- 撤销一个应用的所有迁移:
python manage.py migrate books zero
10 历史模型(ing)
11 删除模型字段的注意事项(ing)
class IPAddressField(Field):
system_check_deprecated_details = {
'msg': (
'IPAddressField has been deprecated. Support for it (except '
'in historical migrations) will be removed in Django 1.9.'
),
'hint': 'Use GenericIPAddressField instead.', # optional
'id': 'fields.W900', # pick a unique ID for your field.
}
12 数据迁移
迁移还可以用来改变数据库本身的数据:
- 制作一个可以使用的空迁移文件:
python manage.py makemigrations --empty yourappname
- 编写一个迁移,使用 first_name 和 last_name 的组合值填充新的 name 字段,使用历史模型进行迭代:
from django.db import migrations
def combine_names(apps, schema_editor):
Person = apps.get_model('yourappname', 'Person')
for person in Person.objects.all():
person.name = '%s %s' % (person.first_name, person.last_name)
person.save()
class Migration(migrations.Migration):
dependencies = [
('yourappname', '0001_initial'),
]
operations = [
migrations.RunPython(combine_names),
]
13 压缩迁移
压缩迁移:将一组现有的多个迁移减少到一个迁移,这些迁移仍然代表相同的更改
自定义迁移名称: squashmigrations --squashed-name
$ ./manage.py squashmigrations myapp 0004
Will squash the following migrations:
- 0001_initial
- 0002_some_change
- 0003_another_change
- 0004_undo_something
Do you wish to proceed? [yN] y
Optimizing...
Optimized from 12 operations to 7 operations.
Created new squashed migration /home/andrew/Programs/DjangoTest/test/migrations/0001_squashed_0004_undo_something.py
You should commit this migration but leave the old ones in place;
the new migration will be used for new installs. Once you are sure
all instances of the codebase have applied the migrations you squashed,
you can delete them.