热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

djangoorm原理_Djangomigration原理

Django自带了一套ORM,就是将数据库的表映射成Python的Class,这样我们在写程序的时候,就基本不需要写SQL和数据库交互了&

Django 自带了一套 ORM,就是将数据库的表映射成 Python 的 Class,这样我们在写程序的时候,就基本不需要写 SQL 和数据库交互了,直接操作 Python 对象就可以了,Django 的 ORM 把我们对类的操作,转换成对数据库的 SQL 操作。

那么 Class 的属性是如何对应到 Table 的 Column 的呢?Class 的属性变了,如何关联到表结构的变化?本文就介绍这种 migration 实现的原理。

参与 Class 和 Table 结构一致的,一共有3个角色:

Models:只记录了现在的 Model 是什么样子的;

Migrations:记录了 Model 是如何从最初的样子,变成现在这个样子的——即要变成现在的样子,需要走哪几步;

数据库中的 django_migrations 表:记录在 Migrations 的所有步骤中,当前的 Table 已经执行了哪几步,这样就可以只执行没有执行过的步骤,来变成最新的状态;

一次 migration 之旅

前面我们说过,Django ORM 基本屏蔽了用户需要做的 SQL 操作,包括 DDL 操作。

从一个用户的角度看,我们做一次 migration,只需要3步:

修改 models.py,比如添加字段;

执行 python manage.py makemigrations ;

执行 python manage.py migrate ;

这时候,数据库中的 Table 和我们 Django 中的 Model 就对上了,我们写业务逻辑就可以了。

但实际上,Django 在背后做了什么呢?如果不了解这些,遇到一些问题就束手无策了(文末会提到一些经典的问题)。

在整个流程中,Django 负责的是自动识别出 Model 进行了哪些变化,将这些变化应用到 Table 中,保证最终 Model 和 Table 还是对应的。本质上,就是将 py 文件的变化,转成数据库的 DDL 变化。

所以首先,Django 要知道你的 Model 做了哪些变化。这是第2步:生成 migrations。Migrations 生成的方式,颇有点现在“声明式”的意思。

它的过程是这样的(这里我们将修改后的 Model 记作 Model-2,修改前的 Model 记作 Model-1):从 migrations/ 文件夹下的 0001_xxx.py 开始 apply,到 0002_xxx.py,一直到最后一个文件,这些文件都记录了 Model 的变化,都 apply 一遍之后,就得到了修改前的的 Model-1 的状态,然后和修改过的 Model-2 来比较,得到了 diff,然后看如何消除这些 diff——即产生最新的 migrations 文件,将 Model-1 的状态转移到 Model-2.这样新的 migrations 就生成了,如果你再跑一遍的话,就发现什么 migrations 也不会出现,因为 migrations 文件记录的变化已经和最新的 Model 一样了(声明式天生的幂等性!)。

这些 migrations 记录了 Model 从 0001_init 开始的所有变化,有了 migrations 文件,你就可以从一个空的数据库,变成现在的结构了。

但是如果数据库不是空的,而是已经有一些结构呢?通常是这种情况:生产环境的数据库,表结构是 v3(执行过migrations 0001,0002,0003),你在开发的时候生成了 migrations 0004,0005。那么怎么知道这个数据库只要执行 0004,0005 就可以了呢?更复杂一点的话,假如你有一个灰度服务器,一个生产服务器,灰度服务器执行过了 0001-0004,生产服务器落后一个版本,执行了0001-0003,怎么知道这些数据库应该执行哪些 migrations 呢?

这里根本的问题是:migrate 的执行(DDL的执行)不是幂等的。所以我们就需要一个地方,记录一下哪些 migrations 已经被执行过了。Django 会自动在你的数据库中建立一张 django_migrations 表,用来记录执行过的 migrations。这样你在执行 python manage.py migrate 的时候,django 会去对比 django_migrations 表,只执行没有执行过的 migrations,无论你有多少表结构版本不同的数据库都没有关系。

下面再用一张图来表示一下整个过程。其实想明白了很简单(我也不知道为啥这张图画出来咋这么复杂,点开可以看大图)。这张图是给一个很简单的 User Model 增加了一个字段。注意一开始蓝色的 Model 和 Table 是 map 的,最终状态红色的 Model 和 Table 也是 map 的。

一些骚操作

有了上面的知识,我们处理起来一些问题就得心应手啦!

1.migrate执行到一半失败了怎么办?

这种情况是很容易发生的,因为 make migrations 的时候,Django 只知道消除 diff,并不知道表结构。这就很有可能导致生成的 migrations 实际上是违反数据库约束的,而不能执行成功。

最糟糕的情况是,一个 migrations 中有很多步,其中部分执行成功了。就造成数据库的结构处于一个“未知”的状态。

