2010-04-06 12 views
15

¿Hay alguna manera de hacer pasar cualquier definición de propiedad a un serializador json al serializar una clase de modelo de Django?Serializar los métodos @property en una clase de Python

ejemplo:

class FooBar(object.Model) 

    name = models.CharField(...) 

    @property 
    def foo(self): 
     return "My name is %s" %self.name 

desea realizar una serie de:

[{ 

    'name' : 'Test User', 

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

Respuesta

0

Puede obtener todas las propiedades de una clase usando un poco de magia negro:

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

Por ejemplo:

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

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

Luego puede compilar el diccionario y serializarlo desde allí.

+0

que requeriría esencialmente la creación de mi propia hachís, sólo para serializar esencialmente a un hash. Si voy por esa ruta, casi puedo cortar toda la serialización. Tenía la esperanza de seguir utilizando la clase de modelo django y simplemente llamar serialize ('json', my_object, ...) – ashchristopher

+0

Desafortunadamente, parece que las rutinas de serialización de núcleo de Django excluyen específicamente todo lo que no está en '_meta' que Básicamente solo busca los campos del modelo db. Así que mientras puede escribir una función que solo saca campos de propiedad (lo cual se puede hacer mejor con 'inspeccionar.método getmembers' en segundo rubor), incluso el uso del parámetro 'fields' en el método' serializers.serialize' no funcionaría. Vea aquí, donde iteran sobre el queryset pasado y solo buscan cosas en '_meta': http://code.djangoproject.com/browser/django/trunk/django/core/serializers/base.py# L39 –

12

Puede ampliar los serializadores de Django sin/demasiado/mucho trabajo. Aquí hay un serializador personalizado que toma un conjunto de consulta y una lista de atributos (campos o no), y devuelve 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) 

Uso:

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

Por supuesto, esto es probablemente más trabajo que acaba de escribir su propia simple serializador JSON, pero tal vez no más trabajo que su propio serializador XML (que había que redefinir "handle_field" para unir el caso XML además de cambiar la clase base para hacer eso).

+0

Obteniendo el objeto '' MySerializer 'no tiene el atributo' first'' error en Django (1.5.4) –

5

Las cosas han cambiado un poco desde 2010, por lo que la respuesta de @ user85461 parece que ya no funciona con Django 1.8 y Python 3.4. Esta es una respuesta actualizada con lo que parece funcionar para mí.

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 

Uso:

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

Esta es la respuesta correcta y funcionó bien para mí. – Rafay

+0

publicó una versión mejorada. – caot

3

La solución funcionó bien que se propone por M. Rafay Aleem y Wtower, pero es mucho código duplicado. Aquí es una mejora a:

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 

Como Usar:

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

Esta es una combinación de M. Rafay Aleem y respuesta y Caots Wtowers. Esto es SECO y solo le permite especificar los accesorios adicionales en lugar de todos los campos y accesorios como en la versión 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 

Como Usar:

ExtJsonSerializer().serialize(MyModel.objects.all(), props=['property_1', ...]) 
Cuestiones relacionadas