在使用和学习Django框架时,发现很多人包括我自己在对Django项目进行版本管理时,通常把migrations文件添加到了.gitignore中。

笔者也一直有疑问这种做法是否正确,于是去查看官方文档,找到以下这段。

原文:

You should think of migrations as a version control system for your database schema. makemigrations is responsible for packaging up your model changes into individual migration files - analogous to commits - and migrate is responsible for applying those to your database.

The migration files for each app live in a “migrations” directory inside of that app, and are designed to be committed to, and distributed as part of, its codebase. You should be making them once on your development machine and then running the same migrations on your colleagues’ machines, your staging machines, and eventually your production machines.

中文翻译:

你可以想象 migrations 相当一个你的数据库的一个版本控制系统。makemigrations 命令负责保存你的模型变化到一个迁移文件 - 和 commits 很类似 - 同时 migrate负责将改变提交到数据库。

每个 app 的迁移文件会保存到每个相应 app 的“migrations”文件夹里面,并且准备如何去执行它, 作为一个分布式代码库。 每当在你的开发机器或是你同事的机器并且最终在你的生产机器上运行同样的迁移,你应当再创建这些文件。

根据官方文档的说法,不将migrations提交到仓库的做法时错误的。

而且如果要使用django自带的封装好的TestCase进行单元测试,migrations也必须保留。

下一篇文章笔者也会介绍一下django中的TestCase的使用心得。

下面介绍一下,在项目中migrations的一些使用心得和遇到的一些问题。

利用migrations初始化数据

我们现在有一个Book的模型,我想在migrate之后初始化一些数据。

This Github Sample is by elfgzp
make_good_use_of_migrations/models.py view raw

class Book(models.Model):name = models.CharField(max_length=32)复制代码

例如:生成三本名称分别为HamletTempestThe Little Prince的书。

在执行了python manage.py makemigrations之后migrations文件夹会生成0001_initial.py的文件。

文件中包含了Book这个模型初始化的一些代码。

在介绍如何利用migrations初始化数据时,先介绍一下migrations常用的两个操作:

RunSQLRunPython

顾名思义分别是执行SQL语句Python函数

下面我用migrations中的RunPython来初始化数据。

  1. 在相应app下的migrations文件新建0002_init_book_data.py migrations/.
    ​ ├── 0001_initial.py.
    ​ └── 0002_init_book_data.py.

  2. 然后增加Migration类继承django.db.migrations.Migration,并在operations中增加需要执行的代码。

This Github Sample is by elfgzp
make_good_use_of_migrations/migrations/0002_init_book_data.py view raw

from django.db import migrations"""
make_good_use_of_migrations 是App的名字
"""def init_book_data(apps, schema_editor):Book = apps.get_model('make_good_use_of_migrations', 'Book')init_data = ['Hamlet', 'Tempest', 'The Little Prince']for name in init_data:book = Book(name=name)book.save()class Migration(migrations.Migration):dependencies = [('make_good_use_of_migrations', '0001_initial'),]# 这里要注意dependencies为上一次migrations的文件名称operations = [migrations.RunPython(init_book_data)]复制代码

  1. 运行python manage.py migrate,可以看到数据已经在数据库中生成了。

利用migrations修复数据

我们常常遇到这种情况,例如我需要给Book模型增加一个外键字段,而且这个字段不能为空,所以旧的数据就要进行处理修复,我们可以这样处理。

  1. 先将需要增加的字段null属性设置为True,然后执行makemigrations
This Github Sample is by elfgzp
make_good_use_of_migrations/models.py view raw

class Author(models.Model):name = models.CharField(max_length=32)class Book(models.Model):name = models.CharField(max_length=32)author = models.ForeignKey(to=Author, on_delete=models.CASCADE, null=False)复制代码

  1. 在相应app下的migrations文件新建0004_fix_book_data.py migrations/.
    ├── 0001_initial.py.
    ├── 0002_init_book_data.py.
    ├── 0003_auto_20181204_0533.py.
    └── 0004_fix_book_data.py.
This Github Sample is by elfgzp
make_good_use_of_migrations/migrations/0004_fix_book_data.py view raw

from django.db import migrationsdef fix_book_data(apps, schema_editor):Book = apps.get_model('make_good_use_of_migrations', 'Book')Author = apps.get_model('make_good_use_of_migrations', 'Author')for book in Book.objects.all():author, _ = Author.objects.get_or_create(name='%s author' % book.name)book.author = authorbook.save()class Migration(migrations.Migration):dependencies = [('make_good_use_of_migrations', '0003_auto_20181204_0533'),]operations = [migrations.RunPython(fix_book_data)]复制代码

  1. 最后再将Book模型中的author字段属性null设为False,并执行makemigrations。执行后会出现,

    You are trying to change the nullable field 'author' on book to non-nullable 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 with a null value for this column)2) Ignore for now, and let me handle existing rows with NULL myself (e.g. because you added a RunPython or RunSQL operation to handle NULL values in a previous data migration)3) Quit, and let me add a default in models.py
    Select an option:
    复制代码

    这里选择第2项,意思是忽略该字段已经为空的数据,使用RunPython或者RunSQL自行处理。

    选择完成后在执行python manage.py migrate,会发现数据库中的数据会按照我们的预期处理完成。