不过不要慌,只要从 log 中看下哪些 migrations 步骤已经执行了,去手动 revert,然后从 django_migrations 删除这些记录即可。参考这篇文章。

2.migrations应不应该加入到 git 仓库中?

应该!虽然是生成的代码,但是也是要用版本管理,让所有的数据库都执行一套 migrations!看看我的教训吧。

3.我什么 models 都没改,但是每次 make migrations 都会生成新的?

不要慌,我也遇到过。这时候你去看下生成的 migrations 做了啥操作,再想想 Django 为什么会这样做即可。

举个例子吧,我之前的 models 里面一个 default 值用了字典,我们知道字典 key 是没有顺序的(Python 3.7)之前,就导致每次 make migrations 的时候,顺序都有几率不一样,Django 就认为状态不一致,需要新的 migration 文件来消除 diff。

4.migrations 文件冲突了,我和同事开发的时候,我生成了一个 0003_xxx.py 我同事也生成了这个 0003.

不要慌,这是正常现象。按照 Django 的提示,执行生成一个新的 migrations 来 merge 前面的两个就好啦。python manage.py makemigrations --merge 。

5.我的migrations太多了,每次 makemigrations 都要等很久,或者我的 migrations 文件名已经快到 999_xxx.py 了!

我一生中只遇到过一个这样的项目。解决方法也很简单,因为我们只需要一个最终的状态,所以我们可以将之前的 migrations 都删掉。

步骤如下:

备份整个项目和数据库(非常重要);

删除除了 __init__.py 之外的所有 migrations 文件夹下的文件:ls */migrations/*.py | grep -v __init__ | xargs rm;

重新生成 migrations 文件,不出意外的话,所有的 app 都只有一个 migration 文件,就是 init;

删除数据库 django_migrations 表中所有的记录:delete from django_migrations;

因为我们的表结构已经是最新的了,所以新生成的 init migrations 并不需要执行。所以我们插入记录到 django_migrations 中,骗 djagno 我们已经执行过了。用这个命令:python manage.py migrate --fake;

大功告成,不出意外,makemigrations 又快了起来。

其他的一些骚操作

如何查看一个 migration 文件对应的 SQL 是啥?

1

2

3

4

5

6

7

$pythonmanage.pysqlmigrateusers0001

BEGIN;

--

--CreatemodelUsers

--

CREATETABLE"users_users"("id"integerNOTNULLPRIMARYKEYAUTOINCREMENT,"name"varchar(33)NOTNULL,"age"integerNOTNULL,"gender"varchar(8)NULL);

COMMIT;

如何查看这个数据库执行过哪些migrations了?

1

2

3

4

5

6

$pythonmanage.pyshowmigrationsusers

users

[X]0001_initial

[X]0002_auto_20190803_1549

[]0003_auto_20190803_1550

[]0004_auto_20190803_1550

最后 n 次执行的 migrations 有问题,数据库炸了,要不要删库跑路?

不用。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

➜pythonmanage.pymigrate

Operationstoperform:

Applyallmigrations:admin,auth,contenttypes,sessions,users

Runningmigrations:

Applyingusers.0003_auto_20190803_1550...OK

Applyingusers.0004_auto_20190803_1550...OK

➜pythonmanage.pymigrateusers0002

Operationstoperform:

Targetspecificmigration:0002_auto_20190803_1549,fromusers

Runningmigrations:

Renderingmodelstates...DONE

Unapplyingusers.0004_auto_20190803_1550...OK

Unapplyingusers.0003_auto_20190803_1550...OK

➜pythonmanage.pyshowmigrationsusers

users

[X]0001_initial

[X]0002_auto_20190803_1549

[]0003_auto_20190803_1550

[]0004_auto_20190803_1550

其他一些类似 Django ORM 的 migrations 的项目:



推荐阅读
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • HDFS2.x新特性
    一、集群间数据拷贝scp实现两个远程主机之间的文件复制scp-rhello.txtroothadoop103:useratguiguhello.txt推pushscp-rr ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • GreenDAO快速入门
    前言之前在自己做项目的时候,用到了GreenDAO数据库,其实对于数据库辅助工具库从OrmLite,到litePal再到GreenDAO,总是在不停的切换,但是没有真正去了解他们的 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • Oracle10g备份导入的方法及注意事项
    本文介绍了使用Oracle10g进行备份导入的方法及相关注意事项,同时还介绍了2019年独角兽企业重金招聘Python工程师的标准。内容包括导出exp命令、删用户、创建数据库、授权等操作,以及导入imp命令的使用。详细介绍了导入时的参数设置,如full、ignore、buffer、commit、feedback等。转载来源于https://my.oschina.net/u/1767754/blog/377593。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
author-avatar
helenheling2007895
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有