django 3.1 序列化讲述
序列化Django对象¶
Django的序列化框架提供了一种将Django模型“翻译”为其他格式的机制。通常,这些其他格式将基于文本,并用于通过电线发送Django数据,但是序列化程序可以处理任何格式(无论是否基于文本)。
也可以看看
如果您只想从表中获取一些数据以序列化的形式,则可以使用dumpdata
管理命令。
序列化数据¶
在最高级别,您可以像这样序列化数据:
- from django.core import serializers
- data = serializers.serialize("xml", SomeModel.objects.all())
该serialize
函数的参数是将数据序列化为的格式(请参阅序列化格式)和 序列化的格式QuerySet
。(实际上,第二个参数可以是产生Django模型实例的任何迭代器,但几乎总是一个QuerySet)。
django.core.serializers.
get_serializer
(格式)¶
您也可以直接使用序列化器对象:
- XMLSerializer = serializers.get_serializer("xml")
- xml_serializer = XMLSerializer()
- xml_serializer.serialize(queryset)
- data = xml_serializer.getvalue()
如果要将数据直接序列化到类似文件的对象(包括HttpResponse
),这将很有用:
- with open("file.xml", "w") as out:
- xml_serializer.serialize(SomeModel.objects.all(), stream=out)
注意
get_serializer()
使用未知 格式的调用将引发 django.core.serializers.SerializerDoesNotExist
异常。
子集字段¶
如果只希望序列化字段的子集,则可以为序列化器指定一个fields
参数:
- from django.core import serializers
- data = serializers.serialize('xml', SomeModel.objects.all(), fields=('name','size'))
在此示例中,仅每个模型的name
和size
属性将被序列化。主键始终被序列化为pk
结果输出中的元素。它永远不会出现在fields
零件中。
注意
根据您的模型,您可能会发现无法反序列化仅序列化其字段子集的模型。如果序列化的对象未指定模型所需的所有字段,则反序列化器将无法保存反序列化的实例。
继承模型¶
如果您有使用抽象基类定义的模型,则无需执行任何特殊操作即可序列化该模型。在要序列化的一个或多个对象上调用序列化程序,输出将是序列化对象的完整表示。
但是,如果您有一个使用多表继承的模型,则还需要序列化该模型的所有基类。这是因为仅对模型上本地定义的字段进行序列化。例如,考虑以下模型:
- class Place(models.Model):
- name = models.CharField(max_length=50)
- class Restaurant(Place):
- serves_hot_dogs = models.BooleanField(default=False)
如果仅序列化餐厅模型:
- data = serializers.serialize('xml', Restaurant.objects.all())
序列化输出上的字段将仅包含serves_hot_dogs
属性。name
基类的属性将被忽略。
为了完全序列化您的Restaurant
实例,您还需要序列化Place
模型:
- all_objects = [*Restaurant.objects.all(), *Place.objects.all()]
- data = serializers.serialize('xml', all_objects)
反序列化数据¶
反序列化数据与序列化非常相似:
- for obj in serializers.deserialize("xml", data):
- do_something_with(obj)
如您所见,该deserialize
函数采用与相同的format参数 serialize
,一个字符串或数据流,并返回一个迭代器。
但是,这里有点复杂。deserialize
迭代器返回的对象 不是常规的Django对象。相反,它们是DeserializedObject
包装创建的但未保存的对象和任何关联的关系数据的特殊实例。
调用DeserializedObject.save()
将对象保存到数据库。
注意
如果pk
序列化数据中的属性不存在或为null,则新实例将保存到数据库中。
这样可以确保反序列化是一种无损操作,即使序列化表示中的数据与数据库中当前的数据不匹配也是如此。通常,使用这些DeserializedObject
实例看起来像:
- for deserialized_object in serializers.deserialize("xml", data):
- if object_should_be_saved(deserialized_object):
- deserialized_object.save()
换句话说,通常的用途是检查反序列化的对象,以确保它们在保存之前“适合”保存。如果您信任数据源,则可以直接保存对象然后继续。
Django对象本身可以按进行检查deserialized_object.object
。如果模型中不存在序列化数据中的字段,DeserializationError
则将引发a, 除非将ignorenonexistent
参数传递为True
:
- serializers.deserialize("xml", data, ignorenonexistent=True)
序列化格式¶
Django支持多种序列化格式,其中一些格式需要您安装第三方Python模块:
识别码 | 信息 |
---|---|
xml |
在简单的XML方言之间进行序列化。 |
json |
与JSON序列化。 |
yaml |
序列化为YAML(YAML不是标记语言)。仅当安装了PyYAML时,此序列化器才可用。 |
XML ¶
基本的XML序列化格式如下所示:
- <?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>
被序列化或反序列化的对象的整个集合由<django-objects>
包含多个<object>
-elements 的-tag 表示。每个此类对象都具有两个属性:“ pk”和“模型”,后者由应用程序名称(“会话”)表示,模型的小写名称(“会话”)用点分隔。
对象的每个字段都被序列化为一个带有<field>
“类型”和“名称”字段的元素。元素的文本内容表示应存储的值。
外键和其他关系字段的处理方式略有不同:
- <object pk="27" model="auth.permission">
- <!-- ... -->
- <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">9</field>
- <!-- ... -->
- </object>
在此示例中,我们指定auth.Permission
具有PK 27 的对象对具有contenttypes.ContentType
PK 9 的实例具有外键。
会为绑定它们的模型导出ManyToMany-relations。例如,auth.User
模型与模型有这样的关系auth.Permission
:
- <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>
本示例将给定用户与具有PK 46和47的权限模型链接。
控制字符
如果要序列化的内容包含XML 1.0标准不接受的控制字符,则序列化将失败,并发生 ValueError
异常。另请阅读W3C对HTML,XHTML,XML和控制代码的解释。
JSON ¶
当使用与之前相同的示例数据时,将通过以下方式将其序列化为JSON:
- [
- {
- "pk": "4b678b301dfd8a4e0dad910de3ae245b",
- "model": "sessions.session",
- "fields": {
- "expire_date": "2013-01-16T08:16:59.844Z",
- ...
- }
- }
- ]
此处的格式比使用XML更简单。整个集合只是表示为一个数组,而对象则由具有三个属性的JSON对象表示:“ pk”,“ model”和“ fields”。“ fields”还是一个对象,其中包含每个字段的名称和值分别作为属性和属性值。
外键将链接对象的PK作为属性值。ManyToMany-relations对于定义它们的模型进行了序列化,并表示为PK列表。
请注意,并非所有Django输出都可以不修改地传递给json
。例如,如果要序列化的对象中有一些自定义类型,则必须为其编写一个自定义json
编码器。这样的事情会起作用:
- from django.core.serializers.json import DjangoJSONEncoder
- class LazyEncoder(DjangoJSONEncoder):
- def default(self, obj):
- if isinstance(obj, YourCustomType):
- return str(obj)
- return super().default(obj)
然后,您可以传递cls=LazyEncoder
给serializers.serialize()
函数:
- from django.core.serializers import serialize
- serialize('json', SomeModel.objects.all(), cls=LazyEncoder)
另请注意,GeoDjango提供了自定义的GeoJSON序列化程序。
现在,所有数据都使用Unicode转储。如果您需要以前的行为,请传递ensure_ascii=True
给该serializers.serialize()
函数。
DjangoJSONEncoder
¶
- 类
django.core.serializers.json.
DjangoJSONEncoder
¶
JSON序列化程序DjangoJSONEncoder
用于编码。的子类 JSONEncoder
,它处理以下其他类型:
datetime
- 形式
YYYY-MM-DDTHH:mm:ss.sssZ
或YYYY-MM-DDTHH:mm:ss.sss+HH:MM
如ECMA-262中所定义的字符串。 date
- 格式
YYYY-MM-DD
为ECMA-262的字符串。 time
- 格式
HH:MM:ss.sss
为ECMA-262的字符串。 timedelta
- 代表ISO-8601中定义的持续时间的字符串。例如, 表示为 。
timedelta(days=1, hours=2, seconds=3.4)
'P1DT02H00M03.400000S'
Decimal
,Promise
(个django.utils.functional.lazy()
对象),UUID
- 对象的字符串表示形式。
YAML ¶
YAML序列化看起来与JSON非常相似。对象列表通过键“ pk”,“模型”和“字段”序列化为序列映射。每个字段还是一个映射,键为字段名称,值为值:
- - fields: {expire_date: !!timestamp '2013-01-16 08:16:59.844560+00:00'}
- model: sessions.session
- pk: 4b678b301dfd8a4e0dad910de3ae245b
参考字段再次由PK或PK序列表示。
现在,所有数据都使用Unicode转储。如果您需要以前的行为,请传递allow_unicode=False
给该serializers.serialize()
函数。
自然键¶
外键和多对多关系的默认序列化策略是序列化关系中对象的主键的值。该策略适用于大多数对象,但是在某些情况下可能会引起困难。
考虑具有外键引用的对象列表的情况 ContentType
。如果要序列化引用内容类型的对象,那么首先需要有一种引用该内容类型的方法。由于ContentType
对象是由Django在数据库同步过程中自动创建的,因此给定内容类型的主键不容易预测。这将取决于migrate
执行方式和执行时间。这是自动生成的对象,特别是包括所有车型真实 Permission
, Group
和 User
。
警告
绝对不要在灯具或其他序列化数据中包含自动生成的对象。偶然地,灯具中的主键可能与数据库中的主键匹配,并且加载灯具将无效。如果它们不匹配的可能性更大,则夹具加载将失败,并带有IntegrityError
。
还有方便的问题。整数id并不总是引用对象的最便捷方法。有时,更自然的参考会有所帮助。
出于这些原因,Django提供了自然键。自然键是值的元组,可用于在不使用主键值的情况下唯一地标识对象实例。
自然键反序列化¶
考虑以下两个模型:
- from django.db import models
- class Person(models.Model):
- first_name = models.CharField(max_length=100)
- last_name = models.CharField(max_length=100)
- birthdate = models.DateField()
- class Meta:
- unique_together = [['first_name', 'last_name']]
- class Book(models.Model):
- name = models.CharField(max_length=100)
- author = models.ForeignKey(Person, on_delete=models.CASCADE)
通常,的序列化数据Book
将使用整数来引用作者。例如,在JSON中,一本书可能被序列化为:
- ...
- {
- "pk": 1,
- "model": "store.book",
- "fields": {
- "name": "Mostly Harmless",
- "author": 42
- }
- }
- ...
推荐作者不是一种特别自然的方式。它要求您知道作者的主键值;它还要求该主键值是稳定且可预测的。
但是,如果我们向Person添加自然键处理,则固定装置将变得更加人性化。要添加自然键处理,请使用方法定义默认的Person Manager get_by_natural_key()
。对于个人,一个好的自然键可能是名字和姓氏对:
- from django.db import models
- class PersonManager(models.Manager):
- def get_by_natural_key(self, first_name, last_name):
- return self.get(first_name=first_name, last_name=last_name)
- class Person(models.Model):
- first_name = models.CharField(max_length=100)
- last_name = models.CharField(max_length=100)
- birthdate = models.DateField()
- objects = PersonManager()
- class Meta:
- unique_together = [['first_name', 'last_name']]
现在,书籍可以使用该自然键来引用Person
对象:
- ...
- {
- "pk": 1,
- "model": "store.book",
- "fields": {
- "name": "Mostly Harmless",
- "author": ["Douglas", "Adams"]
- }
- }
- ...
当您尝试加载此序列化数据时,Django将使用该 get_by_natural_key()
方法解析 为实际对象的主键。["Douglas", "Adams"]
Person
注意
无论您将哪个字段用作自然键,都必须能够唯一标识一个对象。这通常意味着您的模型将为unique_together
您的自然键中的一个或多个字段具有唯一性子句(在单个字段或多个字段上为unique = True )。但是,不需要在数据库级别强制执行唯一性。如果确定一组字段实际上是唯一的,则仍可以将这些字段用作自然键。
没有主键的对象的反序列化将始终检查模型管理器是否具有get_by_natural_key()
方法,如果有,请使用该方法来填充反序列化的对象的主键。
自然键序列化¶
那么,如何在序列化对象时让Django发出自然键?首先,您需要添加另一种方法–这次添加到模型本身:
- class Person(models.Model):
- first_name = models.CharField(max_length=100)
- last_name = models.CharField(max_length=100)
- birthdate = models.DateField()
- objects = PersonManager()
- class Meta:
- unique_together = [['first_name', 'last_name']]
- def natural_key(self):
- return (self.first_name, self.last_name)
该方法应始终返回自然键元组-在本示例中为。然后,当您调用时 ,您提供 或参数:(first name, last name)
serializers.serialize()
use_natural_foreign_keys=True
use_natural_primary_keys=True
- >>> serializers.serialize('json', [book1, book2], indent=2,
- ... use_natural_foreign_keys=True, use_natural_primary_keys=True)
当use_natural_foreign_keys=True
指定时,Django会使用的 natural_key()
方法来序列的任何外键参照定义的方法中的类型的对象。
当use_natural_primary_keys=True
指定时,Django将不能提供该对象的串行化数据的主键,因为它可以反序列化过程来计算:
- ...
- {
- "model": "store.person",
- "fields": {
- "first_name": "Douglas",
- "last_name": "Adams",
- "birth_date": "1952-03-11",
- }
- }
- ...
当您需要将序列化的数据加载到现有数据库中并且不能保证序列化的主键值尚未使用并且不需要确保反序列化的对象保留相同的主键时,这将很有用。
如果dumpdata
用于生成序列化数据,请使用 和 命令行标志生成自然键。dumpdata --natural-foreign
dumpdata --natural-primary
注意
您无需同时定义natural_key()
和 get_by_natural_key()
。如果您不希望Django在序列化期间输出自然键,而是希望保留加载自然键的功能,则可以选择不实现该natural_key()
方法。
相反,如果(出于某种奇怪的原因)您希望Django在序列化期间输出自然键,但又无法加载这些键值,则只需不定义该get_by_natural_key()
方法即可。
自然键和向前引用¶
有时,当您使用自然外键时,您需要对一个对象具有一个外键引用另一个尚未反序列化的对象的对象进行反序列化数据。这称为“前向参考”。
例如,假设您的灯具中包含以下对象:
- ...
- {
- "model": "store.book",
- "fields": {
- "name": "Mostly Harmless",
- "author": ["Douglas", "Adams"]
- }
- },
- ...
- {
- "model": "store.person",
- "fields": {
- "first_name": "Douglas",
- "last_name": "Adams"
- }
- },
- ...
为了处理这种情况,您需要传递 handle_forward_references=True
给serializers.deserialize()
。这将deferred_fields
在DeserializedObject
实例上设置属性。您需要跟踪DeserializedObject
没有该属性的实例,None
然后再调用save_deferred_fields()
它们。
典型用法如下:
- objs_with_deferred_fields = []
- for obj in serializers.deserialize('xml', data, handle_forward_references=True):
- obj.save()
- if obj.deferred_fields is not None:
- objs_with_deferred_fields.append(obj)
- for obj in objs_with_deferred_fields:
- obj.save_deferred_fields()
为此,ForeignKey
引用模型必须具有 null=True
。
序列化期间的依赖项¶
通常可以通过注意灯具内对象的顺序来避免显式地处理前向引用。
为了解决这个问题,dumpdata
使用该选项的调用将 在序列化标准主键对象之前使用方法序列化任何模型。dumpdata --natural-foreign
natural_key()
但是,这可能并不总是足够的。如果您的自然键引用了另一个对象(通过使用外键或另一个对象的自然键作为自然键的一部分),那么您需要确保自然键所依赖的对象出现在序列化数据中在自然键要求它们之前。
要控制此顺序,您可以定义natural_key()
方法的依赖关系 。您可以通过dependencies
在natural_key()
方法本身上设置属性来实现。
例如,让我们Book
从上面的示例向模型添加一个自然键:
- class Book(models.Model):
- name = models.CharField(max_length=100)
- author = models.ForeignKey(Person, on_delete=models.CASCADE)
- def natural_key(self):
- return (self.name,) + self.author.natural_key()
a的自然键Book
是其名称和作者的组合。这意味着Person
必须先序列化Book
。为了定义这种依赖性,我们增加了一行:
- def natural_key(self):
- return (self.name,) + self.author.natural_key()
- natural_key.dependencies = ['example_app.person']
此定义确保所有Person
对象在任何Book
对象之前先序列化。反过来,任何对象引用Book
都将经过序列化Person
,并Book
已系列化。
django 3.1 序列化讲述的更多相关文章
- Django 缓存、序列化、信号
一,缓存 由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcac ...
- Django框架之序列化和上传文件
一.Django的序列化(对于ajax请求) Django中的序列化主要应用在将数据库中检索的数据返回给客户端用户,特别的Ajax请求一般返回的为Json格式. 1)django序列化的使用方法 . ...
- django rest_framework Serializers 序列化组件
为什么要用序列化组件 当我们做前后端分离的项目~~我们前后端交互一般都选择JSON数据格式,JSON是一个轻量级的数据交互格式. 那么我们给前端数据的时候都要转成json格式,那就需要对我们从数据库拿 ...
- Django的DRF序列化方法
安装rest_framework -- pip install djangorestframework -- 注册rest_framework序列化 -- Python--json -- 第一版 用v ...
- JSON和Django内置序列化
JSON 什么是JSON JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation) JSON 是轻量级的文本数据交换格式 JSON 独立于语言 * J ...
- Django REST Framework序列化器
Django序列化和json模块的序列化 从数据库中取出数据后,虽然不能直接将queryset和model对象以及datetime类型序列化,但都可以将其转化成可以序列化的类型,再序列化. 功能需求都 ...
- AJAX—JSON和Django内置序列化
JSON 什么是JSON JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation) JSON 是轻量级的文本数据交换格式 JSON 独立于语言 * J ...
- Django学习之序列化和信号
一.序列化 1.serializers 2.json.dumps 二.信号 1.Django内置信号 2.自定义信号 一.序列化 关于Django中的序列化主要应用在将数据库中检索的数据返回给客户端用 ...
- [Django REST framework - 序列化组件、source、钩子函数]
[Django REST framework - 序列化组件.source.钩子函数] 序列化器-Serializer 什么是rest_framework序列化? 在写前后端不分离的项目时: 我们有f ...
随机推荐
- 【翻译】.NET 5 Preview8发布
[翻译].NET 5 Preview8发布 今天,.NET 5预览8发布了,对于.NET5.0的功能开发已经完成了,这必须要排除待处理的bug,预览8是最后一次预览版本.预计11月正式的.NET5.0 ...
- CSS 段落/换行/缩进
CSS 段落/换行/缩进 1.盒模型 box-sizing: content-box|border-box|inherit; content-box(默认) : 实际宽度/高度 = width/hei ...
- CSS3实现圆环进度条
摘要:圆环进度条被应用于各个场景,比如我们可以用来表示加载进度等.通常我们可以用 css3 的动画去实现. 详解 css3 实现圆环进度条 简单的画一个圆环,我们都知道如何使用 css 画一个圆环.( ...
- 23种设计模式 - 数据结构(Composite - iterator - Chain of Responsibility)
其他设计模式 23种设计模式(C++) 每一种都有对应理解的相关代码示例 → Git原码 ⌨ 数据结构 Composite 动机(Motivation) 软件在某些情况下,客户代码过多依赖于对象容器复 ...
- 基于JSP+Servlet的学生信息管理系统
JavaWeb期末项目,一个基于JSP和Servlet的学生信息管理系统实现,前端用了bootstrap和一些自定义的css样式,数据库用了mysql 传送门: GitHub 实现功能 登录(教师, ...
- eslint 的 使用常见问题(一)
在source tree 远程拉去一个项目,然后无缘无故 代码各处飘红 ,然后看了很是烦躁.碰见一下几个问题,后续持更 一.升级es6 出现这个问题的原因:let是EcmaScript 6 里面才有的 ...
- Fitness - 05.04
倒计时241天 运动38分钟,共计9组.拉伸10分钟. 每组跑步2分钟(6.3KM/h),走路2分钟(6KM/h). 上午下了课,直奔健身房. 手机坏了,没有听音乐. 没有吃午饭,但是上午喝的咖啡还是 ...
- HTTP协议中的提交方式和解释POST、GET、HEAD、PUT等
最基本的方法有4种,分别是GET,POST,PUT,DELETE HTTP中的GET,POST,PUT,DELETE就对应着对这个资源的查,改,增,删4个操作 GET一般用于获取/查询资源信息,而PO ...
- 利用css3 transform实现一个时钟
transform:rotate(1deg) <!DOCTYPE html> <html lang="en"> <head> <meta ...
- Java里一个线程两次调用start()方法会出现什么情况
Java的线程是不允许启动两次的,第二次调用必然会抛出IllegalThreadStateException,这是一种运行时异常,多次调用start被认为是编程错误. 如果业务需要线程run中的代码再 ...