解决多人开发时migrations产生的冲突

为了模拟多人多分支开发,新建一个master-2的分支,并且版本在创建Author类之前,并且在Book模型中增加remark字段。

model.py文件中的内容如下:

This Github Sample is by elfgzp
make_good_use_of_migrations/models.py view raw

class Book(models.Model):name = models.CharField(max_length=32)remark = models.CharField(max_length=32, null=True)复制代码

migrations文件目录如下:

migrations/.
├── 0001_initial.py.
├── 0002_init_book_data.py.
└──0003_book_remark.py.

当我们把master-2的代码合并到master时,会发现migrations中出现了重复的编号0003并且他们共同依赖于0002_init_book_data

migrations/.
├── 0001_initial.py.
├── 0002_init_book_data.py.
├── 0003_auto_20181204_0533.py
├── 0003_book_remark.py
├── 0004_fix_book_data.py
└──0005_auto_20181204_0610.py.

这时候就需要用到命令:

python manage.py makemigrations --merge
复制代码

然后就会在migrations目录生成一个0006_merge_20181204_0622.py文件

This Github Sample is by elfgzp
make_good_use_of_migrations/migrations/0006_merge_20181204_0622.py view raw

from django.db import migrationsclass Migration(migrations.Migration):dependencies = [('make_good_use_of_migrations', '0005_auto_20181204_0610'),('make_good_use_of_migrations', '0003_book_remark'),]operations = []复制代码

这时候在执行python manage.py migrate就可以了。

使用migrations.RunPython需要注意的问题

在函数中是无法调用模型类的函数的

假设在Book模型中定义了两个函数print_name和类函数print_class_name

This Github Sample is by elfgzp
make_good_use_of_migrations/models.py view raw

class Book(models.Model):name = models.CharField(max_length=32)author = models.ForeignKey(to=Author, on_delete=models.CASCADE, null=False)remark = models.CharField(max_length=32, null=True)def print_name(self):print(self.name)@classmethoddef print_class_name(cls):print(cls.__name__)复制代码

migrations中是无法调用的,笔者也没有仔细研究,推测是Book类初始化时只把字段初始化了。

This Github Sample is by elfgzp
make_good_use_of_migrations/migrations/0004_fix_book_data.py view raw

from django.db import migrationsdef fix_book_data(apps, schema_editor):Book = apps.get_model('make_good_use_of_migrations', 'Book')Author = apps.get_model('make_good_use_of_migrations', 'Author')for book in Book.objects.all():author, _ = Author.objects.get_or_create(name='%s author' % book.name)book.author = author"""book.print_name()book.print_class_name()这样调用会报错"""book.save()class Migration(migrations.Migration):dependencies = [('make_good_use_of_migrations', '0003_auto_20181204_0533'),]operations = [migrations.RunPython(fix_book_data)]复制代码

在函数中模型的类所重写的save方法无效,包括继承的save方法

migrations中所有重写的save方法都不会运行,例如:

This Github Sample is by elfgzp
make_good_use_of_migrations/models.py view raw

class Book(models.Model):name = models.CharField(max_length=32)author = models.ForeignKey(to=Author, on_delete=models.CASCADE, null=False)remark = models.CharField(max_length=32, null=True)def print_name(self):print(self.name)@classmethoddef print_class_name(cls):print(cls.__name__)def save(self, *args, **kwargs):if not self.remark:self.remark = 'This is a book.'复制代码

最后初始化生成的数据的remark字段的值仍然为空。

在函数中模型注册的所有signal无效

虽然给Book模型注册了signal,但是在migrations中仍然不会起作用

This Github Sample is by elfgzp
make_good_use_of_migrations/models.py view raw

@receiver(pre_save, sender=Book)
def generate_book_remark(sender, instance, *args, **kwargs):print(instance)if not instance.remark:instance.remark = 'This is a book.'复制代码

不要将数据处理放到模型变更的migrations文件中

在做数据修复或者生成初始化数据时,不要将处理函数放到自动生成的变更或生成字段、模型的migrations文件中,例如:

This Github Sample is by elfgzp
make_good_use_of_migrations/migrations/0005_auto_20181204_0610.py view raw

class Migration(migrations.Migration):dependencies = [('make_good_use_of_migrations', '0004_fix_book_data'),]operations = [migrations.AlterField(model_name='book',name='author',field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,to='make_good_use_of_migrations.Author'),),"""migrations.RunPython(xxx) 不要把数据处理放到模型变更中"""]复制代码

