序列化Python类中的@property方法

序列化Python类中的@property方法,python,django,serialization,Python,Django,Serialization,在序列化Django模型类时,有没有办法将@property定义传递给json序列化程序 例如: class FooBar(object.Model) name = models.CharField(...) @property def foo(self): return "My name is %s" %self.name 要序列化到: [{ 'name' : 'Test User', 'foo' : 'My name is T

在序列化Django模型类时,有没有办法将@property定义传递给json序列化程序

例如:

class FooBar(object.Model)

    name = models.CharField(...)

    @property
    def foo(self):
        return "My name is %s" %self.name
要序列化到:

[{

    'name' : 'Test User',

    'foo' : 'My name is Test User',
},]

您可以使用一些黑魔法获得类的所有属性:

def list_class_properties(cls):
    return [k for k,v in cls.__dict__.iteritems() if type(v) is property]
例如:

>>> class Foo:
       @property
       def bar(self):
           return "bar"

>>> list_class_properties(Foo)
['bar']

然后,您可以构建字典并从中序列化它。

您可以扩展Django的序列化程序,而无需/太/多的工作。这里是一个自定义序列化程序,它接受一个查询集和一个属性列表(字段或非字段),并返回JSON

from StringIO import StringIO
from django.core.serializers.json import Serializer

class MySerializer(Serializer):
    def serialize(self, queryset, list_of_attributes, **options):
        self.options = options
        self.stream = options.get("stream", StringIO())
        self.start_serialization()
        for obj in queryset:
            self.start_object(obj)
            for field in list_of_attributes:
                self.handle_field(obj, field)
            self.end_object(obj)
        self.end_serialization()
        return self.getvalue()

    def handle_field(self, obj, field):
        self._current[field] = getattr(obj, field)
用法:

>>> MySerializer().serialize(MyModel.objects.all(), ["field1", "property2", ...])
>>> ExtJsonSerializer().serialize(MyModel.objects.all(), fields=['myfield', ...], props=['myprop', ...])

当然,这可能比编写自己更简单的JSON序列化程序要多,但也可能比编写自己的XML序列化程序要少(除了更改基类外,还必须重新定义“handle_field”以匹配XML大小写)。

自2010年以来,情况发生了一些变化,因此@user85461的答案似乎不再适用于Django 1.8和Python 3.4。这是一个更新的答案,似乎对我有用

from django.core.serializers.base import Serializer as BaseSerializer
from django.core.serializers.python import Serializer as PythonSerializer
from django.core.serializers.json import Serializer as JsonSerializer
from django.utils import six

class ExtBaseSerializer(BaseSerializer):
    """ Abstract serializer class; everything is the same as Django's base except from the marked lines """
    def serialize(self, queryset, **options):
        self.options = options

        self.stream = options.pop('stream', six.StringIO())
        self.selected_fields = options.pop('fields', None)
        self.selected_props = options.pop('props', None)  # added this
        self.use_natural_keys = options.pop('use_natural_keys', False)
        self.use_natural_foreign_keys = options.pop('use_natural_foreign_keys', False)
        self.use_natural_primary_keys = options.pop('use_natural_primary_keys', False)

        self.start_serialization()
        self.first = True
        for obj in queryset:
            self.start_object(obj)
            concrete_model = obj._meta.concrete_model
            for field in concrete_model._meta.local_fields:
                if field.serialize:
                    if field.rel is None:
                        if self.selected_fields is None or field.attname in self.selected_fields:
                            self.handle_field(obj, field)
                    else:
                        if self.selected_fields is None or field.attname[:-3] in self.selected_fields:
                            self.handle_fk_field(obj, field)
            for field in concrete_model._meta.many_to_many:
                if field.serialize:
                    if self.selected_fields is None or field.attname in self.selected_fields:
                        self.handle_m2m_field(obj, field)
            # added this loop
            if self.selected_props:
                for field in self.selected_props:
                    self.handle_prop(obj, field)
            self.end_object(obj)
            if self.first:
                self.first = False
        self.end_serialization()
        return self.getvalue()

    # added this function
    def handle_prop(self, obj, field):
        self._current[field] = getattr(obj, field)


class ExtPythonSerializer(ExtBaseSerializer, PythonSerializer):
    pass


class ExtJsonSerializer(ExtPythonSerializer, JsonSerializer):
    pass
用法:

>>> MySerializer().serialize(MyModel.objects.all(), ["field1", "property2", ...])
>>> ExtJsonSerializer().serialize(MyModel.objects.all(), fields=['myfield', ...], props=['myprop', ...])

Rafay Aleem和Wtower提出的解决方案运行良好,但它复制了大量代码。这里有一个改进:

from django.core.serializers.base import Serializer as BaseSerializer
from django.core.serializers.python import Serializer as PythonSerializer
from django.core.serializers.json import Serializer as JsonSerializer

class ExtBaseSerializer(BaseSerializer):

    def serialize_property(self, obj):
        model = type(obj)
        for field in self.selected_fields:
            if hasattr(model, field) and type(getattr(model, field)) == property:
                self.handle_prop(obj, field)

    def handle_prop(self, obj, field):
        self._current[field] = getattr(obj, field)

    def end_object(self, obj):
        self.serialize_property(obj)

        super(ExtBaseSerializer, self).end_object(obj)


class ExtPythonSerializer(ExtBaseSerializer, PythonSerializer):
    pass


class ExtJsonSerializer(ExtPythonSerializer, JsonSerializer):
    pass
如何使用它:

ExtJsonSerializer().serialize(MyModel.objects.all(), fields=['field_name_1', 'property_1' ...])
ExtJsonSerializer().serialize(MyModel.objects.all(), props=['property_1', ...])

这是M.Rafay Aleem和W.Towers answer和caots的组合。 这是干,让你只指定额外的道具,而不是所有的领域和道具在caots版本

from django.core.serializers.json import Serializer as JsonSerializer
from django.core.serializers.python import Serializer as PythonSerializer
from django.core.serializers.base import Serializer as BaseSerializer

class ExtBaseSerializer(BaseSerializer):
    def serialize(self, queryset, **options):
        self.selected_props = options.pop('props')
        return super(ExtBaseSerializer, self).serialize(queryset, **options)

    def serialize_property(self, obj):
        model = type(obj)
        for field in self.selected_props:
            if hasattr(model, field) and type(getattr(model, field)) == property:
                self.handle_prop(obj, field)

    def handle_prop(self, obj, field):
        self._current[field] = getattr(obj, field)

    def end_object(self, obj):
        self.serialize_property(obj)

        super(ExtBaseSerializer, self).end_object(obj)

class ExtPythonSerializer(ExtBaseSerializer, PythonSerializer):
    pass

class ExtJsonSerializer(ExtPythonSerializer, JsonSerializer):
    pass
如何使用它:

ExtJsonSerializer().serialize(MyModel.objects.all(), fields=['field_name_1', 'property_1' ...])
ExtJsonSerializer().serialize(MyModel.objects.all(), props=['property_1', ...])

这实际上需要创建我自己的散列,只需要序列化为一个散列。如果我走这条路,我几乎可以删掉整个系列化。我希望继续使用django模型类并简单地调用serialize('json',my_object,…),不幸的是,django的核心序列化例程似乎特别排除了
\u meta
中没有的任何内容,它基本上只查找db模型字段。因此,虽然您可以编写一个只提取属性字段的函数(这可能最好在事后使用
inspect.getmembers
方法),但即使使用
serializer.serialize
方法上的
fields
参数,也无法工作。在这里,他们迭代传递的查询集,只在
\u meta
中查找内容:获取
'MySerializer'对象没有属性'first'
Django(1.5.4)上的错误我发现前面的答案更适合同时传递字段和属性,因为否则您无法在响应中限制模型中的字段。在初始查询中,似乎所有这些都不能与.values()一起使用。@Patrick我也可以用这种方法筛选字段。尝试类似的方法:
ExtJsonSerializer().serialize(MyModel.objects.all(),fields=('a','b',…),props=['property_1',…])