1
概述前文"django的使用及原理介绍[1]-初始Django"中介绍了如何使用Django搭建一个数据库管理的应用。其中介绍了如何通过model创建表/表字段,如何通过api添加/更新下删除表记录,在这个过程中,全程没有介绍任何关于mysql的操作。能让Django对数据库表进行管理而不需要写一行mysql语句,最大的功臣就是ORM(Object Relational Mapping, 对象管理映射)。本文结合ORM介绍migration的机制和原理。
2ORM是什么在大学学编程时老师提到最多的就是要面向对象编程(Object Oriented Programming, OOP),面向对象编程是一种程序设计思想,其将所有的实体作为对象,一个对象包含了数据和对数据的操作(函数)。当前最流行的是关系型数据库,关系型数据库包括实体及实体之间的关系,实体之间的关系也可以使用对象进行表达,这样就将关系型数据库与面向对象编程的对象有了一一对应的关系。而ORM 就是通过实例对象的语法,完成关系型数据库的操作的技术。ORM将数据库映射成对象的关系如下:
数据库的表(table) --> 类(class)
记录(record,行数据)--> 对象(object)
字段(field)--> 对象的属性(attribute)
数据库的操作则映射为类方法,由类方法完成数据库的CRUD操作。正式因为ORM使用对象封装了数据库的所有操作,因此可以无需使用任何的SQL语句只需使用面向对象编程,完成与数据对象的交互。总结起来ORM的优点包括:
数据模型统一集中定义,更加容易更新和维护,也利于重用代码
开发是面向对象的,有利于提高开发效率,降低开发成本。
很多框架提供了现成ORM,很多功能能够直接使用。
ORM是基于MVC架构的,按该架构开发会使代码更清晰。
降低了对SQL的使用门槛,避免编写性能不佳的SQL。
当然ORM也有缺点:
对象到关系型数据库的映射需要消耗系统性能,性能不如原生SQL。
在多表查询时,会使ORM变得很复杂。
ORM抽象了数据库层,开发者无法定制一些特殊的SQL操作。
但整体而言,使用ORM机制在实际的开发中,会大大提升开发的效率,缩短开发周期,因此也受到广大开发的青睐。
3 Django的migration过程 在前面分享的文章"django的使用及原理介绍[1]-初始Django"中,在启动服务之前需要执行如下命令:python manage.py makemigrations bm # 在bm模块下生成migrations文件夹,包含0001_initial.py文件。python manage.py migrate bm # 将0001_initial.py的变更更新到数据库中。
这两条命令就是将在bm/models.py中定义的对象映射到数据库表。在bm/models.py中定义的类(Class)对应数据库中的表(Table)。这里以Title类(表)为例介绍,models.py中Title类定义如下:
class Title(models.Model): STATUS_ENABLED = 0 STAUS_DISABLED = 1 STATUS_CHOICE = ( (STATUS_ENABLED, 'enabled'), (STAUS_DISABLED, 'disabled') ) name = models.CharField(max_length=128, verbose_name='title') created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) status = models.SmallIntegerField(choices=STATUS_CHOICE, default=STATUS_ENABLED)
执行“python manage.py makemigrations bm”后会将应用下models.py中的类(继承了django.db.models.Model)生成对应的migrations文件,若是第一次执行,则会在bm/migrations下生成0001_initial.py文件,如下:
0001_initial.py中生成的Title相关内容如下:class Migration(migrations.Migration): initial = True dependencies = [] operations = [ migrations.CreateModel( name='Title', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('status', models.SmallIntegerField(choices=[(0, 'enabled'), (1, 'disabled')], default=0)), ('name', models.CharField(max_length=128, verbose_name='title')), ], options={ 'ordering': ('created_at',), }, ) ]
若类Title中没有定义id属性,则生成的migrations文件会自动添加id字段,默认是int型,若定义了id属性,则优先使用用户定义的id字段,可以将id定义为uuid类型(
id = models.UUIDField()
)。若是已经生成了
0001_initial.py文件,然后对Title进行变更,比如增加desc字段,如下:
class Title(models.Model): STATUS_ENABLED = 0 STAUS_DISABLED = 1 STATUS_CHOICE = ( (STATUS_ENABLED, 'enabled'), (STAUS_DISABLED, 'disabled') ) name = models.CharField(max_length=128, verbose_name='title') created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) status = models.SmallIntegerField(choices=STATUS_CHOICE, default=STATUS_ENABLED) desc = models.CharField(max_length=512) # 增加的字段
再次执行“
python manage.py makemigrations bm
”则会在0001_initial.py 生成新了migrations文件。
生成的0002_title_desc.py文件的内容如下:
class Migration(migrations.Migration): dependencies = [ ('bm', '0001_initial'), ] operations = [ migrations.AddField( model_name='title', name='desc', field=models.CharField(default='', max_length=512), ), ]
0002_title_desc.py中的dependencies指明了依赖的前一个migrations文件(例子中是0001_initial.py),所以0002_title_desc.py记录的是相对于前一个migrations文件的变化。
执行完“python manage.py makemigrations bm”,然后执行“python manage.py migrate bm”,将会将migrations的变更在数据库中执行(新建表或增加/删除字段等)。
4Django的migration原理
models.py的变更最终会作用到数据库中,其映射关系如下:
在models.py中可做的修改包括新建类(表)、增加字段、删除字段及变更字段的属性,需要注意的是:a) 新增表字段和删除表字段不要同时做,否则django会解析为将删除的字段修改为新增的字段,可能会导致字段属性不一致等问题(表中删除字段数据格式与新增字段格式不一致);b)一般而言,不表进行删除字段操作,只做新增操作。
执行“python manage.py makemigrations”过程,主要工作是根据models.py的变更和已经生成的migrations文件进行比较,来生成变更的migrations文件。
执行“python manage.py migrate”过程,为了保证执行的migrations文件不重复执行(重复执行会有问题),因此django在每个应用下还会额外自动创建django_migration表,用于记录执行过的migrations文件,如示例中的bm数据库包括的表为(bm_author, bm_book,bm_title为models.py定义的,django_migration为django自动创建的):
在修改models.py之前,可以看到django_migration表中的数据为:
修改models.py(为Title类增加desc字段),执行“python manage.py migrate”后,会在表中增加一条数据,如下:
5总结本文介绍了models.py的变更如何作用到数据库中,演示采用的是sqlite数据库,实际使用时也可以在settings.py文件中配置其他数据库。后文将会介绍rest_framework的源码,django的执行过程等。示例代码见:https://github.com/836304831/mp_weixin.git。