不要放在一起的主要原因是,当RunPython中函数的处理逻辑一旦出现异常无法向下执行,

django_migrations将不会记录这一次处理,但是表结构的变更已经执行了!

这也是Django migrations做的不好的地方,正确应该是出现异常需要做数据库回滚。

一旦出现这种情况,只能手动将migrations的名称如0005_auto_20181204_0610,写入到数据库表django_migrations中,然后将RunPython中的逻辑单独剥离出来。

总结

以上就是笔者在项目中使用Django框架的migrations的心得,下一篇会介绍Django框架的TestCase

本文的源码会放到github上,github.com/elfgzp/djan…

本人博客原文地址:elfgzp.cn/2018/12/04/…

Django使用心得(一) 善用migrations相关推荐

  1. Django使用心得(四)

    本篇主要讲解如何在django的模板中自定义tag. 主要内容: 自定义tag的步骤 带参数和不带参数的自定义tag 在模板中使用自定义的tag 补充说明 1. 自定义tag的步骤 自定义tag主要有 ...

  2. Django使用心得(二)

    本篇主要内容: django中引用javascript django中引用css及图片资源 1. django中引用javascript web开发中必然要引用一些javascript的函数库来进行一 ...

  3. Django学习心得

    Django是python的web开发框架,遵循MVC的设计模式,但在Django中通常称为MTV(model-template-views).model是数据持久层,主要存放实体映射.实体关系以及实 ...

  4. python实验过程心得体会_Python中django学习心得

    {{lis.2 }} //通过索引取,结果:222 {{ dic.name}} //通过key取,结果:alex {{ person_list.1.name }} //通过索引取到对象,通过对象属性取 ...

  5. 第十六 django进一步了解

    一.关于django 的URLS 1.配置固定访问 1.1.配置fly views from django.shortcuts import render,HttpResponse# Create y ...

  6. Django开发基础----操作数据库

    Django中对数据库的操作是由Models来完成的 Models是什么? 通常,一个Model对应数据库的一张数据表 Django中Models以类的形式出现 它包含了一些基本字段以及数据的一些行为 ...

  7. Django框架 之基础入门

    django是一款MVT的框架 一.基本过程 1.创建项目:django-admin startproject 项目名称 2.编写配置文件settings.py(数据库配置.时区.后台管理中英文等) ...

  8. 【连载】Django入门到实战(一)

    一.项目目录结构介绍 manager.py 与项目进行交互的命令行工具集的入口(项目管理器) MyDjango 目录:项目容器,包含项目的基本配置,目录名称不建议修改 __init__.py Pyth ...

  9. Django Python Web应用程序框架简介

    在这个由四部分组成的系列文章的前三篇文章中,比较了不同的Python Web框架,我们介绍了Pyramid , Flask和Tornado Web框架. 我们已经构建了同一个应用程序3次,最终进入了D ...

最新文章

  1. linux检测病毒工具,Linux下查杀病毒工具
  2. matlab 滤波_MATLAB之简单卡尔曼滤波
  3. CRM lifecycle status
  4. 2019.08.08学习整理
  5. dedeCMS如何进行关键词过滤替换和屏蔽非法词汇?
  6. 宅男、游戏、美女,一场不一样的技术公开课让你老泪纵横
  7. oracle分同步事务和异步事务,oracle的事务隔离
  8. 查找两个表中主键一样但其它字段数值不一样的记录
  9. [项目管理] ISO900X 标准体系
  10. 操作系统中的基础抽象
  11. S/HIC 系列软件:S/HIC 利用随机森林识别 软/硬 清扫
  12. Markdown是什么?
  13. 微信小程序InnerAudioContext IOS真机无法播放声音
  14. 历史上的今天:“计算机之父”争夺战;Microsoft Excel 诞生;百度推出百度地图...
  15. XPDL与WS-BPEL的比较之三:人工活动
  16. 会编程的少年有多厉害?8岁女儿写代码哄程序员爸爸开心,网友直呼:破防了
  17. php+die(.)函数,die函数介绍与使用方法详解
  18. python使用pymysql包,操作mysql数据库,包括安装及使用(附代码)
  19. gpuimage123
  20. 跨境电商季节性选品攻略

热门文章

  1. 节选自《白衣飘飘的年代:宋朝那些有趣的人和事》(重庆大学出版社)
  2. 关于maya中的sets集
  3. 时间转换格式比较大小
  4. 国内首次!这家中国企业的语言AI实力被公认全球No.2!仅次于谷歌
  5. 报错Cannot resolve symbol XXX
  6. GameFramework制作游戏(二)制作UI界面
  7. 团队项目-《野狼吃鸡》需求分析
  8. ASM AMDU工具的使用
  9. 简单利用aircrack测试无线网络(图)
  10. 国产编程语言CBrother的初见