“Django/docs/2.2.x/topics/serialization”的版本间差异
(autoload) |
小 (Page commit) |
||
第1行: | 第1行: | ||
+ | {{DISPLAYTITLE:序列化 Django 对象 — Django 文档}} | ||
<div id="serializing-django-objects" class="section"> | <div id="serializing-django-objects" class="section"> | ||
− | = | + | = 序列化 Django 对象 = |
− | Django | + | Django 的序列化框架提供了一种将 Django 模型“翻译”成其他格式的机制。 通常这些其他格式将基于文本并用于通过线路发送 Django 数据,但序列化程序可以处理任何格式(基于文本或非文本)。 |
− | |||
− | |||
− | |||
<div class="admonition seealso"> | <div class="admonition seealso"> | ||
− | + | 也可以看看 | |
− | + | 如果你只是想从你的表中获取一些数据到序列化的形式,你可以使用 [[#id1|:djadmin:`dumpdata`]] 管理命令。 | |
− | |||
第19行: | 第16行: | ||
<div id="serializing-data" class="section"> | <div id="serializing-data" class="section"> | ||
− | == | + | == 序列化数据 == |
− | + | 在最高级别,序列化数据是一个非常简单的操作: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第27行: | 第24行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">from django.core import serializers |
− | data = serializers.serialize( | + | data = serializers.serialize("xml", SomeModel.objects.all())</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | <code>serialize</code> 函数的参数是将数据序列化为的格式(请参阅 [[#id4|序列化格式]] )和要序列化的 [[../../ref/models/querysets#django.db.models.query|QuerySet]]。 (实际上,第二个参数可以是任何生成 Django 模型实例的迭代器,但它几乎总是一个 QuerySet)。 | |
− | |||
− | [[../../ref/models/querysets#django.db.models.query| | ||
− | |||
− | |||
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">django.core.serializers.</span></span><span class="sig-name descname"><span class="pre">get_serializer</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">format</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 您还可以直接使用序列化程序对象: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第48行: | 第41行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">XMLSerializer = serializers.get_serializer("xml") |
xml_serializer = XMLSerializer() | xml_serializer = XMLSerializer() | ||
xml_serializer.serialize(queryset) | xml_serializer.serialize(queryset) | ||
− | data = xml_serializer.getvalue()</ | + | data = xml_serializer.getvalue()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果您想将数据直接序列化为类似文件的对象(包括 [[../../ref/request-response#django.http|HttpResponse]]),这将非常有用: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第63行: | 第55行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">with open("file.xml", "w") as out: |
− | xml_serializer.serialize(SomeModel.objects.all(), stream=out)</ | + | xml_serializer.serialize(SomeModel.objects.all(), stream=out)</syntaxhighlight> |
</div> | </div> | ||
第71行: | 第63行: | ||
<div class="admonition note"> | <div class="admonition note"> | ||
− | + | 笔记 | |
− | + | 使用未知的 [[#serialization-formats|格式]] 调用 [[#django.core.serializers.get_serializer|get_serializer()]] 将引发 <code>django.core.serializers.SerializerDoesNotExist</code> 异常。 | |
− | |||
− | <code>django.core.serializers.SerializerDoesNotExist</code> | ||
第81行: | 第71行: | ||
<div id="subset-of-fields" class="section"> | <div id="subset-of-fields" class="section"> | ||
− | <span id=" | + | <span id="id3"></span> |
− | === | + | === 字段子集 === |
− | + | 如果您只想序列化字段的子集,则可以为序列化程序指定 <code>fields</code> 参数: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第91行: | 第80行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">from django.core import serializers |
− | data = serializers.serialize('xml', SomeModel.objects.all(), fields=('name','size'))</ | + | data = serializers.serialize('xml', SomeModel.objects.all(), fields=('name','size'))</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 在本例中,只会序列化每个模型的 <code>name</code> 和 <code>size</code> 属性。 主键始终序列化为结果输出中的 <code>pk</code> 元素; 它从未出现在 <code>fields</code> 部分。 | |
− | |||
− | |||
<div class="admonition note"> | <div class="admonition note"> | ||
− | + | 笔记 | |
− | + | 根据您的模型,您可能会发现无法反序列化仅序列化其字段子集的模型。 如果序列化对象没有指定模型所需的所有字段,反序列化器将无法保存反序列化实例。 | |
− | |||
− | |||
− | |||
第116行: | 第100行: | ||
<div id="inherited-models" class="section"> | <div id="inherited-models" class="section"> | ||
− | === | + | === 继承模型 === |
− | + | 如果您有一个使用 [[../db/models#abstract-base-classes|抽象基类]] 定义的模型,则无需执行任何特殊操作即可序列化该模型。 只需在要序列化的对象(或多个对象)上调用序列化程序,输出将是序列化对象的完整表示。 | |
− | |||
− | |||
− | |||
− | + | 但是,如果您有一个使用 [[../db/models#multi-table-inheritance|多表继承]] 的模型,您还需要序列化该模型的所有基类。 这是因为只有在模型上本地定义的字段才会被序列化。 例如,考虑以下模型: | |
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第131行: | 第110行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">class Place(models.Model): |
name = models.CharField(max_length=50) | name = models.CharField(max_length=50) | ||
class Restaurant(Place): | class Restaurant(Place): | ||
− | serves_hot_dogs = models.BooleanField(default=False)</ | + | serves_hot_dogs = models.BooleanField(default=False)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果您只序列化 Restaurant 模型: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第146行: | 第125行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">data = serializers.serialize('xml', Restaurant.objects.all())</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 序列化输出上的字段将仅包含 <code>serves_hot_dogs</code> 属性。 基类的 <code>name</code> 属性将被忽略。 | |
− | |||
− | + | 为了完全序列化您的 <code>Restaurant</code> 实例,您还需要序列化 <code>Place</code> 模型: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第161行: | 第138行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">all_objects = [*Restaurant.objects.all(), *Place.objects.all()] |
− | data = serializers.serialize('xml', all_objects)</ | + | data = serializers.serialize('xml', all_objects)</syntaxhighlight> |
</div> | </div> | ||
第173行: | 第150行: | ||
<div id="deserializing-data" class="section"> | <div id="deserializing-data" class="section"> | ||
− | == | + | == 反序列化数据 == |
− | + | 反序列化数据也是一个相当简单的操作: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第181行: | 第158行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">for obj in serializers.deserialize("xml", data): |
− | do_something_with(obj)</ | + | do_something_with(obj)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如您所见,<code>deserialize</code> 函数采用与 <code>serialize</code> 相同的格式参数,一个字符串或数据流,并返回一个迭代器。 | |
− | <code>serialize</code> | ||
− | + | 然而,这里有点复杂。 <code>deserialize</code> 迭代器 ''返回的对象不是'' 简单的 Django 对象。 相反,它们是特殊的 <code>DeserializedObject</code> 实例,用于包装已创建但未保存的对象和任何关联的关系数据。 | |
− | <code>deserialize</code> | ||
− | |||
− | |||
− | + | 调用 <code>DeserializedObject.save()</code> 将对象保存到数据库中。 | |
<div class="admonition note"> | <div class="admonition note"> | ||
− | + | 笔记 | |
− | + | 如果序列化数据中的 <code>pk</code> 属性不存在或为空,则将新实例保存到数据库中。 | |
− | |||
</div> | </div> | ||
− | + | 这确保了反序列化是一种非破坏性操作,即使序列化表示中的数据与数据库中当前的数据不匹配。 通常,使用这些 <code>DeserializedObject</code> 实例看起来像: | |
− | |||
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第215行: | 第184行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">for deserialized_object in serializers.deserialize("xml", data): |
if object_should_be_saved(deserialized_object): | if object_should_be_saved(deserialized_object): | ||
− | deserialized_object.save()</ | + | deserialized_object.save()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 换句话说,通常的用途是检查反序列化的对象,以确保它们在执行之前“适合”保存。 当然,如果您信任您的数据源,您可以保存对象并继续。 | |
− | |||
− | |||
− | + | Django 对象本身可以被检查为 <code>deserialized_object.object</code>。 如果序列化数据中的字段在模型上不存在,除非将 <code>ignorenonexistent</code> 参数作为 <code>True</code> 传入,否则将引发 <code>DeserializationError</code>: | |
− | |||
− | <code> | ||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第235行: | 第199行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">serializers.deserialize("xml", data, ignorenonexistent=True)</syntaxhighlight> |
</div> | </div> | ||
第244行: | 第208行: | ||
<div id="serialization-formats" class="section"> | <div id="serialization-formats" class="section"> | ||
− | <span id=" | + | <span id="id4"></span> |
− | == | + | == 序列化格式 == |
− | Django | + | Django 支持多种序列化格式,其中一些需要您安装第三方 Python 模块: |
− | |||
{| | {| | ||
− | !width="14%"| | + | !width="14%"| 标识符 |
− | !width="86%"| | + | !width="86%"| 信息 |
|- | |- | ||
| <code>xml</code> | | <code>xml</code> | ||
− | | | + | | 与简单的 XML 方言之间进行序列化。 |
|- | |- | ||
| <code>json</code> | | <code>json</code> | ||
− | | | + | | 与 [https://json.org/ JSON] 之间的序列化。 |
|- | |- | ||
| <code>yaml</code> | | <code>yaml</code> | ||
− | | | + | | 序列化为 YAML(YAML 不是标记语言)。 此序列化程序仅在安装了 [https://pyyaml.org/ PyYAML] 时可用。 |
− | |||
|} | |} | ||
第269行: | 第231行: | ||
=== XML === | === XML === | ||
− | + | 基本的 XML 序列化格式非常简单: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第275行: | 第237行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"><?xml version="1.0" encoding="utf-8"?> |
− | + | <django-objects version="1.0"> | |
− | + | <object pk="123" model="sessions.session"> | |
− | + | <field type="DateTimeField" name="expire_date">2013-01-16T08:16:59.844560+00:00</field> | |
− | + | <!-- ... --> | |
− | + | </object> | |
− | + | </django-objects></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 序列化或反序列化的整个对象集合由包含多个 <code><object></code> 元素的 <code><django-objects></code> 标签表示。 每个这样的对象都有两个属性:“pk”和“model”,后者由应用程序的名称(“sessions”)和模型的小写名称(“session”)表示,中间用点分隔。 | |
− | |||
− | <code>< | ||
− | |||
− | |||
− | + | 对象的每个字段都被序列化为一个 <code><field></code> 元素,其中包含字段“类型”和“名称”。 元素的文本内容表示应该存储的值。 | |
− | |||
− | |||
− | + | 外键和其他关系字段的处理方式略有不同: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第302行: | 第258行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"><object pk="27" model="auth.permission"> |
− | + | <!-- ... --> | |
− | + | <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">9</field> | |
− | + | <!-- ... --> | |
− | + | </object></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 在此示例中,我们指定具有 PK 27 的 <code>auth.Permission</code> 对象具有指向具有 PK 9 的 <code>contenttypes.ContentType</code> 实例的外键。 | |
− | |||
− | + | 为绑定它们的模型导出多对多关系。 例如,<code>auth.User</code> 模型与 <code>auth.Permission</code> 模型有这样的关系: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第321行: | 第275行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"><object pk="1" model="auth.user"> |
− | + | <!-- ... --> | |
− | + | <field to="auth.permission" name="user_permissions" rel="ManyToManyRel"> | |
− | + | <object pk="46"></object> | |
− | + | <object pk="47"></object> | |
− | + | </field> | |
− | + | </object></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 此示例将给定用户与具有 PK 46 和 47 的权限模型相关联。 | |
<div class="admonition-control-characters admonition"> | <div class="admonition-control-characters admonition"> | ||
− | + | 控制字符 | |
− | + | 如果要序列化的内容包含 XML 1.0 标准中不接受的控制字符,则序列化将失败并出现 <code>ValueError</code> 异常。 另请阅读 W3C 对 [https://www.w3.org/International/questions/qa-controls HTML、XHTML、XML 和控制代码] 的解释。 | |
− | |||
− | <code>ValueError</code> | ||
− | |||
第349行: | 第300行: | ||
<div id="serialization-formats-json" class="section"> | <div id="serialization-formats-json" class="section"> | ||
− | <span id=" | + | <span id="id5"></span> |
=== JSON === | === JSON === | ||
− | + | 当使用与之前相同的示例数据时,它将按以下方式序列化为 JSON: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第359行: | 第309行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">[ |
{ | { | ||
− | + | "pk": "4b678b301dfd8a4e0dad910de3ae245b", | |
− | + | "model": "sessions.session", | |
− | + | "fields": { | |
− | + | "expire_date": "2013-01-16T08:16:59.844Z", | |
... | ... | ||
} | } | ||
} | } | ||
− | ]</ | + | ]</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 此处的格式设置比 XML 简单一些。 整个集合仅表示为一个数组,对象由具有三个属性的 JSON 对象表示:“pk”、“model”和“fields”。 “fields”也是一个对象,分别包含每个字段的名称和值作为属性和属性值。 | |
− | |||
− | |||
− | |||
− | |||
− | + | 外键只是将链接对象的 PK 作为属性值。 ManyToMany 关系针对定义它们的模型进行序列化,并表示为 PK 列表。 | |
− | ManyToMany | ||
− | |||
− | + | 请注意,并非所有 Django 输出都可以不加修改地传递给 <code>json</code>。 例如,如果要序列化的对象中有一些自定义类型,则必须为其编写自定义 <code>json</code> 编码器。 像这样的事情会起作用: | |
− | |||
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第392行: | 第333行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">from django.core.serializers.json import DjangoJSONEncoder |
class LazyEncoder(DjangoJSONEncoder): | class LazyEncoder(DjangoJSONEncoder): | ||
第398行: | 第339行: | ||
if isinstance(obj, YourCustomType): | if isinstance(obj, YourCustomType): | ||
return str(obj) | return str(obj) | ||
− | return super().default(obj)</ | + | return super().default(obj)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 然后,您可以将 <code>cls=LazyEncoder</code> 传递给 <code>serializers.serialize()</code> 函数: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第410行: | 第350行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">from django.core.serializers import serialize |
− | serialize('json', SomeModel.objects.all(), cls=LazyEncoder)</ | + | serialize('json', SomeModel.objects.all(), cls=LazyEncoder)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 另请注意,GeoDjango 提供了 [[../../ref/contrib/gis/serializers|自定义 GeoJSON 序列化程序]] 。 | |
<div id="djangojsonencoder" class="section"> | <div id="djangojsonencoder" class="section"> | ||
− | ==== | + | ==== DjangoJSONEncoder ==== |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">django.core.serializers.json.</span></span><span class="sig-name descname"><span class="pre">DjangoJSONEncoder</span></span> |
: | : | ||
− | + | JSON 序列化程序使用 <code>DjangoJSONEncoder</code> 进行编码。 <code>JSONEncoder</code> 的子类,它处理这些附加类型: | |
− | <code>JSONEncoder</code> | ||
; <code>datetime</code> | ; <code>datetime</code> | ||
− | : | + | : [https://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15 ECMA-262] 中定义的 <code>YYYY-MM-DDTHH:mm:ss.sssZ</code> 或 <code>YYYY-MM-DDTHH:mm:ss.sss+HH:MM</code> 形式的字符串。 |
; <code>date</code> | ; <code>date</code> | ||
− | : | + | : [https://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15 ECMA-262] 中定义的 <code>YYYY-MM-DD</code> 形式的字符串。 |
; <code>time</code> | ; <code>time</code> | ||
− | : | + | : [https://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15 ECMA-262] 中定义的 <code>HH:MM:ss.sss</code> 形式的字符串。 |
; <code>timedelta</code> | ; <code>timedelta</code> | ||
− | : | + | : 表示 ISO-8601 中定义的持续时间的字符串。 例如,<code>timedelta(days=1, hours=2, seconds=3.4)</code> 表示为 <code>'P1DT02H00M03.400000S'</code>。 |
− | ; <code>Decimal</code> | + | ; <code>Decimal</code>、<code>Promise</code>(<code>django.utils.functional.lazy()</code> 对象)、<code>UUID</code> |
− | : | + | : 对象的字符串表示形式。 |
第448行: | 第387行: | ||
=== YAML === | === YAML === | ||
− | YAML | + | YAML 序列化看起来与 JSON 非常相似。 对象列表被序列化为具有键“pk”、“模型”和“字段”的序列映射。 每个字段又是一个映射,键是字段名,值是值: |
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第456行: | 第393行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">- fields: {expire_date: !!timestamp '2013-01-16 08:16:59.844560+00:00'} |
model: sessions.session | model: sessions.session | ||
− | pk: 4b678b301dfd8a4e0dad910de3ae245b</ | + | pk: 4b678b301dfd8a4e0dad910de3ae245b</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 引用字段再次仅由 PK 或 PK 序列表示。 | |
第472行: | 第409行: | ||
<span id="topics-serialization-natural-keys"></span> | <span id="topics-serialization-natural-keys"></span> | ||
− | == | + | == 自然键 == |
− | + | 外键和多对多关系的默认序列化策略是序列化关系中对象的主键的值。 此策略适用于大多数对象,但在某些情况下可能会造成困难。 | |
− | |||
− | |||
− | |||
− | + | 考虑具有引用 [[../../ref/contrib/contenttypes#django.contrib.contenttypes.models|ContentType]] 的外键的对象列表的情况。 如果您要序列化引用内容类型的对象,那么您需要有一种方法来引用该内容类型。 由于 <code>ContentType</code> 对象是 Django 在数据库同步过程中自动创建的,给定内容类型的主键不容易预测; 这将取决于 [[#id6|:djadmin:`migrate`]] 的执行方式和时间。 这适用于所有自动生成对象的模型,特别是包括 [[../../ref/contrib/auth#django.contrib.auth.models|Permission]]、[[../../ref/contrib/auth#django.contrib.auth.models|Group]] 和 [[../../ref/contrib/auth#django.contrib.auth.models|User]]。 | |
− | [[../../ref/contrib/contenttypes#django.contrib.contenttypes.models| | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | [[../../ref/contrib/auth#django.contrib.auth.models| | ||
− | [[../../ref/contrib/auth#django.contrib.auth.models| | ||
− | [[../../ref/contrib/auth#django.contrib.auth.models| | ||
<div class="admonition warning"> | <div class="admonition warning"> | ||
第495行: | 第419行: | ||
警告 | 警告 | ||
− | + | 您永远不应该在夹具或其他序列化数据中包含自动生成的对象。 偶然地,夹具中的主键可能与数据库中的主键匹配,并且加载夹具将不起作用。 在更可能的情况下,它们不匹配,夹具加载将失败并显示 [[../../ref/exceptions#django.db|IntegrityError]]。 | |
− | |||
− | |||
− | |||
− | |||
</div> | </div> | ||
− | + | 还有一个方便的问题。 整数 id 并不总是引用对象的最方便的方式; 有时,更自然的参考会有所帮助。 | |
− | |||
− | |||
− | + | 正是出于这些原因,Django 提供了 ''自然键'' 。 自然键是一组值,可用于在不使用主键值的情况下唯一标识对象实例。 | |
− | |||
− | |||
<div id="deserialization-of-natural-keys" class="section"> | <div id="deserialization-of-natural-keys" class="section"> | ||
− | === | + | === 自然键的反序列化 === |
− | + | 考虑以下两个模型: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第521行: | 第437行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">from django.db import models |
class Person(models.Model): | class Person(models.Model): | ||
第534行: | 第450行: | ||
class Book(models.Model): | class Book(models.Model): | ||
name = models.CharField(max_length=100) | name = models.CharField(max_length=100) | ||
− | author = models.ForeignKey(Person, on_delete=models.CASCADE)</ | + | author = models.ForeignKey(Person, on_delete=models.CASCADE)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 通常,<code>Book</code> 的序列化数据将使用整数来指代作者。 例如,在 JSON 中,一本书可能被序列化为: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第546行: | 第461行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">... |
{ | { | ||
− | + | "pk": 1, | |
− | + | "model": "store.book", | |
− | + | "fields": { | |
− | + | "name": "Mostly Harmless", | |
− | + | "author": 42 | |
} | } | ||
} | } | ||
− | ...</ | + | ...</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这不是指代作者的一种特别自然的方式。 它要求您知道作者的主键值; 它还要求这个主键值是稳定的和可预测的。 | |
− | |||
− | |||
− | + | 但是,如果我们为 Person 添加自然键处理,则装置变得更加人性化。 要添加自然键处理,您可以使用 <code>get_by_natural_key()</code> 方法为 Person 定义一个默认管理器。 在 Person 的情况下,一个好的自然键可能是一对名字和姓氏: | |
− | |||
− | |||
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第574行: | 第483行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">from django.db import models |
class PersonManager(models.Manager): | class PersonManager(models.Manager): | ||
第588行: | 第497行: | ||
class Meta: | class Meta: | ||
− | unique_together = [[../'first_name', 'last_name']]</ | + | unique_together = [[../'first_name', 'last_name']]</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 现在书籍可以使用该自然键来引用 <code>Person</code> 对象: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第599行: | 第508行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">... |
{ | { | ||
− | + | "pk": 1, | |
− | + | "model": "store.book", | |
− | + | "fields": { | |
− | + | "name": "Mostly Harmless", | |
− | + | "author": ["Douglas", "Adams"] | |
} | } | ||
} | } | ||
− | ...</ | + | ...</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 当您尝试加载这个序列化数据时,Django 将使用 <code>get_by_natural_key()</code> 方法将 <code>["Douglas", "Adams"]</code> 解析为实际 <code>Person</code> 对象的主键。 | |
− | <code>get_by_natural_key()</code> | ||
− | |||
<div class="admonition note"> | <div class="admonition note"> | ||
− | + | 笔记 | |
− | + | 用于自然键的任何字段都必须能够唯一标识一个对象。 这通常意味着您的模型将为您的自然键中的一个或多个字段提供一个唯一性子句(在单个字段上使用 unique=True,或者在多个字段上使用 <code>unique_together</code>)。 但是,不需要在数据库级别强制执行唯一性。 如果您确定一组字段实际上是唯一的,您仍然可以将这些字段用作自然键。 | |
− | |||
− | |||
− | <code>unique_together</code> | ||
− | |||
− | |||
− | |||
− | |||
</div> | </div> | ||
− | + | 没有主键的对象的反序列化将始终检查模型的管理器是否有 <code>get_by_natural_key()</code> 方法,如果有,使用它来填充反序列化对象的主键。 | |
− | |||
− | |||
第640行: | 第538行: | ||
<div id="serialization-of-natural-keys" class="section"> | <div id="serialization-of-natural-keys" class="section"> | ||
− | === | + | === 自然键的序列化 === |
− | + | 那么如何让 Django 在序列化对象时发出自然键呢? 首先,您需要添加另一个方法——这次是添加到模型本身: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第649行: | 第546行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">class Person(models.Model): |
first_name = models.CharField(max_length=100) | first_name = models.CharField(max_length=100) | ||
last_name = models.CharField(max_length=100) | last_name = models.CharField(max_length=100) | ||
第660行: | 第557行: | ||
def natural_key(self): | def natural_key(self): | ||
− | return (self.first_name, self.last_name)</ | + | return (self.first_name, self.last_name)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 该方法应该总是返回一个自然键元组——在这个例子中,<code>(first name, last name)</code>。 然后,当您调用 <code>serializers.serialize()</code> 时,您提供 <code>use_natural_foreign_keys=True</code> 或 <code>use_natural_primary_keys=True</code> 参数: | |
− | |||
− | <code>serializers.serialize()</code> | ||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第674行: | 第568行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> serializers.serialize('json', [book1, book2], indent=2, |
− | ... use_natural_foreign_keys=True, use_natural_primary_keys=True)</ | + | ... use_natural_foreign_keys=True, use_natural_primary_keys=True)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 当指定 <code>use_natural_foreign_keys=True</code> 时,Django 将使用 <code>natural_key()</code> 方法将任何外键引用序列化到定义该方法的类型的对象。 | |
− | <code>natural_key()</code> | ||
− | |||
− | + | 当指定 <code>use_natural_primary_keys=True</code> 时,Django 不会在此对象的序列化数据中提供主键,因为它可以在反序列化过程中计算: | |
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第692行: | 第582行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">... |
{ | { | ||
− | + | "model": "store.person", | |
− | + | "fields": { | |
− | + | "first_name": "Douglas", | |
− | + | "last_name": "Adams", | |
− | + | "birth_date": "1952-03-11", | |
} | } | ||
} | } | ||
− | ...</ | + | ...</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 当您需要将序列化数据加载到现有数据库中并且您不能保证序列化主键值尚未被使用,并且不需要确保反序列化对象保留相同的主键时,这会很有用。 | |
− | |||
− | |||
− | |||
− | + | 如果您使用 [[#id8|:djadmin:`dumpdata`]] 生成序列化数据,请使用 <code>dumpdata --natural-foreign</code> 和 <code>dumpdata --natural-primary</code> 命令行标志生成自然键。 | |
− | |||
− | |||
<div class="admonition note"> | <div class="admonition note"> | ||
− | + | 笔记 | |
− | + | 您不需要同时定义 <code>natural_key()</code> 和 <code>get_by_natural_key()</code>。 如果你不希望 Django 在序列化过程中输出自然键,但又想保留加载自然键的能力,那么你可以选择不实现 <code>natural_key()</code> 方法。 | |
− | <code>get_by_natural_key()</code> | ||
− | |||
− | |||
− | |||
− | + | 相反,如果(出于某种奇怪的原因)您希望 Django 在序列化期间输出自然键,但 ''not'' 能够加载这些键值,只需不要定义 <code>get_by_natural_key()</code> 方法。 | |
− | |||
− | |||
第735行: | 第614行: | ||
<div id="natural-keys-and-forward-references" class="section"> | <div id="natural-keys-and-forward-references" class="section"> | ||
− | <span id=" | + | <span id="id10"></span> |
− | === | + | === 自然键和前向引用 === |
<div class="versionadded"> | <div class="versionadded"> | ||
+ | <span class="versionmodified added">2.2 版中的新功能。</span> | ||
</div> | </div> | ||
− | + | 有时,当您使用 [[#topics-serialization-natural-keys|自然外键]] 时,您需要反序列化数据,其中一个对象的外键引用了另一个尚未反序列化的对象。 这称为“前向参考”。 | |
− | |||
− | |||
− | + | 例如,假设您的夹具中有以下对象: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第753行: | 第631行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">... |
{ | { | ||
− | + | "model": "store.book", | |
− | + | "fields": { | |
− | + | "name": "Mostly Harmless", | |
− | + | "author": ["Douglas", "Adams"] | |
} | } | ||
}, | }, | ||
... | ... | ||
{ | { | ||
− | + | "model": "store.person", | |
− | + | "fields": { | |
− | + | "first_name": "Douglas", | |
− | + | "last_name": "Adams" | |
} | } | ||
}, | }, | ||
− | ...</ | + | ...</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 为了处理这种情况,您需要将 <code>handle_forward_references=True</code> 传递给 <code>serializers.deserialize()</code>。 这将在 <code>DeserializedObject</code> 实例上设置 <code>deferred_fields</code> 属性。 您需要跟踪此属性不是 <code>None</code> 的 <code>DeserializedObject</code> 实例,然后在它们上调用 <code>save_deferred_fields()</code>。 | |
− | <code>handle_forward_references=True</code> | ||
− | |||
− | |||
− | |||
− | + | 典型用法如下所示: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第786行: | 第660行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">objs_with_deferred_fields = [] |
for obj in serializers.deserialize('xml', data, handle_forward_references=True): | for obj in serializers.deserialize('xml', data, handle_forward_references=True): | ||
第794行: | 第668行: | ||
for obj in objs_with_deferred_fields: | for obj in objs_with_deferred_fields: | ||
− | obj.save_deferred_fields()</ | + | obj.save_deferred_fields()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 为此,参考模型上的 <code>ForeignKey</code> 必须具有 <code>null=True</code>。 | |
− | <code>null=True</code> | ||
第806行: | 第679行: | ||
<div id="dependencies-during-serialization" class="section"> | <div id="dependencies-during-serialization" class="section"> | ||
− | === | + | === 序列化期间的依赖 === |
− | + | 通常可以通过注意夹具内对象的顺序来避免显式处理前向引用。 | |
− | |||
− | + | 为了解决这个问题,使用 <code>dumpdata --natural-foreign</code> 选项调用 [[#id11|:djadmin:`dumpdata`]] 将在序列化标准主键对象之前使用 <code>natural_key()</code> 方法序列化任何模型。 | |
− | |||
− | + | 然而,这可能并不总是足够的。 如果您的自然键引用另一个对象(通过使用另一个对象的外键或自然键作为自然键的一部分),那么您需要能够确保自然键所依赖的对象出现在序列化数据中在自然键需要它们之前。 | |
− | |||
− | |||
− | |||
− | |||
− | + | 要控制此顺序,您可以定义对 <code>natural_key()</code> 方法的依赖关系。 您可以通过在 <code>natural_key()</code> 方法本身上设置 <code>dependencies</code> 属性来完成此操作。 | |
− | <code>natural_key()</code> | ||
− | |||
− | + | 例如,让我们为上面示例中的 <code>Book</code> 模型添加一个自然键: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第831行: | 第695行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">class Book(models.Model): |
name = models.CharField(max_length=100) | name = models.CharField(max_length=100) | ||
author = models.ForeignKey(Person, on_delete=models.CASCADE) | author = models.ForeignKey(Person, on_delete=models.CASCADE) | ||
def natural_key(self): | def natural_key(self): | ||
− | return (self.name,) + self.author.natural_key()</ | + | return (self.name,) + self.author.natural_key()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | <code>Book</code> 的自然键是其名称和作者的组合。 这意味着 <code>Person</code> 必须在 <code>Book</code> 之前序列化。 为了定义这个依赖,我们额外添加了一行: | |
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第849行: | 第711行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">def natural_key(self): |
return (self.name,) + self.author.natural_key() | return (self.name,) + self.author.natural_key() | ||
− | natural_key.dependencies = ['example_app.person']</ | + | natural_key.dependencies = ['example_app.person']</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 此定义确保所有 <code>Person</code> 对象在任何 <code>Book</code> 对象之前序列化。 反过来,在 <code>Person</code> 和 <code>Book</code> 都被序列化之后,任何引用 <code>Book</code> 的对象都将被序列化。 | |
− | + | ||
− | |||
+ | </div> | ||
</div> | </div> | ||
</div> | </div> | ||
+ | <div class="clearer"> | ||
+ | |||
+ | |||
</div> | </div> | ||
− | [[Category:Django 2.2.x | + | [[Category:Django 2.2.x 文档]] |
2021年10月31日 (日) 04:08的最新版本
序列化 Django 对象
Django 的序列化框架提供了一种将 Django 模型“翻译”成其他格式的机制。 通常这些其他格式将基于文本并用于通过线路发送 Django 数据,但序列化程序可以处理任何格式(基于文本或非文本)。
序列化数据
在最高级别,序列化数据是一个非常简单的操作:
serialize
函数的参数是将数据序列化为的格式(请参阅 序列化格式 )和要序列化的 QuerySet。 (实际上,第二个参数可以是任何生成 Django 模型实例的迭代器,但它几乎总是一个 QuerySet)。
- django.core.serializers.get_serializer(format)
您还可以直接使用序列化程序对象:
如果您想将数据直接序列化为类似文件的对象(包括 HttpResponse),这将非常有用:
字段子集
如果您只想序列化字段的子集,则可以为序列化程序指定 fields
参数:
在本例中,只会序列化每个模型的 name
和 size
属性。 主键始终序列化为结果输出中的 pk
元素; 它从未出现在 fields
部分。
笔记
根据您的模型,您可能会发现无法反序列化仅序列化其字段子集的模型。 如果序列化对象没有指定模型所需的所有字段,反序列化器将无法保存反序列化实例。
反序列化数据
反序列化数据也是一个相当简单的操作:
如您所见,deserialize
函数采用与 serialize
相同的格式参数,一个字符串或数据流,并返回一个迭代器。
然而,这里有点复杂。 deserialize
迭代器 返回的对象不是 简单的 Django 对象。 相反,它们是特殊的 DeserializedObject
实例,用于包装已创建但未保存的对象和任何关联的关系数据。
调用 DeserializedObject.save()
将对象保存到数据库中。
笔记
如果序列化数据中的 pk
属性不存在或为空,则将新实例保存到数据库中。
这确保了反序列化是一种非破坏性操作,即使序列化表示中的数据与数据库中当前的数据不匹配。 通常,使用这些 DeserializedObject
实例看起来像:
换句话说,通常的用途是检查反序列化的对象,以确保它们在执行之前“适合”保存。 当然,如果您信任您的数据源,您可以保存对象并继续。
Django 对象本身可以被检查为 deserialized_object.object
。 如果序列化数据中的字段在模型上不存在,除非将 ignorenonexistent
参数作为 True
传入,否则将引发 DeserializationError
:
序列化格式
Django 支持多种序列化格式,其中一些需要您安装第三方 Python 模块:
标识符 | 信息 |
---|---|
xml
|
与简单的 XML 方言之间进行序列化。 |
json
|
与 JSON 之间的序列化。 |
yaml
|
序列化为 YAML(YAML 不是标记语言)。 此序列化程序仅在安装了 PyYAML 时可用。 |
XML
基本的 XML 序列化格式非常简单:
序列化或反序列化的整个对象集合由包含多个 <object>
元素的 <django-objects>
标签表示。 每个这样的对象都有两个属性:“pk”和“model”,后者由应用程序的名称(“sessions”)和模型的小写名称(“session”)表示,中间用点分隔。
对象的每个字段都被序列化为一个 <field>
元素,其中包含字段“类型”和“名称”。 元素的文本内容表示应该存储的值。
外键和其他关系字段的处理方式略有不同:
在此示例中,我们指定具有 PK 27 的 auth.Permission
对象具有指向具有 PK 9 的 contenttypes.ContentType
实例的外键。
为绑定它们的模型导出多对多关系。 例如,auth.User
模型与 auth.Permission
模型有这样的关系:
此示例将给定用户与具有 PK 46 和 47 的权限模型相关联。
JSON
当使用与之前相同的示例数据时,它将按以下方式序列化为 JSON:
此处的格式设置比 XML 简单一些。 整个集合仅表示为一个数组,对象由具有三个属性的 JSON 对象表示:“pk”、“model”和“fields”。 “fields”也是一个对象,分别包含每个字段的名称和值作为属性和属性值。
外键只是将链接对象的 PK 作为属性值。 ManyToMany 关系针对定义它们的模型进行序列化,并表示为 PK 列表。
请注意,并非所有 Django 输出都可以不加修改地传递给 json
。 例如,如果要序列化的对象中有一些自定义类型,则必须为其编写自定义 json
编码器。 像这样的事情会起作用:
然后,您可以将 cls=LazyEncoder
传递给 serializers.serialize()
函数:
另请注意,GeoDjango 提供了 自定义 GeoJSON 序列化程序 。
DjangoJSONEncoder
- class django.core.serializers.json.DjangoJSONEncoder
JSON 序列化程序使用 DjangoJSONEncoder
进行编码。 JSONEncoder
的子类,它处理这些附加类型:
datetime
- ECMA-262 中定义的
YYYY-MM-DDTHH:mm:ss.sssZ
或YYYY-MM-DDTHH:mm:ss.sss+HH:MM
形式的字符串。 date
- ECMA-262 中定义的
YYYY-MM-DD
形式的字符串。 time
- ECMA-262 中定义的
HH:MM:ss.sss
形式的字符串。 timedelta
- 表示 ISO-8601 中定义的持续时间的字符串。 例如,
timedelta(days=1, hours=2, seconds=3.4)
表示为'P1DT02H00M03.400000S'
。 Decimal
、Promise
(django.utils.functional.lazy()
对象)、UUID
- 对象的字符串表示形式。
YAML
YAML 序列化看起来与 JSON 非常相似。 对象列表被序列化为具有键“pk”、“模型”和“字段”的序列映射。 每个字段又是一个映射,键是字段名,值是值:
引用字段再次仅由 PK 或 PK 序列表示。
自然键
外键和多对多关系的默认序列化策略是序列化关系中对象的主键的值。 此策略适用于大多数对象,但在某些情况下可能会造成困难。
考虑具有引用 ContentType 的外键的对象列表的情况。 如果您要序列化引用内容类型的对象,那么您需要有一种方法来引用该内容类型。 由于 ContentType
对象是 Django 在数据库同步过程中自动创建的,给定内容类型的主键不容易预测; 这将取决于 :djadmin:`migrate` 的执行方式和时间。 这适用于所有自动生成对象的模型,特别是包括 Permission、Group 和 User。
警告
您永远不应该在夹具或其他序列化数据中包含自动生成的对象。 偶然地,夹具中的主键可能与数据库中的主键匹配,并且加载夹具将不起作用。 在更可能的情况下,它们不匹配,夹具加载将失败并显示 IntegrityError。
还有一个方便的问题。 整数 id 并不总是引用对象的最方便的方式; 有时,更自然的参考会有所帮助。
正是出于这些原因,Django 提供了 自然键 。 自然键是一组值,可用于在不使用主键值的情况下唯一标识对象实例。
自然键的反序列化
考虑以下两个模型:
通常,Book
的序列化数据将使用整数来指代作者。 例如,在 JSON 中,一本书可能被序列化为:
这不是指代作者的一种特别自然的方式。 它要求您知道作者的主键值; 它还要求这个主键值是稳定的和可预测的。
但是,如果我们为 Person 添加自然键处理,则装置变得更加人性化。 要添加自然键处理,您可以使用 get_by_natural_key()
方法为 Person 定义一个默认管理器。 在 Person 的情况下,一个好的自然键可能是一对名字和姓氏:
现在书籍可以使用该自然键来引用 Person
对象:
当您尝试加载这个序列化数据时,Django 将使用 get_by_natural_key()
方法将 ["Douglas", "Adams"]
解析为实际 Person
对象的主键。
笔记
用于自然键的任何字段都必须能够唯一标识一个对象。 这通常意味着您的模型将为您的自然键中的一个或多个字段提供一个唯一性子句(在单个字段上使用 unique=True,或者在多个字段上使用 unique_together
)。 但是,不需要在数据库级别强制执行唯一性。 如果您确定一组字段实际上是唯一的,您仍然可以将这些字段用作自然键。
没有主键的对象的反序列化将始终检查模型的管理器是否有 get_by_natural_key()
方法,如果有,使用它来填充反序列化对象的主键。
自然键的序列化
那么如何让 Django 在序列化对象时发出自然键呢? 首先,您需要添加另一个方法——这次是添加到模型本身:
该方法应该总是返回一个自然键元组——在这个例子中,(first name, last name)
。 然后,当您调用 serializers.serialize()
时,您提供 use_natural_foreign_keys=True
或 use_natural_primary_keys=True
参数:
当指定 use_natural_foreign_keys=True
时,Django 将使用 natural_key()
方法将任何外键引用序列化到定义该方法的类型的对象。
当指定 use_natural_primary_keys=True
时,Django 不会在此对象的序列化数据中提供主键,因为它可以在反序列化过程中计算:
当您需要将序列化数据加载到现有数据库中并且您不能保证序列化主键值尚未被使用,并且不需要确保反序列化对象保留相同的主键时,这会很有用。
如果您使用 :djadmin:`dumpdata` 生成序列化数据,请使用 dumpdata --natural-foreign
和 dumpdata --natural-primary
命令行标志生成自然键。
笔记
您不需要同时定义 natural_key()
和 get_by_natural_key()
。 如果你不希望 Django 在序列化过程中输出自然键,但又想保留加载自然键的能力,那么你可以选择不实现 natural_key()
方法。
相反,如果(出于某种奇怪的原因)您希望 Django 在序列化期间输出自然键,但 not 能够加载这些键值,只需不要定义 get_by_natural_key()
方法。
自然键和前向引用
2.2 版中的新功能。
有时,当您使用 自然外键 时,您需要反序列化数据,其中一个对象的外键引用了另一个尚未反序列化的对象。 这称为“前向参考”。
例如,假设您的夹具中有以下对象:
为了处理这种情况,您需要将 handle_forward_references=True
传递给 serializers.deserialize()
。 这将在 DeserializedObject
实例上设置 deferred_fields
属性。 您需要跟踪此属性不是 None
的 DeserializedObject
实例,然后在它们上调用 save_deferred_fields()
。
典型用法如下所示:
为此,参考模型上的 ForeignKey
必须具有 null=True
。
序列化期间的依赖
通常可以通过注意夹具内对象的顺序来避免显式处理前向引用。
为了解决这个问题,使用 dumpdata --natural-foreign
选项调用 :djadmin:`dumpdata` 将在序列化标准主键对象之前使用 natural_key()
方法序列化任何模型。
然而,这可能并不总是足够的。 如果您的自然键引用另一个对象(通过使用另一个对象的外键或自然键作为自然键的一部分),那么您需要能够确保自然键所依赖的对象出现在序列化数据中在自然键需要它们之前。
要控制此顺序,您可以定义对 natural_key()
方法的依赖关系。 您可以通过在 natural_key()
方法本身上设置 dependencies
属性来完成此操作。
例如,让我们为上面示例中的 Book
模型添加一个自然键:
Book
的自然键是其名称和作者的组合。 这意味着 Person
必须在 Book
之前序列化。 为了定义这个依赖,我们额外添加了一行:
此定义确保所有 Person
对象在任何 Book
对象之前序列化。 反过来,在 Person
和 Book
都被序列化之后,任何引用 Book
的对象都将被序列化。