序列化 serializers

阅读: 60585     评论:5

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
  • json
  • yaml

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>

外键字段被序列化成下面的格式:

<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

序列化成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

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=Trueuse_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",
    }
}
...

 信号 signal 消息框架 message 

评论总数: 5


点击登录后方可评论

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) 实际开发中,哪个方法最常用?



两种方法各有优点,根据场景进行选择。没有常用不常用的区别。