https://www.klette.us/migrate-django-models-to-uuid-primary-key/

又是一篇渣翻

老设计决策有时候会让你很难受,下面就是一个例子。

我手上的一个 Django Model 是这样的:

此外,还有一个外键指向它

现在,我们需要让 municipality 支持多个国家,显然一个唯一 field 将会引起冲突,不够用。(译者:这个理由有点牵强)

对于所有代码的现代部分,我们使用UUID作为主键。所以我们想要 migrate municaipality的主键到UUID,并且维持了原有的关系。(译者:外键和自增主键都是数据库性能的杀手。)

2017年9月份,Django 依然不支持优雅的迁移主键,所以我们自己做了(译者:我也不知道现在行不行。)

我们尝试了许多 magic 解决方案,但是我们在 migrations 系统这个被难住了,并且不能检测以及很好的处理改变。

在经过一小段研究和错误,我们找到了以下的解决方案。尽管这还有一些小问题,但是的确有效。

再一次提醒,从数据库的角度,当你定义了一个ForeignKey field 在 Django 中,Django将会创建一个数据库列,是同样的类型,作为 referenced model 的主键,并且增加外键约束。所以在上面的例子中,我们有两个表:

所以我们需要解除外键约束,更换 root model,然后映射新的主键到旧的上,并且重新应用外键到上面去。

我们首先打破外键

现在,·Municipality·modle 没有被任何外键所 refer,我们可以在上面进行工作了。

增加一个新的 id field:

处于某些原因,默认值在我的案例中不 work,所以我增加了一个过程到创建的 migration,来创建新的唯一 id。

现在我们有一个UUID ·id·field 在·Municipality 中,然后我们应该可以更换主键了。

创建 migration,并且确保在code上的AlterField操作在id之前。我们已经在id上增加了primary_key并且增加了 unique=True 到code field。constraint 没了,在我们删除 primary_key 的时候就没了。

让我们开始一个空的迁移

打开文件,输入

  1. 增加一个临时的field来保存 Municipality 的UUID,我们不让他是一个ForeignKeyfield,否则 Django confuse。
  2. 我们运行 match 函数来寻找新的 id,通过寻找旧的 key,并且存储到临时的 field。
  3. 删除旧的 municipality field
  4. 重命名临时 field 到 municipality
  5. 最后迁移到一个外键,然后创建我们需要 constraint

下面还有一些内容。自从我们将 migrations 分离成多个文件,我们让代码变得很脆弱(如果后面的某些 migrations 失败了)。这将会让我们的应用进入一个 unworkable 的状态。所以确保测试一下migrations。你可通过手工把这所有的步骤整合到一个migration里,但是如果你有来自多个不同app的 reference,你可能需要把这些步骤再分开。

logging

迁移的过程中,你可能遇到很多问题,所以一个比较不错的方式,是创建一个简单的迁移 logging。

想要观察 Django 运行了那些 SQL 语句,运行python manage.py sqlmigrate <appname><migration_number>,这是一个超级有用的方法。