Сериализовать методы @property в классе Python
Есть ли способ, чтобы какие-либо определения @property передавались в сериализатор json при сериализации класса модели Django?
Пример:
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',
},]
Ответы
Ответ 1
Вы можете расширять сериализаторы 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", ...])
Конечно, это, вероятно, больше работы, чем просто написание собственного простого JSON-сериализатора, но, возможно, не больше работы, чем ваш собственный XML-сериализатор (вам придется переопределить "handle_field" для соответствия XML-случаю в дополнение к изменению базовый класс для этого).
Ответ 2
С 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 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
Использование:
>>> ExtJsonSerializer().serialize(MyModel.objects.all(), fields=['myfield', ...], props=['myprop', ...])
Ответ 3
Решение работало хорошо, что предлагается М. Рафаем Алемом и 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' ...])
Ответ 4
Это комбинация ответов М. Рафая Алема и Вотерса и цитат.
Это DRY и позволяет вам указывать дополнительные реквизиты, а не все поля и реквизиты, как в версии с кавычками.
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(), props=['property_1', ...])
Ответ 5
Вы можете получить все свойства класса, используя черную магию:
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']
Затем вы можете построить словарь и сериализовать его оттуда.