迁移是Django将您对模型所做的更改(添加字段、删除模型等)传播到数据库模式中的方法。它们的设计主要是自动的,但是您需要知道何时进行迁移,何时运行它们,以及可能遇到的常见问题。
有几个命令可用于与迁移和Django对数据库模式的处理进行交互:
migrate
,负责应用和取消应用迁移。
makemigrations
,它负责根据对模型所做的更改创建新的迁移。
sqlmigrate
,显示迁移的SQL语句。
showmigrations
其中列出了项目的迁移及其状态。
您应该将迁移视为数据库模式的版本控制系统。 makemigrations
负责将模型更改打包到单个迁移文件中(类似于提交),以及 migrate
负责将这些应用到数据库。
每个应用程序的迁移文件都位于该应用程序内部的“迁移”目录中,并设计为提交到其代码库,并作为代码库的一部分分发。您应该在开发机器上进行一次迁移,然后在同事的机器、登台机器以及最终的生产机器上运行相同的迁移。
备注
可以通过修改 MIGRATION_MODULES
设置。
迁移将以相同的方式在同一个数据集上运行,并产生一致的结果,这意味着在开发和分段中看到的情况,在相同的情况下,正是在生产中发生的情况。
Django将对模型或字段的任何更改进行迁移,甚至是不影响数据库的选项,因为它能够正确重建字段的唯一方法是在历史记录中进行所有更改,并且在以后的某些数据迁移中可能需要这些选项(例如,如果设置了自定义验证器)。
Django附带的所有后端以及任何第三方后端都支持迁移,前提是它们已编程为支持模式更改(通过 SchemaEditor 类)。
然而,在模式迁移方面,一些数据库比其他数据库更有能力;下面介绍了一些注意事项。
在模式支持方面,PostgreSQL是这里所有数据库中功能最强的。
MySQL缺乏对模式更改操作周围事务的支持,这意味着如果迁移无法应用,则必须手动取消选择更改才能重试(不可能回滚到更早的点)。
MySQL 8.0为以下方面引入了显著的性能增强 DDL operations ,提高了它们的效率,并减少了对完整表重建的需要。然而,它不能保证完全没有锁定或中断。在仍然需要锁定的情况下,这些操作的持续时间将与涉及的行数成比例。
最后,MySQL对索引覆盖的所有列的总大小有相对较小的限制。这意味着在其他后台可以创建的索引将无法在MySQL下创建。
sqlite几乎没有内置模式更改支持,因此django尝试通过以下方式来模拟它:
使用新架构创建新表
复制数据
丢掉旧表格
重命名新表以匹配原始名称
这个过程通常工作得很好,但它可能很慢,偶尔会有小车。不建议您在生产环境中运行和迁移sqlite,除非您非常清楚风险及其限制;Django附带的支持旨在允许开发人员在本地计算机上使用sqlite来开发不太复杂的Django项目,而无需完整的数据库。
Django可以为您创建迁移。对您的模型进行更改--例如,添加一个字段并删除一个模型--然后运行 makemigrations
:
$ python manage.py makemigrations
Migrations for 'books':
books/migrations/0003_auto.py:
~ Alter field author on book
您的模型将被扫描并与迁移文件中当前包含的版本进行比较,然后将写出一组新的迁移。确保读取输出以查看 makemigrations
认为你已经改变了——这并不完美,对于复杂的改变,它可能无法检测到你所期望的。
一旦有了新的迁移文件,就应该将它们应用于数据库,以确保它们按预期工作:
$ python manage.py migrate
Operations to perform:
Apply all migrations: books
Running migrations:
Rendering model states... DONE
Applying books.0003_auto... OK
一旦应用了迁移,就将迁移和模型作为一个提交提交提交到您的版本控制系统中——这样,当其他开发人员(或生产服务器)签出代码时,他们将同时获得对模型的更改和附带的迁移。
如果希望为迁移(S)指定一个有意义的名称而不是生成的名称,可以使用 makemigrations --name
选项:
$ python manage.py makemigrations --name changed_my_model your_app_label
因为迁移存储在版本控制中,所以偶尔会遇到这样的情况,即您和另一个开发人员同时提交到同一应用程序的迁移,从而导致两个具有相同编号的迁移。
别担心-这些数字仅供开发者参考,Django只关心每个迁移都有不同的名称。迁移会在文件中指定它们所依赖的其他迁移(包括同一应用程序中的早期迁移),因此可以检测同一应用程序何时有两个未排序的新迁移。
当这种情况发生时,Django会提示您并给您一些选择。如果它认为这是安全的,它将提供自动线性化这两个迁移为您。如果没有,你就必须自己去修改迁移——不用担心,这并不困难,在 迁移文件 下面。
在支持RST事务的数据库(SQLite和PostgreSQL)上,默认情况下所有迁移操作都将在单个事务内运行。相比之下,如果数据库不支持RST事务(例如,SQL、Oracle),那么所有操作都将在没有事务的情况下运行。
您可以通过设置 atomic
属性为 False
。例如::
from django.db import migrations
class Migration(migrations.Migration):
atomic = False
还可以使用 atomic()
或者通过使 atomic=True
至 RunPython
。看见 非原子迁移 了解更多详细信息。
虽然迁移是针对每个应用程序的,但模型隐含的表和关系太复杂,无法一次为一个应用程序创建。当您进行的迁移需要运行其他内容时-例如,您添加 ForeignKey
在你的 books
应用程序到您的 authors
应用程序-最终的迁移将包含对中的迁移的依赖性 authors
。
这意味着当您运行迁移时, authors
迁移首先运行,并创建 ForeignKey
引用,然后是使 ForeignKey
列随后运行并创建约束。如果没有发生这种情况,迁移将尝试创建 ForeignKey
列,而没有它引用的现有表,则数据库将抛出错误。
这种依赖行为会影响大多数迁移操作,在这些操作中,您只能使用单个应用程序。限制为单个应用程序(在 makemigrations
或 migrate
)是一个尽最大努力的承诺,而不是一个保证;任何其他应用程序,需要用来获得正确的依赖关系将是。
没有迁移的应用程序不能有关系 (ForeignKey
, ManyToManyField
等)迁移到应用程序。有时它可以工作,但不受支持。
这个 swappable_dependency()
函数在迁移中使用,用于在换入模型的应用程序中声明对迁移的“可交换”依赖关系,目前是在此应用程序的第一次迁移时。因此,应该在初始迁移中创建换入模型。这一论点 value
是一个字符串 "<app label>.<model>"
描述应用程序标签和型号名称,例如 "myapp.MyModel"
。
通过使用 swappable_dependency()
,您通知迁移框架迁移依赖于另一个迁移,该迁移设置了一个可交换模型,从而允许将来用不同的实现替换该模型的可能性。这通常用于引用需要进行自定义或替换的模型,例如自定义用户模型 (settings.AUTH_USER_MODEL
,它缺省为 "auth.User"
)在Django的认证系统中。
迁移以磁盘格式存储,此处称为“迁移文件”。这些文件实际上是普通的Python文件,具有商定的对象布局,以声明性风格编写。
基本迁移文件如下所示:
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("migrations", "0001_initial")]
operations = [
migrations.DeleteModel("Tribble"),
migrations.AddField("Author", "rating", models.IntegerField(default=0)),
]
Django在加载迁移文件(作为python模块)时查找的是 django.db.migrations.Migration
调用 Migration
. 然后,它检查该对象的四个属性,其中大多数情况下只使用两个属性:
dependencies
,此迁移依赖的迁移列表。
operations
,一览表 Operation
定义此迁移操作的类。
操作是关键;它们是一组声明性指令,告诉Django需要进行什么模式更改。Django扫描它们,并构建所有应用程序的所有模式更改的内存表示,并使用它来生成导致模式更改的SQL。
内存中的结构还用于计算模型和迁移的当前状态之间的差异;django依次在一组内存中的模型上运行所有更改,以在上次运行时得出模型的状态。 makemigrations
. 然后它使用这些模型与 models.py
用于计算您所更改内容的文件。
您很少需要手动编辑迁移文件,但如果需要,完全可以手动编写迁移文件。有些更复杂的操作是无法自动检测的,只能通过手写迁移才能使用的,因此如果必须编辑它们,不要害怕。
在已迁移的自定义字段中,如果不引发 TypeError
. 旧的迁移将调用 __init__
具有旧签名的方法。因此,如果需要新参数,请创建关键字参数并添加类似 assert 'argument_name' in kwargs
在构造函数中。
您可以选择将管理器序列化为迁移,并在 RunPython
操作。这是通过定义 use_in_migrations
管理器类的属性:
class MyManager(models.Manager):
use_in_migrations = True
class MyModel(models.Model):
objects = MyManager()
如果您正在使用 from_queryset()
函数要动态生成管理器类,需要从生成的类继承以使其可导入::
class MyManager(MyBaseManager.from_queryset(CustomQuerySet)):
use_in_migrations = True
class MyModel(models.Model):
objects = MyManager()
请参阅有关 历史模型 在迁移过程中,我们将看到随之而来的影响。
应用程序的“初始迁移”是创建该应用程序表的第一个版本的迁移。通常,应用程序会有一次初始迁移,但在复杂模型相互依赖的某些情况下,它可能会有两次或更多次初始迁移。
初始迁移标记为 initial = True
迁移类的类属性。如果一个 initial
找不到class属性,如果迁移是应用程序中的第一次迁移(即,如果它不依赖于同一应用程序中的任何其他迁移),则迁移将被视为“初始”。
当 migrate --fake-initial
使用选项时,将专门处理这些初始迁移。对于创建一个或多个表的初始迁移 (CreateModel
操作),django检查数据库中是否已经存在所有这些表,如果存在,则fake应用迁移。同样,对于添加一个或多个字段的初始迁移 (AddField
操作),django检查数据库中是否已经存在所有相应的列,如果存在,则fake应用迁移。没有 --fake-initial
对初始迁移的处理与任何其他迁移都没有区别。
正如前面所讨论的,当连接两个开发分支时,您可能需要手动线性化迁移。在编辑迁移依赖项时,您可能会无意中创建一个不一致的历史状态,其中应用了迁移,但其某些依赖项没有应用。这强烈表明依赖项不正确,因此Django将拒绝运行迁移或进行新的迁移,直到修复它。使用多个数据库时,可以使用 allow_migrate()
方法 database routers 控制哪些数据库 makemigrations
检查历史记录是否一致。
新应用程序已预先配置为接受迁移,因此您可以通过运行添加迁移 makemigrations
一旦您做出了一些改变。
如果您的应用程序已经有模型和数据库表,但还没有迁移(例如,您是根据以前的Django版本创建的),则需要通过运行以下命令将其转换为使用迁移:
$ python manage.py makemigrations your_app_label
这将为您的应用程序进行新的初始迁移。现在,运行 python manage.py migrate --fake-initial
,Django将检测到您有一个初始迁移 and 它要创建的表已经存在,并将迁移标记为已应用。(没有 migrate --fake-initial
标志,该命令将出错,因为它要创建的表已经存在。)
请注意,这只在以下两种情况下有效:
自从你做了表格,你就没有换过你的模型。要使迁移工作,必须进行初始迁移 第一 然后进行更改,因为Django将更改与迁移文件(而不是数据库)进行比较。
您没有手动编辑您的数据库-Django将无法检测到您的数据库与您的模型不匹配,您只会在迁移尝试修改这些表时出错。
迁移可以逆转 migrate
通过超过上一次迁移的数量。例如,反向迁移 books.0003
:
$ python manage.py migrate books 0002
Operations to perform:
Target specific migration: 0002_auto, from books
Running migrations:
Rendering model states... DONE
Unapplying books.0003_auto... OK
...\> py manage.py migrate books 0002
Operations to perform:
Target specific migration: 0002_auto, from books
Running migrations:
Rendering model states... DONE
Unapplying books.0003_auto... OK
如果您想逆转应用程序应用的所有迁移,请使用名称 zero
:
$ python manage.py migrate books zero
Operations to perform:
Unapply all migrations: books
Running migrations:
Rendering model states... DONE
Unapplying books.0002_auto... OK
Unapplying books.0001_initial... OK
...\> py manage.py migrate books zero
Operations to perform:
Unapply all migrations: books
Running migrations:
Rendering model states... DONE
Unapplying books.0002_auto... OK
Unapplying books.0001_initial... OK
如果迁移包含任何不可逆转的操作,则迁移是不可逆转的。试图扭转此类移民将提高 IrreversibleError
:
$ python manage.py migrate books 0002
Operations to perform:
Target specific migration: 0002_auto, from books
Running migrations:
Rendering model states... DONE
Unapplying books.0003_auto...Traceback (most recent call last):
django.db.migrations.exceptions.IrreversibleError: Operation <RunSQL sql='DROP TABLE demo_books'> in books.0003_auto is not reversible
...\> py manage.py migrate books 0002
Operations to perform:
Target specific migration: 0002_auto, from books
Running migrations:
Rendering model states... DONE
Unapplying books.0003_auto...Traceback (most recent call last):
django.db.migrations.exceptions.IrreversibleError: Operation <RunSQL sql='DROP TABLE demo_books'> in books.0003_auto is not reversible
运行迁移时,Django使用存储在迁移文件中的模型的历史版本。如果使用 RunPython
手术,或者如果你有 allow_migrate
数据库路由器上的方法, 需要使用 这些历史模型版本而不是直接导入它们。
警告
如果直接导入模型而不是使用历史模型,则您的迁移 may work initially 但是将来当您尝试重新运行旧迁移时(通常是在设置新安装并运行所有迁移以设置数据库时),它将失败。
这意味着历史模型问题可能不会立即显而易见。如果遇到这种故障,可以编辑迁移以使用历史模型,而不是直接导入并提交这些更改。
因为不可能序列化任意的Python代码,所以这些历史模型将没有您定义的任何自定义方法。但是,他们将拥有相同的字段、关系、经理(仅限于 use_in_migrations = True
) Meta
选项(也是版本化的,因此它们可能与当前的不同)。
警告
这意味着你不会有习惯 save()
当您在迁移中访问对象时调用了这些方法,并且您将没有任何自定义构造函数或实例方法。适当计划!
对字段选项中的函数的引用,例如 upload_to
和 limit_choices_to
和模型管理器声明 use_in_migrations = True
是在迁移中序列化的,因此只要存在引用它们的迁移,就需要保留函数和类。任何 custom model fields 还需要保留,因为这些是通过迁移直接导入的。
此外,模型的具体Base被存储为指针,因此只要存在包含对它们的引用的迁移,您就必须始终保留Base。从好的方面来说,来自这些基本类的方法和管理器正常继承,因此如果您绝对需要访问这些,您可以选择将它们移动到超类中。
要删除旧引用,可以 squash migrations 或者,如果引用不多,则将它们复制到迁移文件中。
与上一节中描述的“引用历史函数”注意事项类似,如果在旧迁移中引用自定义模型字段,则从项目或第三方应用程序中删除这些字段将导致问题。
为了帮助解决这种情况,Django提供了一些模型字段属性来帮助使用 system checks framework .
添加 system_check_deprecated_details
您的模型字段的属性类似于:
class IPAddressField(Field):
system_check_deprecated_details = {
"msg": (
"IPAddressField has been deprecated. Support for it (except "
"in historical migrations) will be removed in Django 1.9."
),
"hint": "Use GenericIPAddressField instead.", # optional
"id": "fields.W900", # pick a unique ID for your field.
}
在您选择的折旧期之后(对于django本身的字段有两个或三个功能版本),更改 system_check_deprecated_details
属性到 system_check_removed_details
更新字典,类似于:
class IPAddressField(Field):
system_check_removed_details = {
"msg": (
"IPAddressField has been removed except for support in "
"historical migrations."
),
"hint": "Use GenericIPAddressField instead.",
"id": "fields.E900", # pick a unique ID for your field.
}
您应该保留字段在数据库迁移中操作所需的方法,例如 __init__()
, deconstruct()
和 get_internal_type()
. 只要存在引用该字段的任何迁移,就保留该存根字段。例如,在压缩迁移并删除旧迁移之后,您应该能够完全删除字段。
除了更改数据库模式外,还可以使用迁移来更改数据库本身中的数据,如果需要,还可以与模式一起使用。
改变数据的迁移通常被称为“数据迁移”;它们最好是作为单独的迁移编写的,与模式迁移并排。
Django不能像模式迁移那样自动为您生成数据迁移,但编写它们并不困难。Django中的迁移文件由 Operations ,您用于数据迁移的主要操作是 RunPython
.
首先,创建一个您可以使用的空迁移文件(Django将把文件放在正确的位置,建议一个名称,并为您添加依赖项):
python manage.py makemigrations --empty yourappname
然后,打开文件;它应该像这样:
# Generated by Django A.B on YYYY-MM-DD HH:MM
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("yourappname", "0001_initial"),
]
operations = []
现在,您需要做的就是创建一个新的函数 RunPython
用它。 RunPython
需要一个可调用参数作为其参数,该参数采用两个参数-第一个参数是 app registry 它将所有模型的历史版本加载到其中,以匹配迁移在历史中的位置,第二个版本是 SchemaEditor ,您可以使用它手动影响数据库架构更改(但请注意,这样做可能会混淆迁移自动检测器!)
让我们写一个移民来填充我们的新 name
组合值的字段 first_name
和 last_name
(we我们已经醒悟过来并意识到并不是每个人都有名字和姓氏)。我们需要做的就是使用历史模型并迭代行::
from django.db import migrations
def combine_names(apps, schema_editor):
# We can't import the Person model directly as it may be a newer
# version than this migration expects. We use the historical version.
Person = apps.get_model("yourappname", "Person")
for person in Person.objects.all():
person.name = f"{person.first_name} {person.last_name}"
person.save()
class Migration(migrations.Migration):
dependencies = [
("yourappname", "0001_initial"),
]
operations = [
migrations.RunPython(combine_names),
]
一旦完成,我们就可以运行了 python manage.py migrate
正常情况下,数据迁移将与其他迁移一起运行。
您可以通过第二个调用 RunPython
运行向后迁移时要执行的任何逻辑。如果省略此可调用项,则向后迁移将引发异常。
写作时 RunPython
使用迁移所在应用程序以外的其他应用程序的模型的函数 dependencies
属性应包括所涉及的每个应用程序的最新迁移,否则您可能会收到类似以下内容的错误: LookupError: No installed app with label 'myappname'
当您尝试在 RunPython
使用函数 apps.get_model()
.
在下面的示例中,我们在 app1
需要使用模型 app2
. 我们不关心 move_m1
除此之外,它还需要从两个应用程序访问模型。因此,我们添加了一个依赖项,指定了 app2
::
class Migration(migrations.Migration):
dependencies = [
("app1", "0001_initial"),
# added dependency to enable using models from app2 in move_m1
("app2", "0004_foobar"),
]
operations = [
migrations.RunPython(move_m1),
]
如果您对更高级的迁移操作感兴趣,或者希望能够编写自己的迁移操作,请参见 migration operations reference 以及“操作方法” writing migrations .
我们鼓励您自由地进行迁移,而不必担心您有多少个迁移代码;迁移代码经过优化,可以一次处理数百个迁移,而不会有太多的速度减慢。然而,最终你会想从几百次迁移移回几次,这就是压扁的原因。
压扁是将一组现有的多个迁移减少到一个(有时是几个)迁移,这些迁移仍然表示相同的更改。
Django通过采取所有现有的迁移,提取它们 Operation
然后将它们全部按顺序排列,然后对它们运行优化器以尝试减少列表的长度-例如,它知道 CreateModel
和 DeleteModel
彼此取消,它知道 AddField
可以卷成 CreateModel
.
一旦操作顺序被尽可能地减少,可能的数量取决于您的模型之间的紧密程度,如果您有 RunSQL
或 RunPython
操作(除非标记为 elidable
)-然后Django将把它写回一组新的迁移文件。
这些文件被标记为替换之前被压缩的迁移,因此它们可以与旧的迁移文件共存,Django将根据您在历史记录中的位置智能地在它们之间切换。如果您仍在完成压缩的迁移集的一部分,它将继续使用它们,直到到达最后,然后切换到压缩的历史记录,而新安装将使用新的压缩迁移并跳过所有旧迁移。
这使您能够压缩且不会弄乱当前生产中尚未完全更新的系统。建议的过程是挤压、保留旧文件、提交并发布,等待所有系统都使用新版本升级(或者如果您是第三方项目,请确保您的用户按顺序升级版本而不跳过任何版本),然后删除旧文件、提交并执行第二个版本。
支持这一切的命令是 squashmigrations
-向它传递您想要挤压的应用程序标签和迁移名称,它将开始工作:
$ ./manage.py squashmigrations myapp 0004
Will squash the following migrations:
- 0001_initial
- 0002_some_change
- 0003_another_change
- 0004_undo_something
Do you wish to proceed? [y/N] y
Optimizing...
Optimized from 12 operations to 7 operations.
Created new squashed migration /home/andrew/Programs/DjangoTest/test/migrations/0001_squashed_0004_undo_something.py
You should commit this migration but leave the old ones in place;
the new migration will be used for new installs. Once you are sure
all instances of the codebase have applied the migrations you squashed,
you can delete them.
使用 squashmigrations --squashed-name
如果要设置压缩迁移的名称,而不是使用自动生成的名称,则选择此选项。
请注意,django中的模型依赖关系可能变得非常复杂,挤压可能会导致不运行的迁移;或者是优化不当(在这种情况下,您可以使用 --no-optimize
尽管您还应该报告一个问题),或者 CircularDependencyError
,在这种情况下,您可以手动解决它。
手动解决 CircularDependencyError
,将循环依赖关系循环中的一个foreignkey分解为单独的迁移,并将依赖关系与另一个应用程序一起移动。如果你不确定,看看如何 makemigrations
当被要求从您的模型中创建全新的迁移时,处理这个问题。在未来的django版本中, squashmigrations
将更新以尝试自行解决这些错误。
一旦压缩了迁移,就应该将其与它替换的迁移一起提交,并将此更改分发到应用程序的所有正在运行的实例,确保它们运行 migrate
在数据库中存储更改。
然后必须通过以下方式将压缩迁移转换为正常迁移:
删除它替换的所有迁移文件。
更新依赖于已删除迁移的所有迁移以依赖于压缩的迁移。
去掉 replaces
属性 Migration
压缩迁移的类(这是Django如何告知它是压缩迁移)。
备注
一旦压扁了一个迁移,就不应该再压扁那个压扁的迁移,直到您将其完全转换为正常迁移为止。
修剪对已删除迁移的引用
如果将来可能会重复使用已删除迁移的名称,则应该使用 migrate --prune
选择。
迁移是包含模型旧定义的Python文件-因此,要编写它们,Django必须获取模型的当前状态并将它们序列化到文件中。
虽然django可以序列化大多数东西,但有些东西我们只是无法序列化为有效的python表示形式——对于如何将值转换回代码,没有python标准。 (repr()
仅适用于基本值,不指定导入路径)。
Django可以序列化以下内容:
int
, float
, bool
, str
, bytes
, None
, NoneType
list
, set
, tuple
, dict
, range
。
datetime.date
, datetime.time
和 datetime.datetime
实例(包括那些时区感知的实例)
decimal.Decimal
实例
enum.Enum
和 enum.Flag
实例
uuid.UUID
实例
functools.partial()
和 functools.partialmethod
具有可序列化的实例 func
, args
和 keywords
价值观。
纯粹而具体的路径对象 pathlib
.具体路径被转换为其纯路径等效,例如 pathlib.PosixPath
至 pathlib.PurePosixPath
。
os.PathLike
实例,例如 os.DirEntry
,转化为 str
或 bytes
使用 os.fspath()
。
LazyObject
包装可序列化值的实例。
列举类型(例如 TextChoices
或 IntegerChoices
)实例。
任何Django字段
任何函数或方法引用(例如 datetime.datetime.today
)(必须在模块的顶级范围内)
如果包装得当,函数可能会被修饰,即使用 functools.wraps()
这个 functools.cache()
和 functools.lru_cache()
明确支持装饰符
从类体内部使用的未绑定方法
任何类引用(必须在模块的顶级范围内)
任何有习俗的东西 deconstruct()
方法 (see below )
Django无法序列化:
嵌套类
任意类实例(例如 MyClass(4.3, 5.7)
)
兰姆达斯
您可以通过编写自定义序列化程序来序列化其他类型。例如,如果Django没有序列化 Decimal
默认情况下,您可以执行以下操作:
from decimal import Decimal
from django.db.migrations.serializer import BaseSerializer
from django.db.migrations.writer import MigrationWriter
class DecimalSerializer(BaseSerializer):
def serialize(self):
return repr(self.value), {"from decimal import Decimal"}
MigrationWriter.register_serializer(Decimal, DecimalSerializer)
第一个论点 MigrationWriter.register_serializer()
是应使用序列化程序的类型或ITable。
这个 serialize()
序列化程序的方法必须返回一个字符串,说明值在迁移中的显示方式,以及一组迁移中需要的任何导入。
deconstruct()
方法¶通过给类A,可以让Django序列化自己的自定义类实例 deconstruct()
方法。它不需要参数,应该返回由三个元素组成的元组 (path, args, kwargs)
:
path
应该是类的python路径,最后一部分包括类名(例如, myapp.custom_things.MyClass
)如果您的类在模块的顶层不可用,那么它是不可序列化的。
args
应该是传递给类的位置参数列表' __init__
方法。此列表中的所有内容本身都应该是可序列化的。
kwargs
应该是要传递给类的关键字参数的dict' __init__
方法。每个值本身都应该是可序列化的。
备注
此返回值与 deconstruct()
方法 for custom fields 它返回一个由四个项组成的元组。
Django将使用给定的参数将值作为类的实例化写出,类似于它写出对Django字段的引用的方式。
防止每次创建新的迁移 makemigrations
是run,还应添加 __eq__()
修饰类的方法。Django的迁移框架将调用此函数来检测状态之间的更改。
只要类构造函数的所有参数本身都是可序列化的,就可以使用 @deconstructible
类修饰符来自 django.utils.deconstruct
添加 deconstruct()
方法:
from django.utils.deconstruct import deconstructible
@deconstructible
class MyCustomClass:
def __init__(self, foo=1):
self.foo = foo
...
def __eq__(self, other):
return self.foo == other.foo
decorator添加了逻辑来捕获和保留参数,并将其保存到构造函数中,然后在调用deconstruct()时返回这些参数。
如果您是具有模型的第三方应用程序的维护者,则可能需要发送支持多个django版本的迁移。在这种情况下,您应该始终运行 makemigrations
使用您希望支持的最低Django版本 .
迁移系统将根据与Django其余部分相同的策略保持向后兼容性,因此Django X.Y上生成的迁移文件应在Django X.Y+1上保持不变运行。但是,迁移系统不保证向前兼容。可能会添加新功能,并且使用较新版本的Django生成的迁移文件可能无法在较旧版本上工作。
参见
包括模式操作API、特殊操作和编写自己的操作。
解释如何为可能遇到的不同场景构建和编写数据库迁移。
7月 22, 2024