2010-04-06 4 views
15

Gibt es eine Möglichkeit, @property-Definitionen bei der Serialisierung einer Django-Modellklasse an einen JSON-Serialisierer zu übergeben?Serialisieren Sie die @property-Methoden in einer Python-Klasse

Beispiel:

class FooBar(object.Model) 

    name = models.CharField(...) 

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

Möchten Sie serialisiert:

[{ 

    'name' : 'Test User', 

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

Antwort

0

Sie können alle Eigenschaften einer Klasse bekommen einige schwarze Magie:

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

Zum Beispiel:

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

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

Dann können Sie das Wörterbuch erstellen und von dort aus serialisieren.

+0

, dass meine eigene Hash im Wesentlichen zu schaffen, nur erfordern würde im Wesentlichen ein Hash-Serialisierung zu. Wenn ich diesen Weg gehe, kann ich fast die gesamte Serialisierung ausschneiden. Ich hoffte, weiterhin die Django Model-Klasse zu verwenden und rufen Sie einfach serialize ('json', my_object, ...) – ashchristopher

+0

Leider scheint es, dass Django Kern Serialisierungsroutinen ausdrücklich alles ausschließen, die nicht in '_meta' ist sucht grundsätzlich nur nach db-Modellfeldern. So, während Sie eine Funktion schreiben können, die nur Eigenschaftsfelder herauszieht (was besser mit dem 'inspect.getmembers 'Methode bei Second Blush), selbst mit dem' fields' Parameter der 'serializers.serialize' Methode würde nicht funktionieren. Sehen Sie hier, wo sie über das übergebene Queryset iterieren und nur nach Dingen in '_meta' suchen: http://code.djangoproject.com/browser/django/trunk/django/core/serializers/base.py# L39 –

12

Sie können Djangos Serializer ohne/zu viel Arbeit erweitern. Hier ist ein benutzerdefinierter Serializer, der ein Abfrage-Set und eine Liste von Attributen (Felder oder nicht) akzeptiert und JSON zurückgibt.

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) 

Verbrauch:

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

Natürlich ist dies wahrscheinlich mehr Arbeit als nur Ihren eigenen einfachen JSON Serializer zu schreiben, aber vielleicht nicht mehr Arbeit, als Sie Ihren eigener XML Serializer (man müßte neu zu definieren "handle_field", um den XML-Fall zusätzlich zur Änderung der Basisklasse anzupassen, um dies zu tun.

+0

Das ''MySerializer' 'Objekt hat keinen Attribut' first'' Fehler auf Django (1.5.4) –

5

Die Dinge haben sich seit 2010 ein wenig verändert, daher scheint die Antwort von @ user85461 nicht mehr mit Django 1.8 und Python 3.4 zu funktionieren. Dies ist eine aktualisierte Antwort mit dem, was für mich zu funktionieren scheint.

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 

Verbrauch:

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

Das ist die richtige Antwort und hat bei mir gut funktioniert. – Rafay

+0

hat eine verbesserte Version veröffentlicht. – caot

3

Die Lösung funktionierte gut, dass durch M. Rafay Aleem und Wtower vorgeschlagen wird, aber es ist viel Code dupliziert. Hier ist ein 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 

Wie man es verwendet:

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

Dies ist eine Kombination von M. Rafay Aleem und Wtowers Antwort und caots. Dies ist DRY und lässt Sie nur die zusätzlichen Requisiten anstelle aller Felder und Requisiten wie in der Version von caots angeben.

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 

Wie man es verwendet:

ExtJsonSerializer().serialize(MyModel.objects.all(), props=['property_1', ...]) 
Verwandte Themen