Serialize the @property methods in a Python class

后端 未结 5 1536
無奈伤痛
無奈伤痛 2020-12-01 09:54

Is there a way to have any @property definitions passed through to a json serializer when serializing a Django model class?

example:

class FooBar(obj         


        
相关标签:
5条回答
  • 2020-12-01 10:05

    Things have changed a bit since 2010, so the answer of @user85461 seems to no longer be working with Django 1.8 and Python 3.4. This is an updated answer with what seems to work for me.

    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
    

    Usage:

    >>> ExtJsonSerializer().serialize(MyModel.objects.all(), fields=['myfield', ...], props=['myprop', ...])
    
    0 讨论(0)
  • 2020-12-01 10:09

    You can get all of the properties of a class using some black magic:

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

    For example:

    >>> class Foo:
           @property
           def bar(self):
               return "bar"
    
    >>> list_class_properties(Foo)
    ['bar']
    

    Then you can build the dictionary and serialize it from there.

    0 讨论(0)
  • 2020-12-01 10:16

    This is a combination of M. Rafay Aleem and Wtowers answer and caots. This is DRY and lets you only specify the extra props instead of all fields and props as in caots version.

    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
    

    How to use it:

    ExtJsonSerializer().serialize(MyModel.objects.all(), props=['property_1', ...])
    
    0 讨论(0)
  • 2020-12-01 10:20

    You can extend Django's serializers without /too/ much work. Here's a custom serializer that takes a queryset and a list of attributes (fields or not), and returns 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)
    

    Usage:

    >>> MySerializer().serialize(MyModel.objects.all(), ["field1", "property2", ...])
    

    Of course, this is probably more work than just writing your own simpler JSON serializer, but maybe not more work than your own XML serializer (you'd have to redefine "handle_field" to match the XML case in addition to changing the base class to do that).

    0 讨论(0)
  • 2020-12-01 10:23

    The solution worked well that is proposed by M. Rafay Aleem and Wtower, but it's duplicated lot of code. Here is an improvment:

    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
    

    How to use it:

    ExtJsonSerializer().serialize(MyModel.objects.all(), fields=['field_name_1', 'property_1' ...])
    
    0 讨论(0)
提交回复
热议问题