【问题标题】:Serialize the @property methods in a Python class序列化 Python 类中的 @property 方法
【发布时间】:2012-10-14 06:39:54
【问题描述】:

在序列化 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',
},]

【问题讨论】:

    标签: python django serialization


    【解决方案1】:

    您可以扩展 Django 的序列化程序而无需 /too/ 太多工作。这是一个自定义序列化程序,它接受一个查询集和一个属性列表(字段或非字段),并返回 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 序列化程序更多的工作(除了更改基类来做到这一点)。

    【讨论】:

    • 在 Django (1.5.4) 上遇到 'MySerializer' object has no attribute 'first' 错误
    【解决方案2】:

    这是 M. Rafay Aleem 和 Wtowers answer and caots 的组合。 这是 DRY,只允许您指定额外的道具,而不是像 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(), props=['property_1', ...])
    

    【讨论】:

    • 我发现之前的答案stackoverflow.com/a/38253327/4140357 更可取,因为它能够同时传递字段和属性,因为否则您无法在响应中限制模型中的字段。似乎这些都不适用于初始查询中的 .values() 。
    • @Patrick 我也可以使用这种方法过滤字段。尝试类似:ExtJsonSerializer().serialize(MyModel.objects.all(), fields=('a', 'b', ...), props=['property_1', ...])
    【解决方案3】:

    M. 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' ...])
    

    【讨论】:

      【解决方案4】:

      自 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
      

      用法:

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

      【讨论】:

        【解决方案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']
        

        然后你可以构建字典并从那里序列化它。

        【讨论】:

        • 这基本上需要创建我自己的散列,只是为了序列化为本质上的散列。如果我走那条路,我几乎可以剪掉整个连载。我希望继续使用 django Model 类并简单地调用 serialize('json', my_object, ...)
        • 不幸的是,似乎 Django 的核心序列化例程专门排除了 _meta 中没有的任何内容,它基本上只查找 db 模型字段。因此,虽然您可以编写一个仅提取属性字段的函数(在第二次脸红时使用inspect.getmembers 方法可能会更好),即使在serializers.serialize 方法上使用fields 参数也行不通。看这里,他们遍历传入的查询集,只在_meta中寻找东西:code.djangoproject.com/browser/django/trunk/django/core/…
        猜你喜欢
        • 2015-02-14
        • 1970-01-01
        • 1970-01-01
        • 2021-10-24
        • 2011-03-19
        相关资源
        最近更新 更多