Django_database migrations 数据库迁移及其相关若干问题/迁移问题排查/迁移和sql语句预览/添加non_null字段到模型/迁移版本回滚/重新执行(roll back)
文章目录
Django_database migrations 数据库迁移及其相关若干问题/迁移问题排查/迁移和sql语句预览
references
使用Django的个人习惯
- 一般的,数据库表设计完后就不易改动(所以在设计环境应该仔细和慎重)
- 至少不宜在敲定之前存放过多数据(存放少量测试数据)
- 数据库表名(模型名)需要严格统一,在python下编程,建议使用下划线分割不同单词(而不是驼峰命名法)
- 但是某些时候,(譬如需求的变更),我们需要修改数据库表
- 比较棘手的是,需要修改某个核心表的主键,更坏的情况是该属性还被作为其他表(Model)的外键,使得重构变得困难
- 及时的使用git打个快照,方便恢复项目
- 外键和其他数据库约束的设置
- 据了解,人们较多的认为,在数据库层面设置外键之类的约束对于性能是不友好的
- 实际操作中,使用其他约束逻辑来代替数据库上的约束
- 在绘制ER图的时候,应当要体现外键(如果合适的化)
- 关于主键:
- 如果使用的是mysql,那么主键默认情况下不区分大小写,这意味着,如果您的候选主键要求区分大小写的时候,可能就需要另外设置主键(譬如AutoField);(当然,也可以配置msql,使得其区分大小写(例如uft8-bin,但是据django文档介绍,该配置可能造成出乎意料的结果(对于ORM不友好))
- 此外,正如上面提到的,在django(等ORM)中,一旦外键关系复杂(或者使用较多),对于重构模型是很不利的
常用迁移命令(manage.py&django-admin)
- 如果您的项目基于虚拟环境建立的,那么每当执行
manage.py
等命令时都应该先激活虚拟环境,否则会出现出乎意料的执行结果
-
django-admin and manage.py | Django documentation | Django (djangoproject.com)
-
django-admin
andmanage.py
¶-
django-admin
is Django’s command-line utility for administrative tasks. This document outlines all it can do.(该命令不是某个django项目独有)- 该命令比较适合于需要在多个项目之间切换执行(环境变迁),执行时需要环境参数
-
In addition,
manage.py
is automatically created in each Django project.- 每个项目都有自己的
manage.py
脚本文件来执行命令 - 单一项目中首选
- It does the same thing as
django-admin
but also sets theDJANGO_SETTINGS_MODULE
environment variable so that it points to your project’ssettings.py
file.
- 每个项目都有自己的
The
django-admin
script should be on your system path if you installed Django viapip
.If it’s not in your path, ensure you have your virtual environment activated.
- Generally, when working on
a single Django project
, it’s easier to usemanage.py
thandjango-admin
. - If you need to switch between multiple Django settings files, use
django-admin
withDJANGO_SETTINGS_MODULE
or the--settings
command line option.
- The command-line examples throughout this document use
django-admin
to be consistent, - but any example can use
manage.py
orpython -m django
just as well.
-
$ django-admin <command> [options] $ manage.py <command> [options] $ python -m django <command> [options]
-
关于迁移的一些问题&参考
-
一日一技:如何让Django 的app migration重新与数据库同步
- 该问题涉及migaration 故障解决
-
I accidentally deleted the migrations folder in Django - Stack Overflow
-
How to Back Up and Restore a MySQL Database {Easy Tutorial} (phoenixnap.com)
清理migrations表
清理migrations实操
PS D:\repos\ELA\backEnd\ela> .\manage.py migrate --fake word zero Operations to perform: Unapply all migrations: word Running migrations: Rendering model states... DONE Unapplying word.0002_rename_uid_wordnotes_user... FAKED Unapplying scoreImprover.0001_initial... FAKED Unapplying word.0001_initial... FAKED PS D:\repos\ELA\backEnd\ela> pmg showmigrations word word [ ] 0001_initial [ ] 0002_rename_uid_wordnotes_user [ ] 0003_alter_wordnotes_user [ ] 0002_remove_wordnotes_uid_wordnotes_user [ ] 0004_merge_20220522_1411 PS D:\repos\ELA\backEnd\ela> cd .\word\migrations\ PS D:\repos\ELA\backEnd\ela\word\migrations> ls Directory: D:\repos\ELA\backEnd\ela\word\migrations Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 2022/5/22 14:12 __pycache__ -a--- 2022/5/19 17:57 0 __init__.py -a--- 2022/5/19 18:58 3432 0001_initial.py -a--- 2022/5/22 14:09 700 0002_remove_wordnotes_uid_wordnotes_user.py -a--- 2022/5/22 14:11 365 0002_rename_uid_wordnotes_user.py -a--- 2022/5/22 14:09 613 0003_alter_wordnotes_user.py -a--- 2022/5/22 14:11 300 0004_merge_20220522_1411.py PS D:\repos\ELA\backEnd\ela\word\migrations> rm 00*;ls Directory: D:\repos\ELA\backEnd\ela\word\migrations Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 2022/5/22 14:12 __pycache__ -a--- 2022/5/19 17:57 0 __init__.py
-
可以自助查看一下刚才用到的
migrate
的帮助手册 -
python manage.py migrate -h
django 反复提示talbe already exsit
- 这种容易情况发生在有多个数据库连接配置的情况
- 如果您已经将提示已存在的表手动的删除,还是会提示,那么可能是您手动操作的数据库和当前django连接(启用的数据库不是同一个!)
django 迁移的基本特点说明
-
Migrations | Django documentation | Django (djangoproject.com)
-
How to create database migrations | Django 文档 | Django (djangoproject.com)
-
django 的迁移功能(模型-数据库同步功能是方便的,但是相应的,某些复杂的情况下,这种自动化还不是那么智能和理想),
- 譬如,为模型添加一个具有unique约束的字段(无论被修改结果的数据库中是否还有数据),这种操作都需要更加费心:
- 到了手动维护migrations 文件时,您需要比较了解django和数据库操作之间的对应关系,否则维护将难以进行
-
正是由于makemigration在
生成迁移文件的时候仅仅比较本地的模型变化
以及已有的迁移文件
,而不会对比数据库的结构
,比较适合单向同步
(统一由django控制模型,而直接修改数据库表结构 )
模型迁移的部分执行导致的错误
- django 的迁移不像原子性事务,某一次迁移复杂任务的执行可能前部半部分是顺利的(
只执行了前半部分的任务
),但是后半部分却发生错误,导致实际数据库表结构发生不完整变更(导致无法迁移回滚
,陷入进退两难的境地),因此,执行稍微复杂的迁移任务前,请务必慎重,甚至要读一下生成的migrations文件 - 一般的,每次改动不应该过大;譬如,我们想要将一个字段更改为外键(带有外键约束),那么就不要同时修改字段名,否则数据库的字段可能被删除了,但是期待的外键字段没有生成(这种情况下执行的不是字段更名(rename),而是被django解释为:移除旧有字段,并增加一个新字段,这个新字段带有外键约束
- 譬如我的一次失误操作:(同时更名以及添加外键约束)
migrations.RemoveField( model_name='wordnotes', name='uid', ), migrations.AddField( model_name='wordnotes', name='user', field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.DO_NOTHING, to='user.user'), preserve_default=False, ),
添加独一无二字段的迁移(add unique OR non-null 字段的方法
- 备份数据后在导入到新数据模型是相对万能的办法,
- 但是下面会引入一些变通的方法试图,稍作修改就能够达到migration Model schema的同步目的
add unique 字段
如果当前模型已有的数据不想删除,并且不想备份后从新导入到新模型中,那么还有如下变通方法(操作相对简单)
额外的可能的方案
- 为当前创建一个普通字段(模型属性),该字段要求宽松(没有unique约束,并且,我们可以为其选择一个默认值(根据情况,转换类型为 字符串/数值)
- 迁移完成后,数据库中有相应字段
- 通过编写脚本来修改这个新字段(可以导入你准备好的互不相同的数据列表,或者借助算法(生成一批不同的值
- (最简单的就是range(size))生成
- 如果需要字符串,那么在用str()函数转一下类型
- 现在,我们再做一次模型修改(增加unique约束),并且再执行一遍迁移(使得unique约束对后续的数据库操作生效)
误删migrations文件夹
一般的,django 执行迁移操作前会检查数据库中的
django-migrations
表;并结合项目中的
migrations中的迁移文件
来执行同步操作
- 某些复杂的模型改动会导致makemigrations无法正确的反应我们的修改,导致数据库无法同步模型的更改
- 粗暴的做法是使用
migrate --fake zero
来清空对应该模型迁移再django-migrations
的中的记录(此清空操作不会影响数据库中的其他表的数据)
如果误删migrations目录,那么手动创建一个migrations目录(并在里面创建空文件
__init__.py
或许是有效的方法
然而,有些时候情况会比较复杂,比如其他的模型以往的迁移过程依赖于这个被删除的migrations目录中的某个迁移文件,将导致无法直接重建被删除的数据库(因为在尝试执行makgemigrations
是,django会一并检查其他
app的模型(表的迁移依赖是否正常)
(ll_env) PS D:\repos\IdeaProjects\djangoProjects\ela_old\ela> pmgmk Migrations for 'blog': blog\migrations\0026_alter_entry_body_text.py - Alter field body_text on entry Migrations for 'words': words\migrations\0001_initial.py - Create model WordNotes - Create model Words - Create model NeepWordsReq - Create model Cet6WordsReq - Create model Cet4WordsReq 某些复杂的场合,甚至需要手动修改迁移文件(migrations中的文件)
django迁移问题的暴力解决方案
添加 non-null字段到模型
How to Solve You are trying to add a non-nullable field to without a default (pytutorial.com)
- 如果迁移时报错
It is impossible to add a non-nullable field ‘id’ to wordsearchhistory without specifying a default. This is because the database needs something to populate existing rows.
- 且,如果要更换的是主键,那么简单的设置字段构造器的参数是无法解决的,但是,如果被更换的旧主键字段不是其他模型中的外键,那么可以通过下述步骤来重新建立模型&建表:
- Deleting and returning the model
Before talking about this way, I’d to warn you that this way will delete your model data.
so you need to delete your model class in models.py then migrate your app models, after migrations return back the model and migrate the model
- Deleting and returning the model
- backup Model & Delete Model(备份以下模型代码,如果已经有数据,可以一同备份数据,然后从models.py中将该模型类删除掉) =>
- Makemigrations Models =>
- Migrate Models =>
- Return Back the models(重新将模型写入到models.py) =>
- Makemigrations Models =>
- Migrate Models
如果不出意外,可以看到模型&数据库表同步完成
虚拟外键
- 将外键行设置为虚拟外键
(db_constraint=False)
- 例如:
user = models.ForeignKey(User, on_delete=models.DO_NOTHING, db_constraint=False)
- 例如:
- django migration出来的sql操作并不够专业(比较粗糙)(这也是Django的模型通用性带来的粗糙特征))
- 专门的DBA设计出来的表格更加专业,这使得我们对于Migration的使用不那么依赖
- 只需要注意模型中的表名字/字段和DBA的表相匹配即可操作表
- 当然,不迁移也是可以操作数据库的
- 换句话说,生产环境中,我们根据DBA建立的专业的数据库表来编写对应的Django模型.
- 尽管如此,迁移意味着我们可以回滚数据库,但这种行为本身就是代价高的,争取早期的完善设计&数据备份会更加通用
某些同步问题着实令人沮丧
- 可以采取如流程恢复同步:
数据库表数据备份(重命名旧表可以当作是备份,如果想直接删除旧表重建也行)
备份最新模型代码(models.py)
清理已有的migrations迁移文件
检查app当前的迁移情况
python manage.py showmigrations <app-name>
- 如果不指定
app-name
,将会列出所有app的migration情况清除所有迁移记录:
python manage.py migrate --fake <app-name> zero
- 重新
showmigrations
来检查清除的情况showmigrations
会检查app中的migrations
目录来提供报告
删除migrations目录内部的所有py文件(仅保留
__init__.py
)重新执行迁移:
- 确保前面数据库旧表的重命名或者(备份后删除)
python manage.py makemigrations <app-naem>
python manage.py migrate <app-name>
- 上述操作将尝试通过django创建全新的数据库表
- 最后登录到数据库检查新表(gui工具的话需要手动刷新)(命令行查看更加及时)
原生的使用django来创建数据库库表,比较不容易出错
对于已经有数据的表,将数据备份出来,再做导入
这是对于项目时间紧迫的情况下的策略,
从认真学习的角度,应该通过了解migratetion的工作方式采取更好的方法来处理冲突和同步问题
Django migration
Migrations
简介
- Migrations are Django’s way of
propagating changes
you make to yourmodels
(adding a field, deleting a model, etc.)into your database schema.
- They’re designed to be
mostly automatic,
but you’ll need to know
- when to make migrations,
- when to run them, and the common problems you might run into.
makemigrations & migrations文件
migrations 文件
¶Django migrations file| djangoproject.com
- You should rarely, if ever, need to edit migration files by hand, but it’s entirely possible to write them manually if you need to.
- Some of the more complex operations are not autodetectable and are only available via a hand-written migration, so don’t be scared about editing them if you have to.
The Commands for check migrations(迁移状态&故障排查工具)
There are several commands which you will use to interact with migrations and Django’s handling of database schema:
migrate
, which is responsible forapplying and unapplying migrations
.makemigrations
, which is responsible forcreating new migrations
based on the changes you have made to your models.
sqlmigrate:检查migrations对应的sql操作
references
-
Model Meta options | Django documentation | Django (djangoproject.com)
-
sqlmigrate
, whichdisplays the SQL statements for a migration
.
sqlmigrate概述
-
这个工具很有用,当您发现某些创建表的迁移并没有安装预期的方式工作,那么很可能是django没有将migration中的任务转换为正确的mysql语句
-
本命令需要连接数据库(而不仅仅是根据本地的模型迁移),来生成对应数据库种类的sql语句
这经常是Model Meta中的配置字段managed
的取值问题
- 尤其是这些模型是您从类似于mysql数据导出的时候,需要特别关注.
sqlmigrate
¶
Model Meta options | Django documentation | Django (djangoproject.com)
python manage.py sqlmigrate app_label migration_name
¶
注意,执行次命令的时候是以
python manage.py
打头来启动(如果想不指定项目的话)如果使用
django-admin
,而没有指定环境参数,则会导致报错
raise ImproperlyConfigured( django.core.exceptions.ImproperlyConfigured: Requested setting TEMPLATES, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings. Prints the SQL for the named migration.
This requires an active database connection, which it will use to resolve constraint names;
this means you must generate the SQL against a copy of the database you wish to later apply it on.
Note that
sqlmigrate
doesn’t colorize its output.sqlmigrate 的选项:
Specifies the database for which to generate the SQL. Defaults to
default
.
例如
python manage.py sqlmigrate polls 0001
(ll_env) PS D:\repos\IdeaProjects\djangoProjects\ela_old\ela> py manage.py sqlmigrate word 0001 -- -- Create model Word -- -- -- Create model WordNotes -- -- -- Create model Cet4WordsReq -- CREATE TABLE `cet4_words_req` (`wordOrder` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `spelling` varchar(255) NOT NULL); -- -- Create model Cet6WordsReq --
migrations 中的迁移文件内容示例
migrations.CreateModel( name='Word', fields=[ ('wid', models.AutoField(primary_key=True, serialize=False)), ('spelling', models.CharField(max_length=255)), ('phonetic', models.CharField(blank=True, max_length=255, null=True)), ('plurality', models.CharField(blank=True, max_length=255, null=True)), ('thirdpp', models.CharField(blank=True, max_length=255, null=True)), ('present_participle', models.CharField(blank=True, max_length=255, null=True)), ('past_tense', models.CharField(blank=True, max_length=255, null=True)), ('past_participle', models.CharField(blank=True, max_length=255, null=True)), ('explains', models.TextField(blank=True, null=True)), ], options={ 'db_table': 'words', 'managed': False, }, ), migrations.CreateModel( name='WordNotes', fields=[ ('id', models.BigAutoField(primary_key=True, serialize=False)), ('wordspelling', models.CharField(blank=True, db_column='wordSpelling', max_length=255, null=True)), ('uid', models.IntegerField(blank=True, db_column='UID', null=True)), ('content', models.CharField(blank=True, max_length=255, null=True)), ('difficulty_rate', models.IntegerField(blank=True, null=True)), ], options={ 'db_table': 'word_notes', 'managed': False, }, ),
- 出现sql语句为空的create操作正是由于option中managed取值所造成的
- 但是建议您从Model中修改,然后重新makemigrations
- 可以配合下方的
showmigrations
命令中的列表来查看指定某个变迁所对应的sql语句映射 - 一般的,initial文件中记录的是初次依照app中的各个模型来创建对应的数据库表的create语句
showmigrations
showmigrations
, which lists a project’s migrations and their status.
注意,执行此命令的时候是以
python manage.py
打头
如果使用
django-admin
会导致报错
raise ImproperlyConfigured( django.core.exceptions.ImproperlyConfigured: Requested setting TEMPLATES, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings. 以app 为单位进行显示(我就
python manage.py
设置函数别名为:pmg
)
cxxu➜~/djangoProjects/ela(main✗)» pmg showmigrations [18:57:33] 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 contenttypes [X] 0001_initial [X] 0002_remove_content_type_name scoreImprover [X] 0001_initial sessions [X] 0001_initial user [X] 0001_initial [X] 0002_remove_user_test_alter_user_examdate_and_more [X] 0003_alter_user_examdate_alter_user_examtype words [X] 0001_initial
migrate(简介)
-
django-admin and manage.py | migrate and options
- 查阅文档,以了解migrate 的各种选项和含义
-
该操作可以执行
makemigrations
产生的迁移文件,从而同步模型和数据库(表)结构
manage shell:检查django和数据库的连接
-
利用
python manage.py shell
来让django启动一个交互式python终端-
在里面可以快速的检查数据库对接情况
-
以及模型定义的字段和参数是否符合预期(譬如默认值)
-
(ll_env) PS D:\repos\ELA\backEnd\ela> pmg shell Python 3.10.0 (tags/v3.10.0:b494f59, Oct 4 2021, 19:00:18) [MSC v.1929 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from user.models import User,UserInfo >>> >>> User.objects.create(name="testScriptUser") <User: [5, 'testScriptUser', 0, '1970-01-01', '4', '1970-01-01']> - 交互式脚本可以专门建立一个目录在里头创建脚本文件编写,以便利用IDE的补全功能
-
models.py文件与模型构建
- models.py 是定义数据库表结构的python文件
models.py不要放置非Model& subClass定义的其他代码,多余的代码会导致makemigrations 出现问题.(譬如提示某数据库表格不存在等问题)
为模型编写
__str__
函数是一个好习惯
譬如
def __str__(self): s=self return str([s.uid,s.name,s.signin,s.examdate,s.examtype,s.signupdate]) 可以在打印模型对象(一条记录)的时候看到模型中包含的主要内容
字段的默认值
- 字段的默认值似乎无法体现在数据库中,而是体现在django中的模型
- 推测应该是在插入记录的时候,django会自动视情况给模型中设置了default参数的对象字段赋值
- 而且,应该以django中的默认值准(覆盖数据库中的默认值)
- 换句话说,django中设置的默认值仅仅对于django自己操作数据库时才生效,不影响原生数据库中为字段设置的默认值.
外键
- 外键(多对一),在MySQL数据库中的表的对应的sql描述中没有ForeignKey 约束
- 默认的,会建立索引(Create Index)
- 约束有django模拟
makemigrations
- 该命令可以检查django app的
models.py
文件中是否发生变更,并生成对应的数据库变迁操作,来同步数据库;- 这些检查出来的变化所对应的模型操作会记录在文件中,存放在app的
migrations
目录下
- 这些检查出来的变化所对应的模型操作会记录在文件中,存放在app的
- 在执行该命令的时候,应当加上具体的app 名称,以保证makemigrations 过程能够正确执行
从已有的mysql数据库迁移到django项目中(django import exsited databases from mysql)
- 曾有过此需求,等有空实践一下
reference1
从django中创建模型并同步到尚且为空的某种数据库(譬如mysql/postgre)中这种方向是简单的
但是某些时候,我们的表在数据库中已将存在,而想让后来创建的django来对接并能够应用django.db模型来管理(同时应用上迁移(migration)等特性),需要注意若干问题
New apps come preconfigured to accept migrations, and so you can add migrations by running
makemigrations
once you’ve made some changes.If your app already has models and database tables, and doesn’t have migrations yet (for example, you created it against a previous Django version), you’ll need to c
onvert it to use migrations
by running:如果是直接从mysql导出,也需要先将mysql数据库导出为django Model(inspectdb命令)
$ python manage.py makemigrations your_app_label
- This will make a new initial migration for your app.
- Now, run
python manage.py migrate --fake-initial
, and Django will detect that you have an initial migration and thatthe tables it wants to create already exist
, and will mark the migration as already applied.- (Without the
migrate --fake-initial
flag, the command would error out because the tables it wants to create already exist.)Note that this only works given two things:(仅在满足以下条件时,上述操作才有效)
- You have
not changed your models
since you made theirtables
. For migrations to work, you mustmake the initial migration *first*
and then make changes, as Django compares changes against migration files, not the database.- You have not manually edited your database - Django won’t be able to detect that your database doesn’t match your models, you’ll just get errors when migrations try to modify those tables.
确保initial(–fake-initial)
reference2
- How to Connect Django to an Existing Legacy Database | DevRa (rafed.github.io)
- Get an existing database
- Import the data to MySQL
- Setup Django project to work with MySQL
- Create the Django models from MySQL
- Create app and use the models
- Modifying database tables
Create the Django models from MySQL
The above below will produce django models in models.py from tables present in MySQL.
$ python manage.py inspectdb > models.py
Modifying database tables
- Legacy tables are not permitted to be modified by default.
- But they can be modified if needed.
- First set managed=True in sub Meta classes of classes you want to modify.
- And then, add the new field you want.
class Meta: managed = True db_table = 'customers'
Now, make the migrations file.
python manage.py makemigrations
Since the tables already exist, we need a fake migration.
python manage.py migrate --fake
Voila! Now the new table field should be added!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· DeepSeek “源神”启动!「GitHub 热点速览」
· 上周热点回顾(2.17-2.23)