2010-04-06 5 views
15

Existe-t-il un moyen de faire passer des définitions @property à un sérialiseur json lors de la sérialisation d'une classe de modèle Django?Sérialiser les méthodes @property dans une classe Python

exemple:

class FooBar(object.Model) 

    name = models.CharField(...) 

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

Vous voulez sérialiser:

[{ 

    'name' : 'Test User', 

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

Répondre

0

Vous pouvez obtenir toutes les propriétés d'une classe en utilisant la magie noire:

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

Par exemple:

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

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

Ensuite, vous pouvez créer le dictionnaire et le sérialiser à partir de là.

+0

Cela nécessiterait la création essentiellement mon propre hachage, pour sérialiser essentiellement un hachage. Si je fais cette route, je peux presque découper toute la sérialisation. J'espérais continuer à utiliser la classe Model django et appeler simplement sérialiser ('json', mon_objet, ...) – ashchristopher

+0

Malheureusement, il semble que les routines de sérialisation de base de Django excluent spécifiquement tout ce qui n'est pas dans '_meta' fondamentalement seulement cherche des champs de modèle de DB. Donc, alors que vous pouvez écrire une fonction qui extrait uniquement les champs de propriété (ce qui peut être mieux fait avec le 'inspecter.La méthode getmembers à la deuxième fade), même en utilisant le paramètre 'fields' de la méthode' serializers.serialize' ne fonctionnerait pas. Voir ici, où ils parcourent le jeu de requête et ne recherchent que du contenu dans '_meta': http://code.djangoproject.com/browser/django/trunk/django/core/serializers/base.py# L39 –

12

Vous pouvez étendre les sérialiseurs de Django sans/trop/beaucoup de travail. Voici un sérialiseur personnalisé qui prend un jeu de requête et une liste d'attributs (champs ou non), et renvoie 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) 

Utilisation:

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

Bien sûr, cela est probablement plus de travail que simplement écrire votre propre sérialiseur JSON plus simple, mais peut-être pas plus de travail que votre propre sérialiseur XML (il vous faudrait redéfinir "handle_field" pour faire correspondre le cas XML en plus de changer la classe de base pour le faire).

+0

Obtenir l'objet 'MySerializer' n'a pas d'attribut 'first'' erreur sur Django (1.5.4) –

5

Les choses ont un peu changé depuis 2010, donc la réponse de @ user85461 ne semble plus fonctionner avec Django 1.8 et Python 3.4. Ceci est une réponse mise à jour avec ce qui semble fonctionner pour moi.

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 

Utilisation:

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

Ceci est la bonne réponse et a bien fonctionné pour moi. – Rafay

+0

posté une version améliorée. – caot

3

La solution a bien fonctionné qui est proposé par M. Rafay Aleem et Wtower, mais il est beaucoup dupliqués de code. Voici un 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 

Comment l'utiliser:

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

C'est une combinaison de M. Rafay Aleem et Wtowers réponse et caots. Ceci est DRY et vous permet de spécifier uniquement les accessoires supplémentaires au lieu de tous les champs et les accessoires comme dans la version 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 

Comment l'utiliser:

ExtJsonSerializer().serialize(MyModel.objects.all(), props=['property_1', ...]) 
Questions connexes