{"title":"迁移","version":"6.1","locale":"zh-hans","docname":"topics/migrations","url":"/zh-hans/6.1/topics/migrations/","canonical":"https://djangodocs.dev/zh-hans/6.1/topics/migrations/","summary":"迁移是 Django 将你对模型的修改（例如增加一个字段，删除一个模型）应用至数据库架构中的方式。","html":"<span id=\"migrations\"></span><h1>迁移<a class=\"heading-anchor\" href=\"#module-django.db.migrations\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h1>\n<p>迁移是 Django 将你对模型的修改（例如增加一个字段，删除一个模型）应用至数据库架构中的方式。它们被设计的尽可能自动化，但你仍需要知道何时构建和运行迁移，你还需要了解一些常见问题。</p>\n<section id=\"the-commands\">\n<h2>命令<a class=\"heading-anchor\" href=\"#the-commands\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>以下是几个常用的与迁移交互的命令，即 Django 处理数据库架构的方式：</p>\n<ul class=\"simple\">\n<li><p><a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#django-admin-migrate\"><code class=\"xref std std-djadmin docutils literal notranslate\">migrate</code></a>，负责应用和撤销迁移。</p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#django-admin-makemigrations\"><code class=\"xref std std-djadmin docutils literal notranslate\">makemigrations</code></a>，基于模型的修改创建迁移。</p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#django-admin-sqlmigrate\"><code class=\"xref std std-djadmin docutils literal notranslate\">sqlmigrate</code></a>，展示迁移使用的 SQL 语句。</p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#django-admin-showmigrations\"><code class=\"xref std std-djadmin docutils literal notranslate\">showmigrations</code></a>，列出项目的迁移和迁移的状态。</p></li>\n</ul>\n<p>你应该将迁移看作是数据库架构的版本控制系统。 <code class=\"docutils literal notranslate\">makemigrations</code> 负责将模型修改打包进独立的迁移文件中——类似提交修改，而 <code class=\"docutils literal notranslate\">migrate</code> 负责将其应用至数据库。</p>\n<p>每个应用的迁移文件位于该应用的 &quot;migrations&quot; 目录中，他们被设计成应用代码的一部分，与应用代码一起被提交，被发布。你只需在开发机上构建一次，就可以在同事的电脑或测试机上运行同样的迁移而保证结果一致。最后在生产环境运行同样的迁移。</p>\n<aside class=\"admonition admonition-note\" role=\"note\">\n<p class=\"admonition-title\">Note</p>\n<p>通过修改配置 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/settings/#std-setting-MIGRATION_MODULES\"><code class=\"xref std std-setting docutils literal notranslate\">MIGRATION_MODULES</code></a> 可以重写包含迁移的应用的包名。</p>\n</aside>\n<p>从同样的数据集合运行迁移在开发、测试和生产环境都会生成同样的结果。</p>\n<p>Django 会在修改模型或字段时生成迁移——即便修改的是不会影响数据库的配置——因为唯一能确保结果正确性的方法时完整记录修改历史，而且这些东西你以后可能在某些数据迁移中用的到（例如，已设置了自定义验证器的时候）。</p>\n</section>\n<section id=\"backend-support\">\n<h2>后端支持<a class=\"heading-anchor\" href=\"#backend-support\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>所有 Django 支持的数据库后端都支持迁移，还有些支持表修改（通过 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/schema-editor/\"><span class=\"doc\">SchemaEditor</span></a> 类实现）的第三方后端也支持。</p>\n<p>然而，有些数据库在表结构变更方面比其它数据库更强；下面介绍一些注意事项。</p>\n<section id=\"postgresql\">\n<h3>PostgreSQL<a class=\"heading-anchor\" href=\"#postgresql\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>PostgreSQL 在架构支持方面是所有数据库中是最强的。</p>\n</section>\n<section id=\"mysql\">\n<h3>MySQL<a class=\"heading-anchor\" href=\"#mysql\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>MySQL 缺乏对架构变更操作相关事务的支持，这意味着如果迁移失败，你将必须手动取消更改才能重试（无法回滚到较早的时间）。</p>\n<p>MySQL 8.0 引入了对 <a class=\"reference external\" href=\"https://dev.mysql.com/doc/refman/en/innodb-online-ddl-operations.html\">DDL 操作</a> 的重大性能增强，使其更高效，并减少了对完整表重建的需求。然而，它不能保证完全没有锁定或中断。在仍然需要锁定的情况下，这些操作的持续时间将与涉及的行数成比例。</p>\n<p>最后，MySQL 对索引覆盖的所有列的组合大小有相对较小的限制。这意味着在 MySQL 下可能在其他后端上创建的索引将无法创建。</p>\n</section>\n<section id=\"sqlite\">\n<h3>SQLite<a class=\"heading-anchor\" href=\"#sqlite\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>SQLite 几乎没有内置的架构更改支持，因此 Django 尝试通过以下方式对其进行模拟：</p>\n<ul class=\"simple\">\n<li><p>使用新架构创建新表</p></li>\n<li><p>复制数据</p></li>\n<li><p>删除旧表</p></li>\n<li><p>重新命名新表，使之与原表名相匹配。</p></li>\n</ul>\n<p>此过程一般工作的很好，但它可能很慢，偶尔也会出现问题。除非你非常清楚风险和它的局限性，否则不建议你在生产环境中运行和迁移 SQLite；Django 自带的支持是为了让开发人员在本地计算机上使用 SQLite 来开发较不复杂的 Django 项目，而无需完整的数据库。</p>\n</section>\n</section>\n<section id=\"workflow\">\n<h2>工作流程<a class=\"heading-anchor\" href=\"#workflow\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>The examples in this section use a <code class=\"docutils literal notranslate\">books</code> app with the following models:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span> <span class=\"nn\">django.db</span> <span class=\"kn\">import</span> models\n\n\n<span class=\"k\">class</span> <span class=\"nc\">Author</span><span class=\"p\">(</span>models<span class=\"o\">.</span>Model<span class=\"p\">):</span>\n    name <span class=\"o\">=</span> models<span class=\"o\">.</span>CharField<span class=\"p\">(</span>max_length<span class=\"o\">=</span><span class=\"mi\">100</span><span class=\"p\">)</span>\n\n\n<span class=\"k\">class</span> <span class=\"nc\">Book</span><span class=\"p\">(</span>models<span class=\"o\">.</span>Model<span class=\"p\">):</span>\n    title <span class=\"o\">=</span> models<span class=\"o\">.</span>CharField<span class=\"p\">(</span>max_length<span class=\"o\">=</span><span class=\"mi\">100</span><span class=\"p\">)</span>\n    author <span class=\"o\">=</span> models<span class=\"o\">.</span>ForeignKey<span class=\"p\">(</span>Author<span class=\"p\">,</span> on_delete<span class=\"o\">=</span>models<span class=\"o\">.</span>CASCADE<span class=\"p\">)</span>\n</code></pre></div>\n<p>Django 可以为您创建迁移。对您的模型进行更改，比如添加一个字段或删除一个模型，然后运行 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#django-admin-makemigrations\"><code class=\"xref std std-djadmin docutils literal notranslate\">makemigrations</code></a>：</p>\n<div class=\"code-block\" data-language=\"shell\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code>$ python manage.py makemigrations\nMigrations <span class=\"k\">for</span> <span class=\"s1\">&#39;books&#39;</span>:\n  books/migrations/0003_auto.py:\n    ~ Alter field author on book\n</code></pre></div>\n<p>你的模型将被扫描并与当前包含在你的迁移文件中的版本进行比较，然后将写出一组新的迁移。请务必阅读输出，看看 <code class=\"docutils literal notranslate\">makemigrations</code> 认为你已更改的内容——它并不完美，对于复杂的更改，可能无法检测到你所期望的。</p>\n<p>一旦您有了新的迁移文件，您应该将它们应用到您的数据库，以确保它们按预期工作：</p>\n<div class=\"code-block\" data-language=\"shell\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code>$ python manage.py migrate\nOperations to perform:\n  Apply all migrations: books\nRunning migrations:\n  Applying books.0003_auto... OK\n</code></pre></div>\n<p>一旦应用了迁移，将迁移和模型更改作为一个单一的提交来提交到您的版本控制系统——这样，当其他开发人员（或你的生产服务器）检查代码时，他们将同时获得对你的模型的更改和伴随的迁移。</p>\n<p>如果您想要为迁移指定一个有意义的名称而不是生成的名称，您可以使用 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#cmdoption-makemigrations-name\"><code class=\"xref std std-option docutils literal notranslate\">makemigrations --name</code></a> 选项：</p>\n<div class=\"code-block\" data-language=\"shell\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code>$ python manage.py makemigrations --name changed_my_model your_app_label\n</code></pre></div>\n<section id=\"executing-sqlmigrate\">\n<span id=\"id1\"></span><h3>Executing <code class=\"docutils literal notranslate\">sqlmigrate</code><a class=\"heading-anchor\" href=\"#executing-sqlmigrate\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>The <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#django-admin-sqlmigrate\"><code class=\"xref std std-djadmin docutils literal notranslate\">sqlmigrate</code></a> command takes migration names and returns the SQL\nthey would run. It doesn't actually run the migration on your database -\ninstead, it prints it to the screen so that you can see what SQL Django thinks\nis required. It's useful for checking what Django is going to do or if you have\ndatabase administrators who require SQL scripts for changes.</p>\n<p>To inspect the SQL of the initial migration that created the tables for the\n<code class=\"docutils literal notranslate\">books</code> models above, run:</p>\n<div class=\"code-block\" data-language=\"shell\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code>$ python manage.py sqlmigrate books <span class=\"m\">0001</span>\n</code></pre></div>\n<p>You should see something similar to the following (we've reformatted it for\nreadability):</p>\n<div class=\"code-block\" data-language=\"sql\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">SQL</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"SQL code\"><code><span class=\"k\">BEGIN</span><span class=\"p\">;</span>\n<span class=\"c1\">--</span>\n<span class=\"c1\">-- Create model Author</span>\n<span class=\"c1\">--</span>\n<span class=\"k\">CREATE</span> <span class=\"k\">TABLE</span> <span class=\"ss\">&quot;books_author&quot;</span> <span class=\"p\">(</span>\n    <span class=\"ss\">&quot;id&quot;</span> <span class=\"nb\">bigint</span> <span class=\"k\">NOT</span> <span class=\"k\">NULL</span> <span class=\"k\">PRIMARY</span> <span class=\"k\">KEY</span> <span class=\"k\">GENERATED</span> <span class=\"k\">BY</span> <span class=\"k\">DEFAULT</span> <span class=\"k\">AS</span> <span class=\"k\">IDENTITY</span><span class=\"p\">,</span>\n    <span class=\"ss\">&quot;name&quot;</span> <span class=\"nb\">varchar</span><span class=\"p\">(</span><span class=\"mi\">100</span><span class=\"p\">)</span> <span class=\"k\">NOT</span> <span class=\"k\">NULL</span>\n<span class=\"p\">);</span>\n<span class=\"c1\">--</span>\n<span class=\"c1\">-- Create model Book</span>\n<span class=\"c1\">--</span>\n<span class=\"k\">CREATE</span> <span class=\"k\">TABLE</span> <span class=\"ss\">&quot;books_book&quot;</span> <span class=\"p\">(</span>\n    <span class=\"ss\">&quot;id&quot;</span> <span class=\"nb\">bigint</span> <span class=\"k\">NOT</span> <span class=\"k\">NULL</span> <span class=\"k\">PRIMARY</span> <span class=\"k\">KEY</span> <span class=\"k\">GENERATED</span> <span class=\"k\">BY</span> <span class=\"k\">DEFAULT</span> <span class=\"k\">AS</span> <span class=\"k\">IDENTITY</span><span class=\"p\">,</span>\n    <span class=\"ss\">&quot;title&quot;</span> <span class=\"nb\">varchar</span><span class=\"p\">(</span><span class=\"mi\">100</span><span class=\"p\">)</span> <span class=\"k\">NOT</span> <span class=\"k\">NULL</span><span class=\"p\">,</span>\n    <span class=\"ss\">&quot;author_id&quot;</span> <span class=\"nb\">bigint</span> <span class=\"k\">NOT</span> <span class=\"k\">NULL</span>\n<span class=\"p\">);</span>\n<span class=\"k\">ALTER</span> <span class=\"k\">TABLE</span> <span class=\"ss\">&quot;books_book&quot;</span>\n  <span class=\"k\">ADD</span> <span class=\"k\">CONSTRAINT</span> <span class=\"ss\">&quot;books_book_author_id_8b91747b_fk_books_author_id&quot;</span>\n    <span class=\"k\">FOREIGN</span> <span class=\"k\">KEY</span> <span class=\"p\">(</span><span class=\"ss\">&quot;author_id&quot;</span><span class=\"p\">)</span>\n    <span class=\"k\">REFERENCES</span> <span class=\"ss\">&quot;books_author&quot;</span> <span class=\"p\">(</span><span class=\"ss\">&quot;id&quot;</span><span class=\"p\">)</span>\n    <span class=\"k\">DEFERRABLE</span> <span class=\"k\">INITIALLY</span> <span class=\"k\">DEFERRED</span><span class=\"p\">;</span>\n<span class=\"k\">CREATE</span> <span class=\"k\">INDEX</span> <span class=\"ss\">&quot;books_book_author_id_8b91747b&quot;</span> <span class=\"k\">ON</span> <span class=\"ss\">&quot;books_book&quot;</span> <span class=\"p\">(</span><span class=\"ss\">&quot;author_id&quot;</span><span class=\"p\">);</span>\n\n<span class=\"k\">COMMIT</span><span class=\"p\">;</span>\n</code></pre></div>\n<p>Note the following:</p>\n<ul class=\"simple\">\n<li><p>The exact output will vary depending on the database you are using, since\nDjango tailors the SQL to it. Database-specific field types and the quoting\nof names are handled for you automatically. The example above is generated\nfor PostgreSQL.</p></li>\n<li><p>Table names are automatically generated by combining the name of the app\n(<code class=\"docutils literal notranslate\">books</code>) with the lowercase name of each model, giving <code class=\"docutils literal notranslate\">books_author</code>\nand <code class=\"docutils literal notranslate\">books_book</code>. (You can override this behavior.)</p></li>\n<li><p>Primary keys (IDs) are added automatically. (You can override this, too.)</p></li>\n<li><p>By convention, Django appends <code class=\"docutils literal notranslate\">&quot;_id&quot;</code> to the foreign key field name.\n(Yes, you can override this, as well.)</p></li>\n<li><p>The foreign key relationship is made explicit by a <code class=\"docutils literal notranslate\">FOREIGN KEY</code>\nconstraint. Don't worry about the <code class=\"docutils literal notranslate\">DEFERRABLE</code> parts; it's telling\nPostgreSQL to not enforce the foreign key until the end of the transaction.</p></li>\n</ul>\n</section>\n<section id=\"version-control\">\n<h3>版本控制<a class=\"heading-anchor\" href=\"#version-control\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>由于迁移存储在版本控制中，因此你有时会遇到这样的情况：你和另一个开发人员都同时向同一应用提交了迁移，从而导致两次迁移的编号相同。</p>\n<p>别担心——这些数字只是给开发者参考的，Django 只在乎每个迁移都有不同的名称。 迁移在文件中指定了它们所依赖的其他哪些迁移——包括同一应用中的早期迁移，所以可以检测到同一应用有两个新的迁移没有排序。</p>\n<p>当这种情况发生时，Django 会提示你，并给你一些选项。如果它认为足够安全，它将为你自动线性化两个迁移。如果不安全，你就得自己去修改迁移——别担心，这并不难，有关更多信息，请参见下面的 <a class=\"reference internal\" href=\"#migration-files\"><span class=\"std std-ref\">迁移文件</span></a>。</p>\n</section>\n</section>\n<section id=\"transactions\">\n<h2>事务<a class=\"heading-anchor\" href=\"#transactions\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>在支持 DDL 事务的数据库上（SQLite 和 PostgreSQL），所有的迁移操作默认都会在一个事务中运行。相反，如果一个数据库不支持 DDL 事务（如 MySQL、Oracle），那么所有的操作将在没有事务的情况下运行。</p>\n<p>你可以通过将 <code class=\"docutils literal notranslate\">atomic</code> 属性设置为 <code class=\"docutils literal notranslate\">False</code> 来防止迁移在事务中运行。例如：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span> <span class=\"nn\">django.db</span> <span class=\"kn\">import</span> migrations\n\n\n<span class=\"k\">class</span> <span class=\"nc\">Migration</span><span class=\"p\">(</span>migrations<span class=\"o\">.</span>Migration<span class=\"p\">):</span>\n    atomic <span class=\"o\">=</span> <span class=\"kc\">False</span>\n</code></pre></div>\n<p>It's also possible to execute parts of the migration inside a transaction using\n<a class=\"reference internal\" href=\"/zh-hans/6.1/topics/db/transactions/#django.db.transaction.atomic\" title=\"django.db.transaction.atomic\"><code class=\"xref py py-func docutils literal notranslate\">atomic()</code></a> or by passing <code class=\"docutils literal notranslate\">atomic=True</code> to\n<a class=\"reference internal\" href=\"/zh-hans/6.1/ref/migration-operations/#django.db.migrations.operations.RunPython\" title=\"django.db.migrations.operations.RunPython\"><code class=\"xref py py-class docutils literal notranslate\">RunPython</code></a>. See\n<a class=\"reference internal\" href=\"/zh-hans/6.1/howto/writing-migrations/#non-atomic-migrations\"><span class=\"std std-ref\">非原子性迁移</span></a> for more details.</p>\n</section>\n<section id=\"dependencies\">\n<h2>依赖<a class=\"heading-anchor\" href=\"#dependencies\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>虽然迁移是按应用进行的，但你的模型所隐含的表和关系太复杂，不可能同时为一个应用创建。当你进行迁移时，需要运行其他的东西——例如，你在你的 <code class=\"docutils literal notranslate\">books</code> 应用中添加了一个指向 <code class=\"docutils literal notranslate\">authors</code> 应用的 <code class=\"docutils literal notranslate\">ForeignKey</code>——最终的迁移将包含对 <code class=\"docutils literal notranslate\">authors</code> 中迁移的依赖。</p>\n<p>这意味着当您运行迁移时，<code class=\"docutils literal notranslate\">authors</code> 迁移首先运行并创建了 <code class=\"docutils literal notranslate\">ForeignKey</code> 引用的表，然后创建 <code class=\"docutils literal notranslate\">ForeignKey</code> 列的迁移在其后运行并创建约束。如果没有这样的顺序，迁移将尝试在引用的表不存在的情况下创建 <code class=\"docutils literal notranslate\">ForeignKey</code> 列，这将导致数据库出错。</p>\n<p>这种依赖性行为会影响大多数只限于单个应用的迁移操作。仅限于单个应用（无论是 <code class=\"docutils literal notranslate\">makemigrations</code> 还是 <code class=\"docutils literal notranslate\">migrate</code>）是尽最大努力的承诺，而不是保证；任何其他需要用来正确获取依赖关系的应用程序都将是。</p>\n<p>没有迁移的应用不得与有迁移的应用有关系（<code class=\"docutils literal notranslate\">ForeignKey</code>，<code class=\"docutils literal notranslate\">ManyToManyField</code> 等）。有时可能可行，但不受支持。</p>\n<section id=\"swappable-dependencies\">\n<h3>可交换的依赖关系<a class=\"heading-anchor\" href=\"#swappable-dependencies\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<dl class=\"py function\">\n<dt class=\"sig sig-object py\" id=\"django.db.migrations.django.db.migrations.swappable_dependency\">\n<span class=\"sig-prename descclassname\">django.db.migrations.</span><span class=\"sig-name descname\">swappable_dependency</span><span class=\"sig-paren\">(</span><em class=\"sig-param\">value</em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.db.migrations.django.db.migrations.swappable_dependency\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd></dd></dl>\n\n<p><code class=\"docutils literal notranslate\">swappable_dependency()</code> 函数在迁移中用于声明对被交换模型的应用中的迁移的 &quot;可交换&quot; 依赖关系，当前是对该应用的第一个迁移的依赖。因此，被交换模型应该在初始迁移中创建。参数 <code class=\"docutils literal notranslate\">value</code> 是一个描述应用标签和模型名称的字符串 <code class=\"docutils literal notranslate\">&quot;&lt;app label&gt;.&lt;model&gt;&quot;</code>，例如 <code class=\"docutils literal notranslate\">&quot;myapp.MyModel&quot;</code>。</p>\n<p>使用 <code class=\"docutils literal notranslate\">swappable_dependency()</code>，您通知迁移框架迁移依赖于另一个设置可交换模型的迁移，从而允许将来可能替换模型的不同实现。通常用于引用可能需要自定义或替换的模型，例如 Django 认证系统中的自定义用户模型（<code class=\"docutils literal notranslate\">settings.AUTH_USER_MODEL</code>，默认为 <code class=\"docutils literal notranslate\">&quot;auth.User&quot;</code>）。</p>\n</section>\n</section>\n<section id=\"migration-files\">\n<span id=\"id2\"></span><h2>迁移文件<a class=\"heading-anchor\" href=\"#migration-files\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>迁移以磁盘格式存储，这里称为“迁移文件”。这些文件实际上是普通的 Python 文件，具有约定的对象布局，以声明式风格编写。</p>\n<p>基本的迁移文件如下所示：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span> <span class=\"nn\">django.db</span> <span class=\"kn\">import</span> migrations<span class=\"p\">,</span> models\n\n\n<span class=\"k\">class</span> <span class=\"nc\">Migration</span><span class=\"p\">(</span>migrations<span class=\"o\">.</span>Migration<span class=\"p\">):</span>\n    dependencies <span class=\"o\">=</span> <span class=\"p\">[(</span><span class=\"s2\">&quot;migrations&quot;</span><span class=\"p\">,</span> <span class=\"s2\">&quot;0001_initial&quot;</span><span class=\"p\">)]</span>\n\n    operations <span class=\"o\">=</span> <span class=\"p\">[</span>\n        migrations<span class=\"o\">.</span>DeleteModel<span class=\"p\">(</span><span class=\"s2\">&quot;Tribble&quot;</span><span class=\"p\">),</span>\n        migrations<span class=\"o\">.</span>AddField<span class=\"p\">(</span><span class=\"s2\">&quot;Author&quot;</span><span class=\"p\">,</span> <span class=\"s2\">&quot;rating&quot;</span><span class=\"p\">,</span> models<span class=\"o\">.</span>IntegerField<span class=\"p\">(</span>default<span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">)),</span>\n    <span class=\"p\">]</span>\n</code></pre></div>\n<p>Django 在加载迁移文件（作为 Python 模块）时寻找的是 <code class=\"docutils literal notranslate\">django.db.migrations.Migration</code> 的子类，称为 <code class=\"docutils literal notranslate\">Migration</code>。然后，它将检查此对象的四个属性，大多数情况下仅使用其中两个：</p>\n<ul class=\"simple\">\n<li><p><code class=\"docutils literal notranslate\">dependencies</code>，所依赖的迁移列表。</p></li>\n<li><p><code class=\"docutils literal notranslate\">operations</code>，定义了此次迁移操作的 <code class=\"docutils literal notranslate\">Operation</code> 类的列表。</p></li>\n</ul>\n<p>操作是关键；它们是一组声明性指令，它们告诉 Django 需要对哪些架构变更。Django 扫描它们并构建所有应用的所有架构变更的内存表示形式，然后使用它生成进行架构变更的 SQL。</p>\n<p>该内存结构还用于确定模型与迁移当前状态之间的差异；Django 按顺序在内存中的模型集上运行所有的变更，得出你上次运行 <code class=\"docutils literal notranslate\">makemigrations</code> 时模型的状态。然后，它使用这些模型与你的 <code class=\"docutils literal notranslate\">models.py</code> 文件中的模型进行比较，以计算出你改变了什么。</p>\n<p>你应该很少需要手动编辑迁移文件，但如果需要，完全可以手动编写。有些更复杂的操作是无法自动检测的，只能通过手写的迁移来实现，所以如果必须手写它们，也不要害怕。</p>\n<section id=\"custom-fields\">\n<h3>自定义字段<a class=\"heading-anchor\" href=\"#custom-fields\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>你不能修改一个已经迁移的自定义字段中的位置参数的数量，否则会引发 <code class=\"docutils literal notranslate\">TypeError</code>。旧的迁移会用旧的签名调用修改后的 <code class=\"docutils literal notranslate\">__init__</code> 方法。所以如果你需要一个新的参数，请创建一个关键字参数，并在构造函数中添加类似 <code class=\"docutils literal notranslate\">assert 'argument_name' in kwargs</code> 的内容。</p>\n</section>\n<section id=\"model-managers\">\n<span id=\"using-managers-in-migrations\"></span><h3>模型管理器<a class=\"heading-anchor\" href=\"#model-managers\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>你可以选择将管理器序列化为迁移，并在 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/migration-operations/#django.db.migrations.operations.RunPython\" title=\"django.db.migrations.operations.RunPython\"><code class=\"xref py py-class docutils literal notranslate\">RunPython</code></a> 操作中使用它们。这是通过在 manager 类上定义一个 <code class=\"docutils literal notranslate\">use_in_migrations</code> 属性来实现的：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span> <span class=\"nc\">MyManager</span><span class=\"p\">(</span>models<span class=\"o\">.</span>Manager<span class=\"p\">):</span>\n    use_in_migrations <span class=\"o\">=</span> <span class=\"kc\">True</span>\n\n\n<span class=\"k\">class</span> <span class=\"nc\">MyModel</span><span class=\"p\">(</span>models<span class=\"o\">.</span>Model<span class=\"p\">):</span>\n    objects <span class=\"o\">=</span> MyManager<span class=\"p\">()</span>\n</code></pre></div>\n<p>如果你使用 <a class=\"reference internal\" href=\"/zh-hans/6.1/topics/db/managers/#django.db.models.from_queryset\" title=\"django.db.models.from_queryset\"><code class=\"xref py py-meth docutils literal notranslate\">from_queryset()</code></a> 函数动态生成管理器类，则需要从生成的类继承以使其可导入：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span> <span class=\"nc\">MyManager</span><span class=\"p\">(</span>MyBaseManager<span class=\"o\">.</span>from_queryset<span class=\"p\">(</span>CustomQuerySet<span class=\"p\">)):</span>\n    use_in_migrations <span class=\"o\">=</span> <span class=\"kc\">True</span>\n\n\n<span class=\"k\">class</span> <span class=\"nc\">MyModel</span><span class=\"p\">(</span>models<span class=\"o\">.</span>Model<span class=\"p\">):</span>\n    objects <span class=\"o\">=</span> MyManager<span class=\"p\">()</span>\n</code></pre></div>\n<p>请参考关于 <a class=\"reference internal\" href=\"#historical-models\"><span class=\"std std-ref\">历史模型</span></a> 在迁移中的说明，以了解随之而来的影响。</p>\n</section>\n<section id=\"initial-migrations\">\n<h3>初始迁移<a class=\"heading-anchor\" href=\"#initial-migrations\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<dl class=\"py attribute\">\n<dt class=\"sig sig-object py\" id=\"django.db.migrations.Migration.initial\">\n<span class=\"sig-prename descclassname\">Migration.</span><span class=\"sig-name descname\">initial</span><a class=\"heading-anchor\" href=\"#django.db.migrations.Migration.initial\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd></dd></dl>\n\n<p>应用的“初始迁移”是创建该应用首版表的迁移。 通常，一个应用有一个初始迁移，但是在某些情况下，复杂的模型依赖可能会导致两个或更多。</p>\n<p>初始迁移在迁移类上标有 <code class=\"docutils literal notranslate\">initial = True</code> 类属性。如果未找到 <code class=\"docutils literal notranslate\">initial</code> 类属性，则如果迁移是应用程序中的第一个迁移（即，如果它不依赖于同一应用程序中的任何其他迁移）则将被视为“初始”。</p>\n<p>当使用 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#cmdoption-migrate-fake-initial\"><code class=\"xref std std-option docutils literal notranslate\">migrate --fake-initial</code></a> 选项时，将对这些初始迁移进行特殊处理。对于创建一个或多个表（<code class=\"docutils literal notranslate\">CreateModel</code> 操作）的初始迁移，Django 会检查所有这些表是否已经存在于数据库中，如果是，则对迁移进行假应用。 类似地，对于添加了一个或多个字段（<code class=\"docutils literal notranslate\">AddField</code> 操作）的初始迁移，Django 检查数据库中是否已存在所有相应的列，如果存在，则对迁移进行假应用。如果没有 <code class=\"docutils literal notranslate\">--fake-initial</code>，初始迁移的处理方式和其他迁移没有区别。</p>\n</section>\n<section id=\"history-consistency\">\n<span id=\"migration-history-consistency\"></span><h3>历史一致性<a class=\"heading-anchor\" href=\"#history-consistency\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>历史一致性前面已经讨论过，当两个开发分支加入时，你可能需要手动线性化迁移。在编辑迁移依赖关系时，你可能会无意中创建一个不一致的历史状态，即一个迁移已经被应用，但它的一些依赖关系还没有应用。这强烈地表明依赖关系不正确，所以 Django 会拒绝运行迁移或进行新的迁移，直到它被修复。当使用多个数据库时，可以使用 <a class=\"reference internal\" href=\"/zh-hans/6.1/topics/db/multi-db/#topics-db-multi-db-routing\"><span class=\"std std-ref\">database routers</span></a> 的 <a class=\"reference internal\" href=\"/zh-hans/6.1/topics/db/multi-db/#allow_migrate\" title=\"allow_migrate\"><code class=\"xref py py-meth docutils literal notranslate\">allow_migrate()</code></a> 方法来控制 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#django-admin-makemigrations\"><code class=\"xref std std-djadmin docutils literal notranslate\">makemigrations</code></a> 检查哪些数据库的历史一致。</p>\n</section>\n</section>\n<section id=\"adding-migrations-to-apps\">\n<h2>向应用添加迁移<a class=\"heading-anchor\" href=\"#adding-migrations-to-apps\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>新的应用已预先配置为接受迁移，因此你可以在进行一些更改后通过运行 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#django-admin-makemigrations\"><code class=\"xref std std-djadmin docutils literal notranslate\">makemigrations</code></a> 添加迁移。</p>\n<p>如果您的应用程序已经拥有模型和数据库表，但尚未进行迁移（例如，您是在以前的 Django 版本中创建的），您需要通过运行以下命令将其转换为使用迁移：</p>\n<div class=\"code-block\" data-language=\"shell\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code>$ python manage.py makemigrations your_app_label\n</code></pre></div>\n<p>这将为你的应用程序进行新的初始迁移。现在，运行 <code class=\"docutils literal notranslate\">python manage.py migrate --fake-initial</code>，Django 将检测到你有一个初始迁移 <em>并且</em> 它要创建的表已经存在，而将迁移标记为已应用。（如果没有 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#cmdoption-migrate-fake-initial\"><code class=\"xref std std-option docutils literal notranslate\">migrate --fake-initial</code></a> 标志，该命令将出错，因为它要创建的表已经存在。）</p>\n<p>请注意，这只适用于以下两种情况：</p>\n<ul class=\"simple\">\n<li><p>自从你建立了表之后，你就没有改变过你的模型。要使迁移生效，你必须 <em>首先</em> 进行初始迁移，然后再进行更改，因为 Django 将变更与迁移文件（而不是数据库）进行比较。</p></li>\n<li><p>你尚未手动编辑数据库——Django 无法检测到你的数据库与你的模型不匹配，当迁移尝试修改这些表时，你只会得到错误。</p></li>\n</ul>\n</section>\n<section id=\"reversing-migrations\">\n<span id=\"id3\"></span><h2>撤销迁移<a class=\"heading-anchor\" href=\"#reversing-migrations\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>可以通过 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#django-admin-migrate\"><code class=\"xref std std-djadmin docutils literal notranslate\">migrate</code></a> 传递上一次迁移的编号来撤销迁移。例如，要撤销迁移 <code class=\"docutils literal notranslate\">books.0003</code>：</p>\n<div class=\"console\" data-console><div class=\"console-panel\" data-platform=\"unix\"><p class=\"console-label\" id=\"console-0-unix-label\">Linux / macOS</p><div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>python manage.py migrate books <span class=\"m\">0002</span>\n<span class=\"go\">Operations to perform:</span>\n<span class=\"go\">  Target specific migration: 0002_auto, from books</span>\n<span class=\"go\">Running migrations:</span>\n<span class=\"go\">  Rendering model states... DONE</span>\n<span class=\"go\">  Unapplying books.0003_auto... OK</span>\n</code></pre></div>\n</div><div class=\"console-panel\" data-platform=\"windows\"><p class=\"console-label\" id=\"console-0-windows-label\">Windows</p><div class=\"code-block\" data-language=\"doscon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Windows</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Windows shell\"><code><span class=\"gp\">...\\&gt;</span> py manage.py migrate books 0002\n<span class=\"go\">Operations to perform:</span>\n<span class=\"go\">  Target specific migration: 0002_auto, from books</span>\n<span class=\"go\">Running migrations:</span>\n<span class=\"go\">  Rendering model states... DONE</span>\n<span class=\"go\">  Unapplying books.0003_auto... OK</span>\n</code></pre></div></div></div>\n<p>如果要撤消应用于一个应用的所有迁移，请使用名称 <code class=\"docutils literal notranslate\">zero</code>：</p>\n<div class=\"console\" data-console><div class=\"console-panel\" data-platform=\"unix\"><p class=\"console-label\" id=\"console-1-unix-label\">Linux / macOS</p><div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>python manage.py migrate books zero\n<span class=\"go\">Operations to perform:</span>\n<span class=\"go\">  Unapply all migrations: books</span>\n<span class=\"go\">Running migrations:</span>\n<span class=\"go\">  Rendering model states... DONE</span>\n<span class=\"go\">  Unapplying books.0002_auto... OK</span>\n<span class=\"go\">  Unapplying books.0001_initial... OK</span>\n</code></pre></div>\n</div><div class=\"console-panel\" data-platform=\"windows\"><p class=\"console-label\" id=\"console-1-windows-label\">Windows</p><div class=\"code-block\" data-language=\"doscon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Windows</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Windows shell\"><code><span class=\"gp\">...\\&gt;</span> py manage.py migrate books zero\n<span class=\"go\">Operations to perform:</span>\n<span class=\"go\">  Unapply all migrations: books</span>\n<span class=\"go\">Running migrations:</span>\n<span class=\"go\">  Rendering model states... DONE</span>\n<span class=\"go\">  Unapplying books.0002_auto... OK</span>\n<span class=\"go\">  Unapplying books.0001_initial... OK</span>\n</code></pre></div></div></div>\n<p>如果迁移包含任何不可逆的操作，则该迁移是不可逆的。 试图撤销这种迁移将引发 <code class=\"docutils literal notranslate\">IrreversibleError</code>：</p>\n<div class=\"console\" data-console><div class=\"console-panel\" data-platform=\"unix\"><p class=\"console-label\" id=\"console-2-unix-label\">Linux / macOS</p><div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>python manage.py migrate books <span class=\"m\">0002</span>\n<span class=\"go\">Operations to perform:</span>\n<span class=\"go\">  Target specific migration: 0002_auto, from books</span>\n<span class=\"go\">Running migrations:</span>\n<span class=\"go\">  Rendering model states... DONE</span>\n<span class=\"go\">  Unapplying books.0003_auto...Traceback (most recent call last):</span>\n<span class=\"go\">django.db.migrations.exceptions.IrreversibleError: Operation &lt;RunSQL  sql=&#39;DROP TABLE demo_books&#39;&gt; in books.0003_auto is not reversible</span>\n</code></pre></div>\n</div><div class=\"console-panel\" data-platform=\"windows\"><p class=\"console-label\" id=\"console-2-windows-label\">Windows</p><div class=\"code-block\" data-language=\"doscon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Windows</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Windows shell\"><code><span class=\"gp\">...\\&gt;</span> py manage.py migrate books 0002\n<span class=\"go\">Operations to perform:</span>\n<span class=\"go\">  Target specific migration: 0002_auto, from books</span>\n<span class=\"go\">Running migrations:</span>\n<span class=\"go\">  Rendering model states... DONE</span>\n<span class=\"go\">  Unapplying books.0003_auto...Traceback (most recent call last):</span>\n<span class=\"gp\">django.db.migrations.exceptions.IrreversibleError: Operation &lt;RunSQL  sql=&#39;DROP TABLE demo_books&#39;&gt;</span> in books.0003_auto is not reversible\n</code></pre></div></div></div>\n</section>\n<section id=\"historical-models\">\n<span id=\"id4\"></span><h2>历史模型<a class=\"heading-anchor\" href=\"#historical-models\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>当你运行迁移时，Django 正在使用存储在迁移文件中的模型的历史版本。如果你使用 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/migration-operations/#django.db.migrations.operations.RunPython\" title=\"django.db.migrations.operations.RunPython\"><code class=\"xref py py-class docutils literal notranslate\">RunPython</code></a> 操作编写 Python 代码，或者你的数据库路由上有 <code class=\"docutils literal notranslate\">allow_migrate</code> 方法，则你 <strong>需要使用</strong> 这些模型的历史版本而不是直接导入它们。</p>\n<aside class=\"admonition admonition-warning\" role=\"note\">\n<p class=\"admonition-title\">Warning</p>\n<p>如果您直接导入模型而不是使用历史模型，您的迁移 <em>可能在初始阶段工作</em>，但在将来尝试重新运行旧迁移时将失败（通常是在设置新安装并运行所有迁移以设置数据库时）。</p>\n<p>这意味着历史模型的问题可能不会立即显现。如果遇到这种故障，可以编辑迁移以使用历史模型，而不是直接导入并提交这些更改。</p>\n</aside>\n<p>因为不可能序列化任意的 Python 代码，这些历史模型不会有你定义的任何自定义方法。然而，它们将具有相同的字段、关系、管理器（仅限于那些具有 <code class=\"docutils literal notranslate\">use_in_migrations = True</code>）和 <code class=\"docutils literal notranslate\">Meta</code> 选项（也有版本控制，因此它们可能与当前的不同）。</p>\n<aside class=\"admonition admonition-warning\" role=\"note\">\n<p class=\"admonition-title\">Warning</p>\n<p>这意味着在迁移中访问对象时，将不会对对象调用自定义的 <code class=\"docutils literal notranslate\">save()</code> 方法，也不会有任何自定义构造函数或实例方法。适当的计划一下吧！</p>\n</aside>\n<p>字段选项中对函数的引用，例如 <code class=\"docutils literal notranslate\">upload_to</code> 和 <code class=\"docutils literal notranslate\">limit_choices_to</code> 以及具有 <code class=\"docutils literal notranslate\">use_in_migrations = True</code> 的模型管理器声明，都会在迁移中序列化，因此只要有迁移引用它们，这些函数和类就需要保留。任何 <a class=\"reference internal\" href=\"/zh-hans/6.1/howto/custom-model-fields/\"><span class=\"doc\">自定义模型字段</span></a> 也需要保留，因为这些都是直接由迁移导入的。</p>\n<p>此外，模型的具体基类是以指针的形式存储的，所以只要有一个包含对它们的引用的迁移，你就必须始终将基类保留在身边。从好的方面来说，这些基类的方法和管理器都是正常继承的，所以如果你一定需要访问这些，你可以选择将它们移到一个父类中。</p>\n<p>要删除旧的引用，你可以 <a class=\"reference internal\" href=\"#migration-squashing\"><span class=\"std std-ref\">压缩迁移</span></a> 或者，如果引用不多，把它们复制到迁移文件中。</p>\n</section>\n<section id=\"considerations-when-removing-model-fields\">\n<span id=\"migrations-removing-model-fields\"></span><h2>删除模型字段时的注意事项<a class=\"heading-anchor\" href=\"#considerations-when-removing-model-fields\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>与上一节中描述的“引用历史函数”注意事项类似，如果在旧迁移中引用了自定义模型字段，则从项目或第三方应用中删除这些字段将导致问题。</p>\n<p>为了解决这种情况，Django 提供了一些模型字段属性，使用 <a class=\"reference internal\" href=\"/zh-hans/6.1/topics/checks/\"><span class=\"doc\">系统检查框架</span></a> 来协助弃用模型字段。</p>\n<p>将 <code class=\"docutils literal notranslate\">system_check_deprecated_details</code> 属性添加到你的模型字段中，类似于：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span> <span class=\"nc\">IPAddressField</span><span class=\"p\">(</span>Field<span class=\"p\">):</span>\n    system_check_deprecated_details <span class=\"o\">=</span> <span class=\"p\">{</span>\n        <span class=\"s2\">&quot;msg&quot;</span><span class=\"p\">:</span> <span class=\"p\">(</span>\n            <span class=\"s2\">&quot;IPAddressField has been deprecated. Support for it (except &quot;</span>\n            <span class=\"s2\">&quot;in historical migrations) will be removed in Django 1.9.&quot;</span>\n        <span class=\"p\">),</span>\n        <span class=\"s2\">&quot;hint&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;Use GenericIPAddressField instead.&quot;</span><span class=\"p\">,</span>  <span class=\"c1\"># optional</span>\n        <span class=\"s2\">&quot;id&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;fields.W900&quot;</span><span class=\"p\">,</span>  <span class=\"c1\"># pick a unique ID for your field.</span>\n    <span class=\"p\">}</span>\n</code></pre></div>\n<p>在你选择的弃用期（Django 本身的字段有两个或三个功能版本）之后，将 <code class=\"docutils literal notranslate\">system_check_deprecated_details</code> 属性改为 <code class=\"docutils literal notranslate\">system_check_removed_details</code> 并更新类似于以下内容的字典：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span> <span class=\"nc\">IPAddressField</span><span class=\"p\">(</span>Field<span class=\"p\">):</span>\n    system_check_removed_details <span class=\"o\">=</span> <span class=\"p\">{</span>\n        <span class=\"s2\">&quot;msg&quot;</span><span class=\"p\">:</span> <span class=\"p\">(</span>\n            <span class=\"s2\">&quot;IPAddressField has been removed except for support in &quot;</span>\n            <span class=\"s2\">&quot;historical migrations.&quot;</span>\n        <span class=\"p\">),</span>\n        <span class=\"s2\">&quot;hint&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;Use GenericIPAddressField instead.&quot;</span><span class=\"p\">,</span>\n        <span class=\"s2\">&quot;id&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;fields.E900&quot;</span><span class=\"p\">,</span>  <span class=\"c1\"># pick a unique ID for your field.</span>\n    <span class=\"p\">}</span>\n</code></pre></div>\n<p>你应该保留该字段在数据库迁移中操作所需的方法，如 <code class=\"docutils literal notranslate\">__init__()</code>，<code class=\"docutils literal notranslate\">deconstruct()</code>，和 <code class=\"docutils literal notranslate\">get_internal_type()</code>。只要任何引用该字段的迁移存在，就保留这个存根字段。例如，在压缩迁移并删除旧的迁移后，你应该可以完全删除该字段。</p>\n</section>\n<section id=\"data-migrations\">\n<span id=\"id5\"></span><h2>数据迁移<a class=\"heading-anchor\" href=\"#data-migrations\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>除了改变数据库架构外，你还可以使用迁移来改变数据库本身的数据，如果你想的话，还可以结合架构来改变。</p>\n<p>更改数据的迁移通常称为“数据迁移”；最好将它们写成单独的迁移，与架构迁移放在一起。</p>\n<p>Django 无法像架构迁移那样自动为您生成数据迁移，但是编写它们并不难。Django 中的迁移文件是由 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/migration-operations/\"><span class=\"doc\">Operations</span></a> 组成的，你用于数据迁移的主要操作是 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/migration-operations/#django.db.migrations.operations.RunPython\" title=\"django.db.migrations.operations.RunPython\"><code class=\"xref py py-class docutils literal notranslate\">RunPython</code></a>。</p>\n<p>要开始，请创建一个空的迁移文件，您可以从中开始工作（Django 将会将文件放在正确的位置，为您建议一个名称，并添加依赖项）：</p>\n<div class=\"code-block\" data-language=\"shell\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code>python manage.py makemigrations --empty yourappname\n</code></pre></div>\n<p>然后，打开文件；它应该是这样的：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"c1\"># Generated by Django A.B on YYYY-MM-DD HH:MM</span>\n<span class=\"kn\">from</span> <span class=\"nn\">django.db</span> <span class=\"kn\">import</span> migrations\n\n\n<span class=\"k\">class</span> <span class=\"nc\">Migration</span><span class=\"p\">(</span>migrations<span class=\"o\">.</span>Migration<span class=\"p\">):</span>\n    dependencies <span class=\"o\">=</span> <span class=\"p\">[</span>\n        <span class=\"p\">(</span><span class=\"s2\">&quot;yourappname&quot;</span><span class=\"p\">,</span> <span class=\"s2\">&quot;0001_initial&quot;</span><span class=\"p\">),</span>\n    <span class=\"p\">]</span>\n\n    operations <span class=\"o\">=</span> <span class=\"p\">[]</span>\n</code></pre></div>\n<p>Now, all you need to do is create a new function and have\n<a class=\"reference internal\" href=\"/zh-hans/6.1/ref/migration-operations/#django.db.migrations.operations.RunPython\" title=\"django.db.migrations.operations.RunPython\"><code class=\"xref py py-class docutils literal notranslate\">RunPython</code></a> use it.\n<a class=\"reference internal\" href=\"/zh-hans/6.1/ref/migration-operations/#django.db.migrations.operations.RunPython\" title=\"django.db.migrations.operations.RunPython\"><code class=\"xref py py-class docutils literal notranslate\">RunPython</code></a> expects a callable as its\nargument which takes two arguments - the first is an <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/applications/\"><span class=\"doc\">app registry</span></a> that has the historical versions of all your models\nloaded into it to match where in your history the migration sits, and the\nsecond is a <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/schema-editor/\"><span class=\"doc\">SchemaEditor</span></a>, which you can use to\nmanually effect database schema changes (but beware, doing this can confuse\nthe migration autodetector).</p>\n<p>让我们编写一个迁移，使用 <code class=\"docutils literal notranslate\">first_name</code> 和 <code class=\"docutils literal notranslate\">last_name</code> 的组合值填充新的 <code class=\"docutils literal notranslate\">name</code> 字段（我们已经意识到，并不是每个人都有名字和姓氏）。 我们需要做的就是使用历史模型并对行进行迭代：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span> <span class=\"nn\">django.db</span> <span class=\"kn\">import</span> migrations\n\n\n<span class=\"k\">def</span> <span class=\"nf\">combine_names</span><span class=\"p\">(</span>apps<span class=\"p\">,</span> schema_editor<span class=\"p\">):</span>\n    <span class=\"c1\"># We can&#39;t import the Person model directly as it may be a newer</span>\n    <span class=\"c1\"># version than this migration expects. We use the historical version.</span>\n    Person <span class=\"o\">=</span> apps<span class=\"o\">.</span>get_model<span class=\"p\">(</span><span class=\"s2\">&quot;yourappname&quot;</span><span class=\"p\">,</span> <span class=\"s2\">&quot;Person&quot;</span><span class=\"p\">)</span>\n    <span class=\"k\">for</span> person <span class=\"ow\">in</span> Person<span class=\"o\">.</span>objects<span class=\"o\">.</span>all<span class=\"p\">():</span>\n        person<span class=\"o\">.</span>name <span class=\"o\">=</span> <span class=\"sa\">f</span><span class=\"s2\">&quot;</span><span class=\"si\">{</span>person<span class=\"o\">.</span>first_name<span class=\"si\">}</span><span class=\"s2\"> </span><span class=\"si\">{</span>person<span class=\"o\">.</span>last_name<span class=\"si\">}</span><span class=\"s2\">&quot;</span>\n        person<span class=\"o\">.</span>save<span class=\"p\">()</span>\n\n\n<span class=\"k\">class</span> <span class=\"nc\">Migration</span><span class=\"p\">(</span>migrations<span class=\"o\">.</span>Migration<span class=\"p\">):</span>\n    dependencies <span class=\"o\">=</span> <span class=\"p\">[</span>\n        <span class=\"p\">(</span><span class=\"s2\">&quot;yourappname&quot;</span><span class=\"p\">,</span> <span class=\"s2\">&quot;0001_initial&quot;</span><span class=\"p\">),</span>\n    <span class=\"p\">]</span>\n\n    operations <span class=\"o\">=</span> <span class=\"p\">[</span>\n        migrations<span class=\"o\">.</span>RunPython<span class=\"p\">(</span>combine_names<span class=\"p\">),</span>\n    <span class=\"p\">]</span>\n</code></pre></div>\n<p>完成后，我们可以像往常一样运行 <code class=\"docutils literal notranslate\">python manage.py migrate</code>，数据迁移将与其他迁移一起运行。</p>\n<p>您可以将第二个可调用对象传递给 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/migration-operations/#django.db.migrations.operations.RunPython\" title=\"django.db.migrations.operations.RunPython\"><code class=\"xref py py-class docutils literal notranslate\">RunPython</code></a> 以运行撤销迁移时要执行的任何逻辑。 如果忽略此可调用对象，则撤销迁移将引发异常。</p>\n<section id=\"accessing-models-from-other-apps\">\n<h3>从其他应用访问模型<a class=\"heading-anchor\" href=\"#accessing-models-from-other-apps\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>在编写使用来自迁移所在应用以外的其他应用模型的 <code class=\"docutils literal notranslate\">RunPython</code> 函数时，迁移的 <code class=\"docutils literal notranslate\">dependencies</code> 属性应包括所涉及的每个应用程序的最新迁移，否则当你尝试使用 <code class=\"docutils literal notranslate\">apps.get_model()</code> 在 <code class=\"docutils literal notranslate\">RunPython</code> 函数中获取模型时，你可能会得到 <code class=\"docutils literal notranslate\">LookupError: No installed app with label 'myappname'</code>。</p>\n<p>在下面的例子中，我们在 <code class=\"docutils literal notranslate\">app1</code> 中进行迁移，需要使用 <code class=\"docutils literal notranslate\">app2</code> 中的模型。我们不关心 <code class=\"docutils literal notranslate\">move_m1</code> 的细节，只关心它需要访问两个应用程序的模型。因此，我们添加了一个依赖关系，指定 <code class=\"docutils literal notranslate\">app2</code> 最后一次迁移：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span> <span class=\"nc\">Migration</span><span class=\"p\">(</span>migrations<span class=\"o\">.</span>Migration<span class=\"p\">):</span>\n    dependencies <span class=\"o\">=</span> <span class=\"p\">[</span>\n        <span class=\"p\">(</span><span class=\"s2\">&quot;app1&quot;</span><span class=\"p\">,</span> <span class=\"s2\">&quot;0001_initial&quot;</span><span class=\"p\">),</span>\n        <span class=\"c1\"># added dependency to enable using models from app2 in move_m1</span>\n        <span class=\"p\">(</span><span class=\"s2\">&quot;app2&quot;</span><span class=\"p\">,</span> <span class=\"s2\">&quot;0004_foobar&quot;</span><span class=\"p\">),</span>\n    <span class=\"p\">]</span>\n\n    operations <span class=\"o\">=</span> <span class=\"p\">[</span>\n        migrations<span class=\"o\">.</span>RunPython<span class=\"p\">(</span>move_m1<span class=\"p\">),</span>\n    <span class=\"p\">]</span>\n</code></pre></div>\n</section>\n<section id=\"more-advanced-migrations\">\n<h3>更高级的迁移<a class=\"heading-anchor\" href=\"#more-advanced-migrations\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>如果你对更高级的迁移操作感兴趣，或者希望能够编写自己的迁移操作，请参阅 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/migration-operations/\"><span class=\"doc\">迁移操作参考</span></a> 和“如何” <a class=\"reference internal\" href=\"/zh-hans/6.1/howto/writing-migrations/\"><span class=\"doc\">编写操作</span></a>。</p>\n</section>\n</section>\n<section id=\"squashing-migrations\">\n<span id=\"migration-squashing\"></span><h2>压缩迁移<a class=\"heading-anchor\" href=\"#squashing-migrations\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>我们鼓励你自由地进行迁移，而不要担心你有多少迁移；迁移代码经过优化，可以一次处理几百个迁移，而不会有太多的减速。然而，最终你会希望从几百个迁移回归到只有几个，这就是压缩的作用。</p>\n<p>压缩是将一组现有的多个迁移减少到一个（有时是几个）迁移，这些迁移仍然代表相同的更改。</p>\n<p>Django通过获取所有现有迁移，提取它们的 <code class=\"docutils literal notranslate\">Operation</code> 并将它们按顺序排列，然后对它们运行一个优化器，以尝试减少列表的长度——例如，它知道 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/migration-operations/#django.db.migrations.operations.CreateModel\" title=\"django.db.migrations.operations.CreateModel\"><code class=\"xref py py-class docutils literal notranslate\">CreateModel</code></a> 和 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/migration-operations/#django.db.migrations.operations.DeleteModel\" title=\"django.db.migrations.operations.DeleteModel\"><code class=\"xref py py-class docutils literal notranslate\">DeleteModel</code></a> 相互抵消，它还知道 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/migration-operations/#django.db.migrations.operations.AddField\" title=\"django.db.migrations.operations.AddField\"><code class=\"xref py py-class docutils literal notranslate\">AddField</code></a> 可以卷入 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/migration-operations/#django.db.migrations.operations.CreateModel\" title=\"django.db.migrations.operations.CreateModel\"><code class=\"xref py py-class docutils literal notranslate\">CreateModel</code></a>。</p>\n<p>一旦操作序列被尽可能地减少——可能的数量取决于你的模型有多紧密交织，如果你有任何 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/migration-operations/#django.db.migrations.operations.RunSQL\" title=\"django.db.migrations.operations.RunSQL\"><code class=\"xref py py-class docutils literal notranslate\">RunSQL</code></a> 或 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/migration-operations/#django.db.migrations.operations.RunPython\" title=\"django.db.migrations.operations.RunPython\"><code class=\"xref py py-class docutils literal notranslate\">RunPython</code></a> 操作（除非它们被标记为 <code class=\"docutils literal notranslate\">elidable</code>，否则无法被优化），Django就会把它写回一组新的迁移文件中。</p>\n<p>These files are marked to say they replace the previously-squashed migrations,\nso they can coexist with the old migration files, and Django will intelligently\nswitch between them depending on where you are in the history. If you're still\npart-way through the set of migrations that you squashed, it will keep using\nthem until it hits the end and then switch to the squashed history, while new\ninstalls will use the new squashed migration and skip all the old ones.</p>\n<p>这样你就可以压缩而不至于把目前还没有完全更新的生产系统搞乱。推荐的流程是压缩，保留旧文件，提交并发布，等到所有系统都升级到新版本（或者如果你是第三方项目，确保你的用户按顺序升级版本，不跳过任何一个版本），然后删除旧文件，提交并进行第二次发布。</p>\n<p>支持所有这些操作的命令是 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#django-admin-squashmigrations\"><code class=\"xref std std-djadmin docutils literal notranslate\">squashmigrations</code></a> - 传递给它您想要合并的应用程序标签和迁移名称，它将开始工作：</p>\n<div class=\"code-block\" data-language=\"shell\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code>$ ./manage.py squashmigrations myapp <span class=\"m\">0004</span>\nWill squash the following migrations:\n - 0001_initial\n - 0002_some_change\n - 0003_another_change\n - 0004_undo_something\nDo you wish to proceed? <span class=\"o\">[</span>y/N<span class=\"o\">]</span> y\nOptimizing...\n  Optimized from <span class=\"m\">12</span> operations to <span class=\"m\">7</span> operations.\nCreated new squashed migration /home/andrew/Programs/DjangoTest/test/migrations/0001_squashed_0004_undo_something.py\n  You should commit this migration but leave the old ones <span class=\"k\">in</span> place<span class=\"p\">;</span>\n  the new migration will be used <span class=\"k\">for</span> new installs. Once you are sure\n  all instances of the codebase have applied the migrations you squashed,\n  you can delete them.\n</code></pre></div>\n<p>如果要设置压缩迁移的名称而不是使用自动生成的迁移名称，请使用 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#cmdoption-squashmigrations-squashed-name\"><code class=\"xref std std-option docutils literal notranslate\">squashmigrations --squashed-name</code></a> 选项。</p>\n<p>请注意，Django 中的模型相互依赖可能会变得非常复杂，压缩可能会导致迁移无法运行；要么是优化错误（在这种情况下，你可以用 <code class=\"docutils literal notranslate\">--no-optimize</code> 再试一次，不过你也应该报告这个问题），要么是 <code class=\"docutils literal notranslate\">CircularDependencyError</code>，在这种情况下，你可以手动解决它。</p>\n<p>要手动解决 <code class=\"docutils literal notranslate\">CircularDependencyError</code> 问题，请将循环依赖中的外键分离到单独的迁移中，并将依赖项移到另一个应用上。如果你不确定，请参见 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#django-admin-makemigrations\"><code class=\"xref std std-djadmin docutils literal notranslate\">makemigrations</code></a> 在被要求从模型创建全新的迁移时如何处理问题。在未来的 Django 版本中，<a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#django-admin-squashmigrations\"><code class=\"xref std std-djadmin docutils literal notranslate\">squashmigrations</code></a> 将被更新以尝试自己解决这些错误。</p>\n<p>一旦你压缩了你的迁移，你应该把它和它所替代的迁移一起提交，并把这个更改分发到你的应用程序的所有运行中的实例，确保它们运行 <code class=\"docutils literal notranslate\">migrate</code> 来将更改存储在它们的数据库中。</p>\n<p>You can then transition the squashed migration to a normal migration by:</p>\n<ul class=\"simple\">\n<li><p>删除它替换的所有迁移文件。</p></li>\n<li><p>将所有依赖被删除迁移的迁移更新为依赖被压缩的迁移。</p></li>\n<li><p>删除压缩迁移的 <code class=\"docutils literal notranslate\">Migration</code> 类的 <code class=\"docutils literal notranslate\">replaces</code> 属性（这就是 Django 告诉它是压缩迁移的方式）。</p></li>\n</ul>\n<aside class=\"admonition admonition-note\" role=\"note\">\n<p class=\"admonition-title\">Note</p>\n<p>You can squash squashed migrations themselves without transitioning to\nnormal migrations, which might be useful for situations where every\nenvironment has not yet run the original squashed migration set. But in\ngeneral it is better to transition squashed migrations to normal migrations\nto be able to clean up older migration files.</p>\n</aside>\n<aside class=\"admonition-pruning-references-to-deleted-migrations admonition\" role=\"note\">\n<p class=\"admonition-title\">修剪已删除的迁移的引用</p>\n<p>如果可能会在将来重复使用已删除的迁移的名称，您应该使用 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#cmdoption-migrate-prune\"><code class=\"xref std std-option docutils literal notranslate\">migrate --prune</code></a> 选项从 Django 的迁移表中删除对它的引用。</p>\n</aside>\n<aside class=\"version-note version-changed\" data-version=\"6.0\" role=\"note\">\n<p class=\"version-note-title\">Changed in Django 6.0</p><p>Support for squashing squashed migrations was added.</p>\n</aside>\n</section>\n<section id=\"serializing-values\">\n<span id=\"migration-serializing\"></span><h2>序列化值<a class=\"heading-anchor\" href=\"#serializing-values\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>迁移是包含模型旧定义的 Python 文件，因此，要编写它们，Django 必须获取模型的当前状态并将它们序列化到一个文件中。</p>\n<p>虽然 Django 可以序列化大多数内容，但有些内容我们无法序列化为有效的 Python 表示形式——对于如何将值转换回代码，没有 Python 标准（<code class=\"docutils literal notranslate\">repr()</code> 只适用于基本的值，而且没有指定导入路径）。</p>\n<p>Django 可以序列化以下内容：</p>\n<ul class=\"simple\">\n<li><p><code class=\"docutils literal notranslate\">int</code>，<code class=\"docutils literal notranslate\">float</code>，<code class=\"docutils literal notranslate\">bool</code>，<code class=\"docutils literal notranslate\">str</code>，<code class=\"docutils literal notranslate\">bytes</code>，<code class=\"docutils literal notranslate\">None</code>，<code class=\"docutils literal notranslate\">NoneType</code></p></li>\n<li><p><code class=\"docutils literal notranslate\">list</code>, <code class=\"docutils literal notranslate\">set</code>, <code class=\"docutils literal notranslate\">tuple</code>, <code class=\"docutils literal notranslate\">dict</code>, <code class=\"docutils literal notranslate\">range</code></p></li>\n<li><p><code class=\"docutils literal notranslate\">datetime.date</code>，<code class=\"docutils literal notranslate\">datetime.time</code> 和 <code class=\"docutils literal notranslate\">datetime.datetime</code> 实例（包括可识别时区的实例）</p></li>\n<li><p><a class=\"reference external\" href=\"https://docs.python.org/3/library/zoneinfo.html#zoneinfo.ZoneInfo\" title=\"(in Python v3.14)\"><code class=\"xref py py-class docutils literal notranslate\">zoneinfo.ZoneInfo</code></a> instances</p></li>\n<li><p><code class=\"docutils literal notranslate\">decimal.Decimal</code> 实例</p></li>\n<li><p><code class=\"docutils literal notranslate\">enum.Enum</code> 和 <code class=\"docutils literal notranslate\">enum.Flag</code> 实例</p></li>\n<li><p><code class=\"docutils literal notranslate\">uuid.UUID</code> 实例</p></li>\n<li><p><a class=\"reference external\" href=\"https://docs.python.org/3/library/functools.html#functools.partial\" title=\"(in Python v3.14)\"><code class=\"xref py py-func docutils literal notranslate\">functools.partial()</code></a> and <a class=\"reference external\" href=\"https://docs.python.org/3/library/functools.html#functools.partialmethod\" title=\"(in Python v3.14)\"><code class=\"xref py py-class docutils literal notranslate\">functools.partialmethod</code></a> instances\nwhich have serializable <code class=\"docutils literal notranslate\">func</code>, <code class=\"docutils literal notranslate\">args</code>, and <code class=\"docutils literal notranslate\">keywords</code> values</p></li>\n<li><p>Pure and concrete path objects from <a class=\"reference external\" href=\"https://docs.python.org/3/library/pathlib.html#module-pathlib\" title=\"(in Python v3.14)\"><code class=\"xref py py-mod docutils literal notranslate\">pathlib</code></a>. Concrete paths are\nconverted to their pure path equivalent, e.g. <a class=\"reference external\" href=\"https://docs.python.org/3/library/pathlib.html#pathlib.PosixPath\" title=\"(in Python v3.14)\"><code class=\"xref py py-class docutils literal notranslate\">pathlib.PosixPath</code></a> to\n<a class=\"reference external\" href=\"https://docs.python.org/3/library/pathlib.html#pathlib.PurePosixPath\" title=\"(in Python v3.14)\"><code class=\"xref py py-class docutils literal notranslate\">pathlib.PurePosixPath</code></a></p></li>\n<li><p><a class=\"reference external\" href=\"https://docs.python.org/3/library/os.html#os.PathLike\" title=\"(in Python v3.14)\"><code class=\"xref py py-class docutils literal notranslate\">os.PathLike</code></a> instances, e.g. <a class=\"reference external\" href=\"https://docs.python.org/3/library/os.html#os.DirEntry\" title=\"(in Python v3.14)\"><code class=\"xref py py-class docutils literal notranslate\">os.DirEntry</code></a>, which are\nconverted to <code class=\"docutils literal notranslate\">str</code> or <code class=\"docutils literal notranslate\">bytes</code> using <a class=\"reference external\" href=\"https://docs.python.org/3/library/os.html#os.fspath\" title=\"(in Python v3.14)\"><code class=\"xref py py-func docutils literal notranslate\">os.fspath()</code></a></p></li>\n<li><p><code class=\"docutils literal notranslate\">LazyObject</code> instances which wrap a serializable value</p></li>\n<li><p>Enumeration types (e.g. <code class=\"docutils literal notranslate\">TextChoices</code> or <code class=\"docutils literal notranslate\">IntegerChoices</code>) instances</p></li>\n<li><p>任何 Django 字段</p></li>\n<li><p>任何函数或方法引用（如 <code class=\"docutils literal notranslate\">datetime.datetime.today</code>）（必须在模块的顶层范围内）</p>\n<ul>\n<li><p>如果正确包装，函数可以进行装饰，即使用 <a class=\"reference external\" href=\"https://docs.python.org/3/library/functools.html#functools.wraps\" title=\"(in Python v3.14)\"><code class=\"xref py py-func docutils literal notranslate\">functools.wraps()</code></a></p></li>\n<li><p>明确支持 <a class=\"reference external\" href=\"https://docs.python.org/3/library/functools.html#functools.cache\" title=\"(in Python v3.14)\"><code class=\"xref py py-func docutils literal notranslate\">functools.cache()</code></a> 和 <a class=\"reference external\" href=\"https://docs.python.org/3/library/functools.html#functools.lru_cache\" title=\"(in Python v3.14)\"><code class=\"xref py py-func docutils literal notranslate\">functools.lru_cache()</code></a> 装饰器</p></li>\n</ul>\n</li>\n<li><p>在类主体内部使用的未绑定方法</p></li>\n<li><p>任何类引用（必须在模块的顶层范围内）</p></li>\n<li><p>具有自定义 <code class=\"docutils literal notranslate\">deconstruct()</code> 方法的任何东西（<a class=\"reference internal\" href=\"#custom-deconstruct-method\"><span class=\"std std-ref\">见下文</span></a>）</p></li>\n</ul>\n<aside class=\"version-note version-changed\" data-version=\"6.0\" role=\"note\">\n<p class=\"version-note-title\">Changed in Django 6.0</p><p>Serialization support for <a class=\"reference external\" href=\"https://docs.python.org/3/library/zoneinfo.html#zoneinfo.ZoneInfo\" title=\"(in Python v3.14)\"><code class=\"xref py py-class docutils literal notranslate\">zoneinfo.ZoneInfo</code></a> instances was added.</p>\n</aside>\n<p>Django 不能序列化：</p>\n<ul class=\"simple\">\n<li><p>嵌套类</p></li>\n<li><p>任何类实例（例如 <code class=\"docutils literal notranslate\">MyClass(4.3, 5.7)</code>）</p></li>\n<li><p>匿名函数</p></li>\n</ul>\n<section id=\"custom-serializers\">\n<span id=\"custom-migration-serializers\"></span><h3>自定义序列化<a class=\"heading-anchor\" href=\"#custom-serializers\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>你可以通过编写一个自定义的序列化器来序列化其他类型。例如，如果 Django 默认没有序列化 <a class=\"reference external\" href=\"https://docs.python.org/3/library/decimal.html#decimal.Decimal\" title=\"(in Python v3.14)\"><code class=\"xref py py-class docutils literal notranslate\">Decimal</code></a> 你可以这样做：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span> <span class=\"nn\">decimal</span> <span class=\"kn\">import</span> Decimal\n\n<span class=\"kn\">from</span> <span class=\"nn\">django.db.migrations.serializer</span> <span class=\"kn\">import</span> BaseSerializer\n<span class=\"kn\">from</span> <span class=\"nn\">django.db.migrations.writer</span> <span class=\"kn\">import</span> MigrationWriter\n\n\n<span class=\"k\">class</span> <span class=\"nc\">DecimalSerializer</span><span class=\"p\">(</span>BaseSerializer<span class=\"p\">):</span>\n    <span class=\"k\">def</span> <span class=\"nf\">serialize</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n        <span class=\"k\">return</span> <span class=\"nb\">repr</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span>value<span class=\"p\">),</span> <span class=\"p\">{</span><span class=\"s2\">&quot;from decimal import Decimal&quot;</span><span class=\"p\">}</span>\n\n\nMigrationWriter<span class=\"o\">.</span>register_serializer<span class=\"p\">(</span>Decimal<span class=\"p\">,</span> DecimalSerializer<span class=\"p\">)</span>\n</code></pre></div>\n<p><code class=\"docutils literal notranslate\">MigrationWriter.register_serializer()</code> 的第一个参数想要使用序列化器的程序类型或类型的可迭代对象。</p>\n<p>序列化器的 <code class=\"docutils literal notranslate\">serialize()</code> 方法必须返回一个字符串，说明该值在迁移中应如何显示以及迁移中需要的一组导入。</p>\n</section>\n<section id=\"adding-a-deconstruct-method\">\n<span id=\"custom-deconstruct-method\"></span><h3>添加 <code class=\"docutils literal notranslate\">deconstruct()</code> 方法<a class=\"heading-anchor\" href=\"#adding-a-deconstruct-method\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>你可以通过给类一个 <code class=\"docutils literal notranslate\">deconstruct()</code> 方法来让Django序列化你的自定义类实例。它不带任何参数，应该返回一个三个项目组成的元组 <code class=\"docutils literal notranslate\">(path, args, kwargs)</code>：</p>\n<ul class=\"simple\">\n<li><p><code class=\"docutils literal notranslate\">path</code> 应该是该类的 Python 路径，并且类名作为最后一部分包括在内（例如，<code class=\"docutils literal notranslate\">myapp.custom_things.MyClass</code>）。如果你的类在模块的顶层不可用，那么它就不能被序列化。</p></li>\n<li><p><code class=\"docutils literal notranslate\">args</code> 应该是一个位置参数的列表，用来传递给你的类的 <code class=\"docutils literal notranslate\">__init__</code> 方法。这个列表中的所有内容本身应该是可序列化的。</p></li>\n<li><p><code class=\"docutils literal notranslate\">kwargs</code> 应该是一个关键字参数的字典，用来传递给你的类的 <code class=\"docutils literal notranslate\">__init__</code> 方法。每个值本身应该是可序列化的。</p></li>\n</ul>\n<aside class=\"admonition admonition-note\" role=\"note\">\n<p class=\"admonition-title\">Note</p>\n<p>此返回值与 <a class=\"reference internal\" href=\"/zh-hans/6.1/howto/custom-model-fields/#custom-field-deconstruct-method\"><span class=\"std std-ref\">自定义字段</span></a> 的 <code class=\"docutils literal notranslate\">deconstruct()</code> 方法不同，后者返回四个项组成的元组。</p>\n</aside>\n<p>Django 会用给定的参数将值作为你的类的实例化写出来，类似于它写出对 Django 字段的引用的方式。</p>\n<p>为了防止每次运行 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#django-admin-makemigrations\"><code class=\"xref std std-djadmin docutils literal notranslate\">makemigrations</code></a> 时都会创建一个新的迁移，你还应该在装饰类中添加一个 <code class=\"docutils literal notranslate\">__eq__()</code> 方法。这个函数将被 Django 的迁移框架调用，以检测状态之间的变化。</p>\n<p>只要类构造函数的所有参数本身都是可序列化的，就可以使用 <code class=\"docutils literal notranslate\">django.utils.deconstruct</code> 的 <code class=\"docutils literal notranslate\">&#64;deconstructible</code> 类装饰器添加 <code class=\"docutils literal notranslate\">deconstruct()</code> 方法：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span> <span class=\"nn\">django.utils.deconstruct</span> <span class=\"kn\">import</span> deconstructible\n\n\n<span class=\"nd\">@deconstructible</span>\n<span class=\"k\">class</span> <span class=\"nc\">MyCustomClass</span><span class=\"p\">:</span>\n    <span class=\"k\">def</span> <span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> foo<span class=\"o\">=</span><span class=\"mi\">1</span><span class=\"p\">):</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span>foo <span class=\"o\">=</span> foo\n        <span class=\"o\">...</span>\n\n    <span class=\"k\">def</span> <span class=\"fm\">__eq__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> other<span class=\"p\">):</span>\n        <span class=\"k\">return</span> <span class=\"bp\">self</span><span class=\"o\">.</span>foo <span class=\"o\">==</span> other<span class=\"o\">.</span>foo\n</code></pre></div>\n<p>装饰器添加逻辑以捕获并保留进入构造函数的参数，然后在调用 deconstruct() 时准确返回这些参数。</p>\n</section>\n</section>\n<section id=\"supporting-multiple-django-versions\">\n<h2>支持多个 Django 版本<a class=\"heading-anchor\" href=\"#supporting-multiple-django-versions\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>如果你是具有模型的第三方应用的维护者，你可能需要发布支持多个 Django 版本的迁移。在这种情况下，你应该始终 <strong>使用你希望支持的最低Django版本</strong> 运行 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#django-admin-makemigrations\"><code class=\"xref std std-djadmin docutils literal notranslate\">makemigrations</code></a>。</p>\n<p>迁移系统会按照与 Django 其他部分相同的策略保持向后兼容，所以在 Django X.Y 上生成的迁移文件在 Django X.Y+1 上运行时应该没有变化。但是，迁移系统并不保证向前兼容。新的功能可能会被添加，而且用新版本的 Django 生成的迁移文件可能无法在旧版本上运行。</p>\n<aside class=\"admonition admonition-seealso\" role=\"note\">\n<p class=\"admonition-title\">See also</p>\n<dl class=\"simple\">\n<dt><a class=\"reference internal\" href=\"/zh-hans/6.1/ref/migration-operations/\"><span class=\"doc\">迁移操作参考</span></a></dt><dd><p>涵盖架构操作 API，特殊操作以及编写自己的操作。</p>\n</dd>\n<dt><a class=\"reference internal\" href=\"/zh-hans/6.1/howto/writing-migrations/\"><span class=\"doc\">编写迁移的“方法”</span></a></dt><dd><p>介绍如何为你可能遇到的不同情况构建和编写数据库迁移。</p>\n</dd>\n</dl>\n</aside>\n</section>","rootId":"module-django.db.migrations","toc":[{"title":"命令","anchor":"the-commands","children":[]},{"title":"后端支持","anchor":"backend-support","children":[{"title":"PostgreSQL","anchor":"postgresql","children":[]},{"title":"MySQL","anchor":"mysql","children":[]},{"title":"SQLite","anchor":"sqlite","children":[]}]},{"title":"工作流程","anchor":"workflow","children":[{"title":"Executing sqlmigrate","anchor":"executing-sqlmigrate","children":[]},{"title":"版本控制","anchor":"version-control","children":[]}]},{"title":"事务","anchor":"transactions","children":[]},{"title":"依赖","anchor":"dependencies","children":[{"title":"可交换的依赖关系","anchor":"swappable-dependencies","children":[]}]},{"title":"迁移文件","anchor":"migration-files","children":[{"title":"自定义字段","anchor":"custom-fields","children":[]},{"title":"模型管理器","anchor":"model-managers","children":[]},{"title":"初始迁移","anchor":"initial-migrations","children":[]},{"title":"历史一致性","anchor":"history-consistency","children":[]}]},{"title":"向应用添加迁移","anchor":"adding-migrations-to-apps","children":[]},{"title":"撤销迁移","anchor":"reversing-migrations","children":[]},{"title":"历史模型","anchor":"historical-models","children":[]},{"title":"删除模型字段时的注意事项","anchor":"considerations-when-removing-model-fields","children":[]},{"title":"数据迁移","anchor":"data-migrations","children":[{"title":"从其他应用访问模型","anchor":"accessing-models-from-other-apps","children":[]},{"title":"更高级的迁移","anchor":"more-advanced-migrations","children":[]}]},{"title":"压缩迁移","anchor":"squashing-migrations","children":[]},{"title":"序列化值","anchor":"serializing-values","children":[{"title":"自定义序列化","anchor":"custom-serializers","children":[]},{"title":"添加 deconstruct() 方法","anchor":"adding-a-deconstruct-method","children":[]}]},{"title":"支持多个 Django 版本","anchor":"supporting-multiple-django-versions","children":[]}],"breadcrumbs":[{"docname":"topics/index","title":"使用 Django","url":"/zh-hans/6.1/topics/"}],"prev":{"docname":"topics/class-based-views/mixins","title":"在基于类的视图中使用混入","url":"/zh-hans/6.1/topics/class-based-views/mixins/"},"next":{"docname":"topics/files","title":"管理文件","url":"/zh-hans/6.1/topics/files/"},"formats":{"html":"/zh-hans/6.1/topics/migrations/","markdown":"/zh-hans/6.1/topics/migrations.md","json":"/zh-hans/6.1/topics/migrations.json"},"source":"https://github.com/django/django/blob/stable/6.1.x/docs/topics/migrations.txt","official":"https://docs.djangoproject.com/zh-hans/6.1/topics/migrations/","inVersions":["6.1","6.0","5.2","5.1","5.0","4.2","4.1","4.0","3.2","3.1","3.0","2.2","2.1","2.0"],"inLocales":["en","sv","zh-hans","ga","fr","ja","id","it","pt-br","ko","es","el","pl"]}