简体   繁体   English

Django继承模型的序列化

[英]Django serialization of inherited model

I have a problem with serialization of Django inherited models. 我对Django继承模型的序列化有问题。 For example 例如

class Animal(models.Model):
    color = models.CharField(max_length=50)

class Dog(Animal):
    name = models.CharField(max_length=50)

...
# now I want to serialize Dog model with Animal inherited fields obviously included
print serializers.serialize('xml', Dog.objects.all())

and only Dog model has been serialized. 并且只有Dog模型已经序列化。

I can do smth like 我可以像

all_objects = list(Animal.objects.all()) + list(Dog.objects.all())
print serializers.serialize('xml', all_objects)

But it looks ugly and because my models are very big so I have to use SAX parser and with such output it's difficult to parse. 但这看起来很丑陋,因为我的模型很大,所以我必须使用SAX解析器,并且这样的输出很难解析。

Any idea how to serialize django models with parent class? 任何想法如何与父类序列化Django模型?

**EDIT: ** It use to work ok before this patch has been applied. **编辑:**在应用此补丁之前,它可以正常工作。 And the explanation why the patch exist "Model saving was too aggressive about creating new parent class instances during deserialization. Raw save on a model now skips saving of the parent class. " I think there should be an option to be able to serialize "local fields only" by default and second option - "all" - to serialize all inherited fields. 以及补丁存在的解释“在反序列化期间,模型保存对于在创建新的父类实例时过于激进。在模型上进行原始保存现在会跳过父类的保存。”我认为应该有一个选项可以序列化“ local默认情况下,“仅字段”和第二个选项“全部”-序列化所有继承的字段。

You found your answer in the documentation of the patch. 您在补丁文档中找到了答案。

all_objects = list(Animal.objects.all()) + list(Dog.objects.all())
print serializers.serialize('xml', all_objects)

However, if you change Animal to be an abstract base class it will work: 但是,如果将Animal更改为抽象基类,它将起作用:

class Animal(models.Model):
    color = models.CharField(max_length=50)

    class Meta:
        abstract = True

class Dog(Animal):
    name = models.CharField(max_length=50)

This works as of Django 1.0. 从Django 1.0开始有效。 See http://docs.djangoproject.com/en/dev/topics/db/models/ . 参见http://docs.djangoproject.com/en/dev/topics/db/models/

You'll need a custom serializer to support inherited fields, as Django's serializer will only serialize local fields. 您将需要一个自定义的序列化程序来支持继承的字段,因为Django的序列化程序只会序列化本地字段。

I ended up writing my own when dealing with this issue, feel free to copy it: https://github.com/zmathew/django-backbone/blob/master/backbone/serializers.py 在解决此问题时,我最终写了我自己的文章,可以随时复制: https : //github.com/zmathew/django-backbone/blob/master/backbone/serializers.py

In order to use it on its own, you need to do: 为了单独使用它,您需要执行以下操作:

serializer = AllFieldsSerializer()
serializer.serialize(queryset, fields=fields)
print serializer.getvalue()

Did you look at select_related() ? 您看过select_related()吗? as in

serializers.serialize('xml', Dog.objects.select_related().all())

You can define a custom Serializer: 您可以定义一个自定义的序列化器:

class DogSerializer(serializers.ModelSerializer):  
    class Meta:  
       model = Dog 
        fields = ('color','name') 

Use it like: 像这样使用它:
serializer = DogSerializer(Dog.objects.all(), many=True) 序列化器= DogSerializer(Dog.objects.all(),many = True)
print serializer.data enter code here 打印serializer.data在此处输入代码

I had the same problem, and i wrote a 'small' queryset serializer which navigates up the inheritance tree and returns all the fields serialized. 我遇到了同样的问题,我写了一个“小” queryset序列化器,该序列化器在继承树中导航并返回所有序列化的字段。

It's far from perfect... but works for me :) 它远非完美...但对我有用:)

a = QuerySetSerializer(MyModel, myqueryset)
a.serialize()

And the snippet: 和摘要:

from __future__ import unicode_literals
import json
import inspect
from django.core import serializers
from django.db.models.base import Model as DjangoBaseModel
class QuerySetSerializer(object):
    def __init__(self, model, initial_queryset):
        """
        @param model: The model of your queryset
        @param initial_queryset: The queryset to serialize
        """
        self.model = model
        self.initial_queryset = initial_queryset
        self.inheritance_tree = self._discover_inheritance_tree()

    def serialize(self):
        list_of_querysets = self._join_inheritance_tree_objects()
        merged_querysets = self._zip_queryset_list(list_of_querysets)

        result = []
        for related_objects in merged_querysets:
            result.append(self._serialize_related_objects(related_objects))
        return json.dumps(result)

    def _serialize_related_objects(self, related_objects):
        """
        In this method, we serialize each instance using the django's serializer function as shown in :
        See https://docs.djangoproject.com/en/1.10/topics/serialization/#inherited-models

        However, it returns a list with mixed objects... Here we join those related objects into one single dict
        """
        serialized_objects = []

        for related_object in related_objects:
            serialized_object = self._serialize_object(related_object)
            fields = serialized_object['fields']
            fields['pk'] = serialized_object['pk']
            serialized_objects.append(fields)

        merged_related_objects = {k: v for d in serialized_objects for k, v in d.items()}
        return merged_related_objects

    def _serialize_object(self, obj):
        data = serializers.serialize('json', [obj, ])
        struct = json.loads(data)
        return struct[0]

    def _discover_inheritance_tree(self):
        # We need to find the inheritance tree which excludes abstract classes,
        # so we can then join them when serializing the instance
        return [x for x in inspect.getmro(self.model) if x is not object and x is not DjangoBaseModel and not x._meta.abstract]

    def _join_inheritance_tree_objects(self):
        """
        Here we join the required querysets from the non abstract inherited models, which we need so we are able to
        serialize them.

        Lets say that MyUser inherits from Customer and customer inherits from django's User model
        This will return [list(MyUser.objects.filter(...), list(Customer.objects.filter(...), list(User.objects.filter(...)
        """

        initial_ids = self._get_initial_ids()
        inheritance__querysets = [list(x.objects.filter(id__in=initial_ids).order_by("id")) for x in self.inheritance_tree]
        return inheritance__querysets

    def _zip_queryset_list(self, list_of_querysets):
        """
        At this stage, we have something like:
        (
            [MyUser1, MyUser2, MyUser3],
            [Customer1, Customer2, Customer3],
            [User1, User2, User3]
        )

        And to make it easier to work with, we 'zip' the list of lists so it looks like:
        (
            [MyUser1, Customer1, User1],
            [MyUser2, Customer2, User2],
            [MyUser3, Customer3, User3],
        )

        """
        return zip(*list_of_querysets)

    def _get_initial_ids(self):
        """
        Returns a list of ids of the initial queryset
        """
        return self.initial_queryset.order_by("id").values_list("id", flat=True)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM