141 votes

jsonify un SQLAlchemy dans l'ensemble des résultats Flacon

Je suis en train de jsonify un SQLAlchemy dans l'ensemble des résultats Flacon/Python.

Le Flacon liste de diffusion suggère la méthode suivante http://librelist.com/browser//flask/2011/2/16/jsonify-sqlalchemy-pagination-collection-result/#04a0754b63387f87e59dda564bde426e :

return jsonify(json_list = qryresult)

Cependant j'obtiens l'erreur suivante dos:

TypeError: <flaskext.sqlalchemy.BaseQuery object at 0x102c2df90> 
is not JSON serializable

Que suis-je surplombant ici?

J'ai trouvé cette question: Comment sérialiser SqlAlchemy résultat JSON? ce qui semble très similaire, cependant je ne sais pas si le Flacon a un peu de magie pour le rendre plus facile la liste de diffusion de poste proposé.

Edit: pour clarifier, c'est ce que mon modèle ressemble

class Rating(db.Model):

    __tablename__ = 'rating'

    id = db.Column(db.Integer, primary_key=True)
    fullurl = db.Column(db.String())
    url = db.Column(db.String())
    comments = db.Column(db.Text)
    overall = db.Column(db.Integer)
    shipping = db.Column(db.Integer)
    cost = db.Column(db.Integer)
    honesty = db.Column(db.Integer)
    communication = db.Column(db.Integer)
    name = db.Column(db.String())
    ipaddr = db.Column(db.String())
    date = db.Column(db.String())

    def __init__(self, fullurl, url, comments, overall, shipping, cost, honesty, communication, name, ipaddr, date):
        self.fullurl = fullurl
        self.url = url
        self.comments = comments
        self.overall = overall
        self.shipping = shipping
        self.cost = cost
        self.honesty = honesty
        self.communication = communication
        self.name = name
        self.ipaddr = ipaddr
        self.date = date

185voto

plaes Points 8535

Il semble que vous n'avez pas exécuté votre requête. Essayez suivantes:

return jsonify(json_list = qryresult.all())

[Edit]: Problème avec jsonify est, qui, généralement, les objets ne peuvent pas être jsonified automatiquement. Même Python datetime ne fonctionne pas ;)

Ce que j'ai fait habituellement, est d'ajouter une propriété (comme serialize) pour les classes qui ont besoin d'être activé:

def dump_datetime(value):
    """Deserialize datetime object into string form for JSON processing."""
    if value is None:
        return None
    return [value.strftime("%Y-%m-%d"), value.strftime("%H:%M:%S")]

class Foo(db.Model):
    # ... SQLAlchemy defs here..
    def __init__(self, ...):
       # self.foo = ...
       pass

    @property
    def serialize(self):
       """Return object data in easily serializeable format"""
       return {
           'id'         : self.id,
           'modified_at': dump_datetime(self.modified_at),
           # This is an example how to deal with Many2Many relations
           'many2many'  : self.serialize_many2many
       }
    @property
    def serialize_many2many(self):
       """
       Return object's relations in easily serializeable format.
       NB! Calls many2many's serialize property.
       """
       return [ item.serialize for item in self.many2many]

Et maintenant pour les vues que je peux le faire:

return jsonify(json_list=[i.serialize for i in qryresult.all()])

Espérons que cette aide ;)

38voto

bitcycle Points 2087

J'ai eu le même besoin, pour sérialiser en json. Jetez un oeil à cette question. Il montre comment découvrir des colonnes de la programmation. Alors, depuis que j'ai créé le code ci-dessous. Il fonctionne pour moi, et je vais l'utiliser dans mon application web. Amusez-vous bien!


def to_json(inst, cls):
    """
    Jsonify the sql alchemy query result.
    """
    convert = dict()
    # add your coversions for things like datetime's 
    # and what-not that aren't serializable.
    d = dict()
    for c in cls.__table__.columns:
        v = getattr(inst, c.name)
        if c.type in convert.keys() and v is not None:
            try:
                d[c.name] = convert[c.type](v)
            except:
                d[c.name] = "Error:  Failed to covert using ", str(convert[c.type])
        elif v is None:
            d[c.name] = str()
        else:
            d[c.name] = v
    return json.dumps(d)

class Person(base):
    __tablename__ = 'person'
    id = Column(Integer, Sequence('person_id_seq'), primary_key=True)
    first_name = Column(Text)
    last_name = Column(Text)
    email = Column(Text)

    @property
    def json(self):
        return to_json(self, self.__class__)

21voto

n0nSmoker Points 326

Voici ma démarche:

MODÈLE:

class AutoSerialize(object):
    'Mixin for retrieving public fields of model in json-compatible format'
    __public__ = None

    def get_public(self, exclude=(), extra=()):
        "Returns model's PUBLIC data for jsonify"
        data = {}
        keys = self._sa_instance_state.attrs.items()
        public = self.__public__ + extra if self.__public__ else extra
        for k, field in  keys:
            if public and k not in public: continue
            if k in exclude: continue
            value = self._serialize(field.value)
            if value:
                data[k] = value
        return data

    @classmethod
    def _serialize(cls, value, follow_fk=False):
        if type(value) in (datetime, date):
            ret = value.isoformat()
        elif hasattr(value, '__iter__'):
            ret = []
            for v in value:
                ret.append(cls._serialize(v))
        elif AutoSerialize in value.__class__.__bases__:
            ret = value.get_public()
        else:
            ret = value

        return ret

class User(db.Model, AutoSerialize):
    __tablename__ = 'users'
    __public__ = ('id', 'name', 'email')
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode(50))
    email = db.Column(db.String(120), unique=True)
    passhash = db.Column(db.String(100))
    ...

VUE:

from flask import jsonfy

@mod.route('/<int:id>/', methods=['GET'])
def get_user_by_id(id):
    u = User.query.get(id)
    return jsonify(u.get_public())

Je ne suis pas sûr à ce sujet:

self._sa_instance_state.attrs.items()

mais il fonctionne. Je n'avais pas assez de temps pour le rendre plus élégant, peut-être que quelqu'un va proposer un meilleur moyen d'obtenir SA les champs

7voto

reubano Points 369

Pour un plat de requête (pas de joint) vous pouvez le faire

@app.route('/results/')
def results():
    data = Table.query.all()
    result = [d.__dict__ for d in data]
    return jsonify(result=result)

et si vous ne voulez retourner certaines colonnes de la base de données, vous pouvez le faire

@app.route('/results/')
def results():
    cols = ['id', 'url', 'shipping']
    data = Table.query.all()
    result = [{col: getattr(d, col) for col in cols} for d in data]
    return jsonify(result=result)

2voto

Kenny Winker Points 6171

J'ai été à la recherche à ce problème pour la meilleure partie de la journée, et voici ce que j'ai trouvé (crédit à http://stackoverflow.com/a/5249214/196358 pour me pointer dans cette direction).

(Note: je suis en utilisant flacon-sqlalchemy, donc mon modèle de déclaration format est un peu différent de tout droit de sqlalchemy).

Dans mon models.py le fichier:

import json

class Serializer(object):
  __public__ = None
  "Must be implemented by implementors"

  def to_serializable_dict(self):
    dict = {}
    for public_key in self.__public__:
      value = getattr(self, public_key)
      if value:
        dict[public_key] = value
    return dict

class SWEncoder(json.JSONEncoder):
  def default(self, obj):
    if isinstance(obj, Serializer):
      return obj.to_serializable_dict()
    if isinstance(obj, (datetime)):
      return obj.isoformat()
    return json.JSONEncoder.default(self, obj)


def SWJsonify(*args, **kwargs):
  return current_app.response_class(json.dumps(dict(*args, **kwargs), cls=SWEncoder, indent=None if request.is_xhr else 2), mimetype='application/json')
  # stolen from https://github.com/mitsuhiko/flask/blob/master/flask/helpers.py

et tous mes objets de modèle ressembler à ceci:

class User(db.Model, Serializer):
  __public__ = ['id','username']
  ... field definitions ...

De mon point de vue j'appelle SWJsonify où je l'aurais appelé Jsonify, comme suit:

@app.route('/posts')
def posts():
  posts = Post.query.limit(PER_PAGE).all()
  return SWJsonify({'posts':posts })

Semble fonctionner assez bien. Même sur les relations. Je n'ai pas obtenu loin avec elle, de sorte YMMV, mais jusqu'à présent, il se sent assez "droit" pour moi.

Suggestions de bienvenue.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X