一、迁移清单
列出某个app下的迁移清单,或整个django项目的迁移清单,相当于列出其历史记录,这时要用到
#某个app下迁移清单 manage.py migrate app_name --list #整个项目迁移清单 manage.py migrate --list
显示如下。注意带*的为已经应用到了 ,()表示尚未应用到的。
E:\project\demo>manage.py migrate other --list other (*) 0001_initial (*) 0002_auto__add_action__del_field_contact_age__add_field_contact_comment (*) 0003_initial (*) 0004_initial (*) 0005_initial (*) 0006_auto__add_field_contact_website (*) 0007_auto__add_unique_contact_name (*) 0008_auto__add_field_action_keys (*) 0009_auto__del_field_action_keys (*) 0010_auto__del_action
二、数据迁移
第一节所涉及的是模式迁移,涉及表、字段、索引的添加和删除;另一种迁移就是数据迁移。
数据迁移是用来改变你的数据库中存储的数据匹配一个新的模式,或特性。
例如,如果你一直以纯文本形式保存密码,现在你需要以利用salt对密码进行散列 的形式,这样就涉及到数据的迁移以适应新的模式。可能有以下三个步骤(每个步骤对应一个迁移):
(1)、创建两列:password_salt 和password_hash (a schema migration).
(2)、使用旧密码的列的内容,计算每个用户的密码后应用到新的列(a data migration)
(3)、移除旧的密码列(a schema migration).
class User(models.Model): username = models.CharField(max_length=255) password = models.CharField(max_length=60)
假设有用户:Jack,密码:admin123.如果以文本的形式肯定不安全了,那么现在就利用salt对密码进行散列。
import sha class User(models.Model): username = models.CharField(max_length=255) password = models.CharField(max_length=60) password_salt = models.CharField(max_length=8, null=True) password_hash = models.CharField(max_length=40, null=True) def check_password(self, password): return sha.sha(self.password_salt+password).hexdigest() == self.password_hash """ 用户注册的过程: 1)用户提供密码(以及其他用户信息); 2)系统为用户生成Salt值; 3)系统将Salt值和用户密码连接到一起; 4)对连接后的值进行散列,得到Hash值; 5)将Hash值和Salt值分别放到数据库中。 用户登录的过程: 1)用户提供用户名和密码; 2)系统通过用户名找到与之对应的Hash值和Salt值; 3)系统将Salt值和用户提供的密码连接到一起; 4)对连接后的值进行散列,得到Hash'(注意有个“撇”); 5)比较Hash和Hash'是否相等,相等则表示密码正确,否则表示密码错误。 """
接下来就是创建模式迁移将会创建两列:
E:\project\demo>manage.py schemamigration other --auto + Added field password_salt on other.User + Added field password_hash on other.User Created 0013_auto__add_field_user_password_salt__add_field_user_password_hash.py . You can now apply this migration with: ./manage.py migrate other
那么接下来就是创建数据迁移了,
E:\project\demo>manage.py datamigration other hash_passwords
Created 0014_hash_passwords.py.
打开 0014_hash_passwords.py 文件 发现如下,我们还需要手写forwards、backwards方法
更改如下:
# -*- coding: utf-8 -*- import datetime from south.db import db from south.v2 import DataMigration from django.db import models class Migration(DataMigration): def forwards(self, orm): import random,sha,string for user in orm.User.objects.all(): #更新数据 user.password_salt = "".join([random.choice(string.letters) for i in range(8)]) user.password_hash = sha.sha(user.password_salt + user.password).hexdigest() user.save() def backwards(self, orm): raise RuntimeError("Cannot reverse this migration.") ......
注意这里的orm.User,对象关系映射(Object Relational Mapping) 这里关系到其迁移的model,如User model。如果用数据迁移中使用其他app的model则用orm['contenttypes.ContentType']. Models
If you want to access models from other apps in your data migration, use a syntax like orm['contenttypes.ContentType']. Models will be available if you can somehow get to them via ForeignKey or ManyToMany traversal from your app’s models; if you want to freeze other models, simply pass --freeze appname on the datamigration command line.
最后在backwards()方法中引发错误,因为这个过程本质上是不可逆转的。就是说只能向前迁移,不能向后。
最后步骤:删除password字段,启动迁移
E:\project\demo>manage.py schemamigration other --auto ? The field 'User.password' does not have a default specified, yet is NOT NULL. ? Since you are removing this field, you MUST specify a default ? value to use for existing rows. Would you like to: ? 1. Quit now. ? 2. Specify a one-off value to use for existing columns now ? 3. Disable the backwards migration by raising an exception; you can edit the migration to fix it later ? Please select a choice: 2 ? Please enter Python code for your one-off default value. ? The datetime module is available, so you can do e.g. datetime.date.today() >>> "" - Deleted field password on other.User Created 0015_auto__del_field_user_password.py. You can now apply this migration with: ./manage.py migrate other
接下来就是应用这3个迁移:
E:\project\demo>manage.py migrate other Running migrations for other: - Migrating forwards to 0004_auto__del_field_user_password. > southtut2:0002_auto__add_field_user_password_salt__add_field_user_password_hash > southtut2:0003_hash_passwords > southtut2:0004_auto__del_field_user_password - Loading initial data for other.
接下来验证一下:
>>> from other.models import User >>> User.objects.get(id=1).check_password('admin123') True >>> User.objects.get(id=1).check_password('admin3') False