Django的序列化工具让你可以将Django的模型‘翻译’成其它格式的数据。通常情况下,这种其它格式的数据是基于文本的,并且用于数据交换\传输过程。
序列化:从Django数据库---Django的模型---JSON/XML等文本格式
反序列化:上面过程的反方向
对于序列化,Django REST Framework更出色,更深入。
Django为我们提供了一个强大的序列化工具serializers。使用它也很简单,如下所示:
from django.core import serializers data = serializers.serialize("xml", SomeModel.objects.all())
首先,从djang.core导入它,然后调用它的serialize方法,这个方法至少接收两个参数,第一个是你要序列化成为的数据格式,这里是‘xml’,第二个是要序列化的数据对象,数据通常是ORM模型的QuerySet,一个可迭代的对象。
就是这么简单!!
还有一种比较复杂,但钩子更多的序列化方法,如下所示:
XMLSerializer = serializers.get_serializer("xml") xml_serializer = XMLSerializer() xml_serializer.serialize(queryset) data = xml_serializer.getvalue()
主要是使用了serializers的get_serializer()和getvalue()方法。
当你需要将序列化的数据保存到一个文件对象中的时候,上面的方式就非常有用,例如:
with open("file.xml", "w") as out: xml_serializer.serialize(SomeModel.objects.all(), stream=out)
如果你不想序列化模型对象所有字段的内容,只想序列化某些指定的字段,可以使用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)
其中的data是我们以前序列化后生成的数据(一个字符串或者数据流)。deserialize()
方法返回的是一个迭代器,通过for循环,拿到它内部的每个元素。
在这里有区别的是,deserialize返回的迭代器对象不是简单的Django模型的对象。而是特殊的DeserializedObject实例。调用DeserializedObject.save()方法可以将对象保存到数据库。
PS:如果序列化数据中的pk属性不存在或为null,则新实例将保存到数据库。
将DeserializedObject保存到数据库,可以如下操作:
for deserialized_object in serializers.deserialize("xml", data): if object_should_be_saved(deserialized_object): deserialized_object.save()
在这么做之前,你必须保证你的序列化数据是合乎你本地ORM模型属性的,否则保存的过程中会出现各种让你挠头的错误,这是很显然的。
反序列化是真正的难点、重点,实际上大多数代码都集中在这里,它所需要处理的问题包括并不限于:
Djanggo支持三种序列化格式,其中的一些可能需要安装第三方库支持:
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>
外键字段被序列化成下面的格式:
<object pk="27" model="auth.permission"> <!-- ... --> <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">9</field> <!-- ... --> </object>
多对多字段被序列化成下面的样子:
<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>
序列化成json格式后,看起来是下面的样子:
[ { "pk": "4b678b301dfd8a4e0dad910de3ae245b", "model": "sessions.session", "fields": { "expire_date": "2013-01-16T08:16:59.844Z", ... } } ]
需要注意的是,如果你的ORM模型具有自定义的字段,那么Django给我们提供的序列化工具,就无法正常工作,你必须自己编写相应部分的序列化代码,下面是一个参考的例子:
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)
上面编写了一个LazyEncoder类,用来实现你的序列化方法,使用下面的方法调用它:
from django.core.serializers import serialize serialize('json', SomeModel.objects.all(), cls=LazyEncoder)
PS:Python本身不支持序列化类
到json格式,Django帮我们实现了它自己的模型类序列化为json的方法,但也仅限于此,如果你在Django内写了一个别的自定义类,一样无法序列化为json格式,除非你自己实现,像上面的例子所示。
yaml的格式和json很像,如下所示:
- fields: {expire_date: !!timestamp '2013-01-16 08:16:59.844560+00:00'} model: sessions.session pk: 4b678b301dfd8a4e0dad910de3ae245b
考虑下面的模型关系:
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模型的时候,通常提供如下的数据:
... { "pk": 1, "model": "store.book", "fields": { "name": "Mostly Harmless", "author": 42 } } ...
注意其中author外键的值为42,这很不直观。如果能直接使用作者的姓名可能会更好点。
注意:提供的数据需要是可以成为主键唯一的字段。防止发生冲突
为了解决这个需求,Django提供了一个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']] # 注意这个联合唯一约束
现在进行Book模型的序列化时,直接提供作者姓名即可:
... { "pk": 1, "model": "store.book", "fields": { "name": "Mostly Harmless", "author": ["江", "刘"] } } ...
那么,如何在序列化对象时让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": "江", "last_name": "刘", "birth_date": "1952-03-11", } } ...
QuerySet 对象传递个模板,在从模板用ajax传递给视图,数据变成了字符串QuerySet, 怎么让数据在变成可用的QuerySet???
变成list再传把
模型序列化
方法1:data = serializers.serialize("xml", SomeModel.objects.all()) 方法二:XMLSerializer = serializers.get_serializer("xml") xml_serializer = XMLSerializer() data = xml_serializer.serialize(queryset) 实际开发中,哪个方法最常用?
两种方法各有优点,根据场景进行选择。没有常用不常用的区别。