如何用正确的姿势去创建或者迁移Django数据库
Django的ORM非常强大,用它进行数据迁移非常方便,它可以结合Models的变化来改变数据库中的数据。 Django跟踪依赖关系,执行顺序以及应用程序来确定应用是否已经进行了数据迁移。
下面我们用一个常见例子来进行举例。当我们需要引入不可为空的新字段时,或者,当我们创建一个新字段来存储缓存的某个值时,我们可以创建新字段并添加初始计数。
在这篇文章中,我们将探索一个简单的示例,您可以很容易地根据需要扩展和修改它。
假设我们有一个名为blog的应用程序,已经在INSTALLED_APPS
里添加了!
Blog下的models是这样定义的:
blog/models.py
from django.db import models class Post(models.Model): title = models.CharField(max_length=255) date = models.DateTimeField(auto_now_add=True) content = models.TextField() def __str__(self): return self.title
这应用程序已经在使用这个Post模型;它已经在生产中,数据库中存储了大量数据。
现在,假设我们想引入一个名为slug的新字段,该字段将用于组成博客的新url。slug必须是唯一(unique)而且不不能为空(not null)。
一般来说,我们添加的时候总是将新字段添加为null=True
或使用默认值(default
)。如果不能使用默认参数解决问题,那么首先将字段创建为null=True,然后为其创建数据迁移。然后,我们可以创建一个新的迁移,将字段设置为null=False。
修改过后的Models如下:
blog/models.py
from django.db import models class Post(models.Model): title = models.CharField(max_length=255) date = models.DateTimeField(auto_now_add=True) content = models.TextField() slug = models.SlugField(null=True) def __str__(self): return self.title
执行数据迁移
python manage.py makemigrations blog Migrations for 'blog': blog/migrations/0002_post_slug.py - Add field slug to post
执行显示:
python manage.py migrate blog Operations to perform: Apply all migrations: blog Running migrations: Applying blog.0002_post_slug... OK
此时,数据库已经帮添加了slug列。
然后我们使用命令,进行一次空的迁移。
python manage.py makemigrations blog --empty Migrations for 'blog': blog/migrations/0003_auto_20170926_1105.py
这时,我们打开blog/migrations/0003_auto_20170926_1105.py,内容如下:
from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): dependencies = [ ('blog', '0002_post_slug'), ] operations = [ ]
然后在这个文件中,我们可以创建一个可以由RunPython命令执行的函数:
blog/migrations/0003_auto_20170926_1105.py
from __future__ import unicode_literals from django.db import migrations from django.utils.text import slugify def slugify_title(apps, schema_editor): ''' We can't import the Post model directly as it may be a newer version than this migration expects. We use the historical version. ''' Post = apps.get_model('blog', 'Post') for post in Post.objects.all(): post.slug = slugify(post.title) post.save() class Migration(migrations.Migration): dependencies = [ ('blog', '0002_post_slug'), ] operations = [ migrations.RunPython(slugify_title), ]
在上面的示例中,我们使用了slugify实用函数。
它将一个字符串作为参数,并将其传入slug.。看下面几个例子:
from django.utils.text import slugify slugify('Hello, World!') 'hello-world' slugify('How to Extend the Django User Model') 'how-to-extend-the-django-user-model'
不管怎样,我们运行这个文件的方法来创建数据迁移的函数,还需要两个参数:APPS和schema_editor。运行的时候需要提供。记住,我们还需要从models里导入和使用 apps.get_model('app_name', 'model_name')
方法。
保存文件并执行迁移,就像平时我们迁移所做的那样:
python manage.py migrate blog Operations to perform: Apply all migrations: blog Running migrations: Applying blog.0003_auto_20170926_1105... OK
我们再看看我们现在的数据库
每个Post条目都有一个值了,因此我们可以安全地将开关从null=True更改为null=False。由于所有的值都是唯一的,我们还可以添加unique=True标志。
更改模型
blog/models.py
from django.db import models class Post(models.Model): title = models.CharField(max_length=255) date = models.DateTimeField(auto_now_add=True) content = models.TextField() slug = models.SlugField(null=False, unique=True) def __str__(self): return self.title
创建一个新的迁移:
python manage.py makemigrations blog
这时我们将看到以下提示:
You are trying to change the nullable field 'slug' on post 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”,选择选项2
Migrations for 'blog': blog/migrations/0004_auto_20170926_1422.py - Alter field slug on post
现在我们可以安全地应用迁移了。
python manage.py migrate blog Operations to perform: Apply all migrations: blog Running migrations: Applying blog.0004_auto_20170926_1422... OK
最后总结:
数据迁移有时很棘手。在为项目创建数据迁移时,始终首先检查生产数据。我在示例中使用的slugify_title的实现有点不太妥当,因为它可以为大型数据集生成重复的标题。我们在修改数据库的时候,首先在登录环境中首测试数据迁移,以避免在生产环境中破坏东西。
一步一步地去做也很重要,这样你就能感觉到你在控制你所引入的变化。注意,这里我为一个简单的数据迁移创建了三个迁移文件。
如您所见,创建这种类型的迁移非常容易。也很灵活。例如,您可以加载一个外部文本文件,将数据插入到一个新的列中。
本文中使用的源代码可以在上面找到:GitHub:https://github.com/sibtc/data-migrations-example
本文翻译自:https://simpleisbetterthancomplex.com/tutorial/2017/09/26/how-to-create-django-data-migrations.html
文章评论 1
当你的才华和能力还不足以支撑你的野心的时候,静下心来,学习!