jsonify výsledek SQLAlchemy nastavit v baňce

hlasů
86

Snažím se jsonify výsledek SQLAlchemy nastavit v baňce / Python.

Mailing list Flask navrhl následující metody http://librelist.com/browser//flask/2011/2/16/jsonify-sqlalchemy-pagination-collection-result/#04a0754b63387f87e59dda564bde426e :

return jsonify(json_list = qryresult)

Nicméně Začínám následující chybě zpět:

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

Co jsem s výhledem tady?

Zjistil jsem, že na tuto otázku: Jak serializovat výsledek SqlAlchemy do JSON? což se zdá být velmi podobná ale nevěděl jsem, jestli Baňka měl nějaké kouzlo, aby bylo jednodušší, protože seznam adres po navrhl.

Edit: Pro vyjasnění, to je to, co můj model vypadá

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
Položena 18/08/2011 v 06:24
zdroj uživatelem
V jiných jazycích...                            


15 odpovědí

hlasů
125

Zdá se, že jste ve skutečnosti nebyly provedeny dotazu. Zkuste následující kroky:

return jsonify(json_list = qryresult.all())

[Edit] : Problém s jsonify je, že obvykle tyto objekty nelze jsonified automaticky. Dokonce Python datetime selže)

To, co jsem obvykle provádí, je přidat další vlastnosti (jako serialize) se třídami, které je třeba serializovat:

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]

A teď pro zobrazení můžu prostě:

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

Snad to pomůže ;)

Odpovězeno 18/08/2011 v 07:52
zdroj uživatelem

hlasů
30

Měl jsem stejnou potřebu, aby serializovat do JSON. Podívejte se na tuto otázku . To ukazuje, jak objevovat sloupce programově. Tak z toho jsem vytvořil kód níže. Funguje to pro mě, a já budu používat jej v mé webové aplikace. Šťastné kódování!


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__)
Odpovězeno 17/03/2012 v 01:42
zdroj uživatelem

hlasů
3

Byl jsem při pohledu na tento problém pro větší část dne, a tady je to, co jsem přišel s (úvěr na https://stackoverflow.com/a/5249214/196358 mě ukázal v tomto směru).

(Poznámka: Já používám baňky-sqlalchemy, takže moje formát Model prohlášení je trochu odlišný od přímé sqlalchemy).

V mém models.pysouboru:

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

a všichni moji objekty modelu vypadat následovně:

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

V mé názory říkám SWJsonify kam bych zavolal Jsonify, tak jako:

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

Zdá se, že fungují docela dobře. Dokonce i na vztazích. Jsem se nedostal daleko s ním, takže YMMV, ale tak daleko, že se cítí dost „právo“ na mě.

Návrhy vítány.

Odpovězeno 06/07/2012 v 02:09
zdroj uživatelem

hlasů
15

Tady je můj přístup:

MODELKA:

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))
    ...

POHLED:

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())

Nejsem si jistý, o tom:

self._sa_instance_state.attrs.items()

ale funguje to. Měl jsem dost na to čas, aby se to více elegantní, možná někdo navrhnout lepší způsob, jak dostat SA polí

Odpovězeno 14/01/2013 v 17:20
zdroj uživatelem

hlasů
4

Pro pevnou dotazu (bez spojení), můžete to udělat

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

i když jen chcete vrátit určité sloupce z databáze, kterou můžete udělat

@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)
Odpovězeno 03/05/2013 v 22:24
zdroj uživatelem

hlasů
0

Pracoval jsem s SQL dotazu defaultdict seznamů objektů RowProxy pojmenovaných jobDict Chvíli mi trvalo, než se přijít na to, jaký typ objekty byly.

Jednalo se o velmi jednoduché rychlý způsob, jak vyřešit nějaké čisté jsonEncoding pouhým typecasting řádek do seznamu a tím nejprve definovat dict s hodnotou seznamu.

    jobDict = defaultdict(list)
    def set_default(obj):
        # trickyness needed here via import to know type
        if isinstance(obj, RowProxy):
            return list(obj)
        raise TypeError


    jsonEncoded = json.dumps(jobDict, default=set_default)
Odpovězeno 09/05/2014 v 16:52
zdroj uživatelem

hlasů
-1

Je to už mnohokrát a existuje spousta platných odpovědí, ale následující blok kódu zdá se k práci:

my_object = SqlAlchemyModel()
my_serializable_obj = my_object.__dict__
del my_serializable_obj["_sa_instance_state"]
print(jsonify(my_serializable_object))

Jsem si vědom, že to není ideální řešení, ani tak elegantní jako ostatní, ale pro ty, kteří chtějí o rychlé řešení, mohli by to zkusit.

Odpovězeno 22/10/2014 v 14:03
zdroj uživatelem

hlasů
21

Tady je to, co je obvykle dostačující pro mě:

I vytvořit serializace mixin který používám s mými modely. Funkce serializace v podstatě načte bez ohledu na atributy SQLAlchemy inspektor odhaluje a staví ji do dict.

from sqlalchemy.inspection import inspect

class Serializer(object):

    def serialize(self):
        return {c: getattr(self, c) for c in inspect(self).attrs.keys()}

    @staticmethod
    def serialize_list(l):
        return [m.serialize() for m in l]

Vše, co nyní potřebujeme, je rozšířit model SQLAlchemy s Serializermixin třídě.

Pokud existují polí nechcete vystavit, nebo kteří potřebují speciální formátování, jednoduše přepsat serialize()funkci v modelu podtřídy.

class User(db.Model, Serializer):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String)
    password = db.Column(db.String)

    # ...

    def serialize(self):
        d = Serializer.serialize(self)
        del d['password']
        return d

V řadičích, vše, co musíte udělat, je zavolat serialize()funkci (nebo serialize_list(l)pokud výsledky dotazu v seznamu) na základě výsledků:

def get_user(id):
    user = User.query.get(id)
    return json.dumps(user.serialize())

def get_users():
    users = User.query.all()
    return json.dumps(User.serialize_list(users))
Odpovězeno 14/01/2015 v 20:16
zdroj uživatelem

hlasů
0

Zde je způsob, jak přidat metodu as_dict () v každé třídě, stejně jako jakoukoli jinou metodu, kterou chcete mít na každé třídě. Není si jistý, jestli to je požadovaným způsobem, nebo ne, ale funguje to ...

class Base(object):
    def as_dict(self):
        return dict((c.name,
                     getattr(self, c.name))
                     for c in self.__table__.columns)


Base = declarative_base(cls=Base)
Odpovězeno 05/05/2015 v 00:22
zdroj uživatelem

hlasů
2

Ok, jsem pracoval na to po dobu několika hodin, a já jsem vytvořil, co považuji za nejvíce pythonic řešení dosud. Následující úryvky kódu jsou python3, ale neměla by být příliš hrozně bolestné portovat, pokud budete potřebovat.

První věc, kterou budeme dělat, je začít s mixin, která dělá vaše db modely působí trochu jako dicts:

from sqlalchemy.inspection import inspect

class ModelMixin:
    """Provide dict-like interface to db.Model subclasses."""

    def __getitem__(self, key):
        """Expose object attributes like dict values."""
        return getattr(self, key)

    def keys(self):
        """Identify what db columns we have."""
        return inspect(self).attrs.keys()

Nyní budeme definovat náš model, zdědí mixin:

class MyModel(db.Model, ModelMixin):
    id = db.Column(db.Integer, primary_key=True)
    foo = db.Column(...)
    bar = db.Column(...)
    # etc ...

To je vše, co je zapotřebí, aby bylo možné předat instanci MyModel()na dict()a získat skutečné živé dictinstance z toho, který nás dostane docela dlouhou cestu k tomu, aby jsonify()pochopili to. Dále je třeba rozšířit JSONEncoder, aby nám ten zbytek cesty:

from flask.json import JSONEncoder
from contextlib import suppress

class MyJSONEncoder(JSONEncoder):
    def default(self, obj):
        # Optional: convert datetime objects to ISO format
        with suppress(AttributeError):
            return obj.isoformat()
        return dict(obj)

app.json_encoder = MyJSONEncoder

Bonusové body: Pokud váš model obsahuje vypočtené pole (to znamená, že chcete, aby vaše JSON výstup obsahovat pole, které nejsou ve skutečnosti uložené v databázi), to je také snadné. Jen definovat vypočítané pole jako @propertys, a rozšířit keys()metody jako tak:

class MyModel(db.Model, ModelMixin):
    id = db.Column(db.Integer, primary_key=True)
    foo = db.Column(...)
    bar = db.Column(...)

    @property
    def computed_field(self):
        return 'this value did not come from the db'

    def keys(self):
        return super().keys() + ['computed_field']

Teď je to triviální jsonify:

@app.route('/whatever', methods=['GET'])
def whatever():
    return jsonify(dict(results=MyModel.query.all()))
Odpovězeno 30/09/2015 v 09:43
zdroj uživatelem

hlasů
4

Pokud používáte flask-restful, můžete použít maršála :

from flask.ext.restful import Resource, fields, marshal

topic_fields = {
    'title':   fields.String,
    'content': fields.String,
    'uri':     fields.Url('topic'),
    'creator': fields.String,
    'created': fields.DateTime(dt_format='rfc822')
}

class TopicListApi(Resource):
    def get(self):
        return {'topics': [marshal(topic, topic_fields) for topic in DbTopic.query.all()]}

Je potřeba výslovně uvést, co se vracejí a jaký typ je to, které jsem raději tak jako tak pro API. Serializace snadno postaráno (bez nutnosti jsonify), data jsou také není problém. Všimněte si, že obsah pro uripole je automaticky generovány na základě topickoncového bodu a id.

Odpovězeno 26/12/2015 v 10:11
zdroj uživatelem

hlasů
0

Jen chci přidat můj způsob, jak toho dosáhnout.

Jen definovat custome JSON kodér serilize své db modelů.

class ParentEncoder(json.JSONEncoder):
    def default(self, obj):
        # convert object to a dict
        d = {}
        if isinstance(obj, Parent):
            return {"id": obj.id, "name": obj.name, 'children': list(obj.child)}
        if isinstance(obj, Child):
            return {"id": obj.id, "name": obj.name}

        d.update(obj.__dict__)
        return d

pak se podle vašeho názoru funkci

parents = Parent.query.all()
dat = json.dumps({"data": parents}, cls=ParentEncoder)
resp = Response(response=dat, status=200, mimetype="application/json")
return (resp)

to funguje dobře i když rodič má vztahy

Odpovězeno 02/06/2016 v 07:44
zdroj uživatelem

hlasů
1

Hledal jsem něco jako kolejnice přístupem používaným v ActiveRecord to_json a realizovány něco podobného pomocí tohoto mixin poté, co byl nespokojený s dalšími návrhy. To se zabývá vnořené modely, a zahrnutí nebo vyloučení atributů na nejvyšší úrovni nebo vnořených modelů.

class Serializer(object):

    def serialize(self, include={}, exclude=[], only=[]):
        serialized = {}
        for key in inspect(self).attrs.keys():
            to_be_serialized = True
            value = getattr(self, key)
            if key in exclude or (only and key not in only):
                to_be_serialized = False
            elif isinstance(value, BaseQuery):
                to_be_serialized = False
                if key in include:
                    to_be_serialized = True
                    nested_params = include.get(key, {})
                    value = [i.serialize(**nested_params) for i in value]

            if to_be_serialized:
                serialized[key] = value

        return serialized

Poté se dostat SERIALIZABLE BaseQuery I rozšířené BaseQuery

class SerializableBaseQuery(BaseQuery):

    def serialize(self, include={}, exclude=[], only=[]):
        return [m.serialize(include, exclude, only) for m in self]

Pro následující modely

class ContactInfo(db.Model, Serializer):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    full_name = db.Column(db.String())
    source = db.Column(db.String())
    source_id = db.Column(db.String())

    email_addresses = db.relationship('EmailAddress', backref='contact_info', lazy='dynamic')
    phone_numbers = db.relationship('PhoneNumber', backref='contact_info', lazy='dynamic')


class EmailAddress(db.Model, Serializer):
    id = db.Column(db.Integer, primary_key=True)
    email_address = db.Column(db.String())
    type = db.Column(db.String())
    contact_info_id = db.Column(db.Integer, db.ForeignKey('contact_info.id'))


class PhoneNumber(db.Model, Serializer):
    id = db.Column(db.Integer, primary_key=True)
    phone_number = db.Column(db.String())
    type = db.Column(db.String())
    contact_info_id = db.Column(db.Integer, db.ForeignKey('contact_info.id'))

    phone_numbers = db.relationship('Invite', backref='phone_number', lazy='dynamic')

Dalo by se udělat něco podobného

@app.route("/contact/search", methods=['GET'])
def contact_search():
    contact_name = request.args.get("name")
    matching_contacts = ContactInfo.query.filter(ContactInfo.full_name.like("%{}%".format(contact_name)))

    serialized_contact_info = matching_contacts.serialize(
        include={
            "phone_numbers" : {
                "exclude" : ["contact_info", "contact_info_id"]
            },
            "email_addresses" : {
                "exclude" : ["contact_info", "contact_info_id"]
            }
        }
    )

    return jsonify(serialized_contact_info)
Odpovězeno 17/07/2016 v 05:57
zdroj uživatelem

hlasů
0

Tady je moje odpověď, pokud používáte deklarativní základnu (s nápovědou od některé z odpovědí již vyslaných):

# in your models definition where you define and extend declarative_base()
from sqlalchemy.ext.declarative import declarative_base
...
Base = declarative_base()
Base.query = db_session.query_property()
...

# define a new class (call "Model" or whatever) with an as_dict() method defined
class Model():
    def as_dict(self):
        return { c.name: getattr(self, c.name) for c in self.__table__.columns }

# and extend both the Base and Model class in your model definition, e.g.
class Rating(Base, 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)
    ...

# then after you query and have a resultset (rs) of ratings
rs = Rating.query.all()

# you can jsonify it with
s = json.dumps([r.as_dict() for r in rs], default=alchemyencoder)
print (s)

# or if you have a single row
r = Rating.query.first()

# you can jsonify it with
s = json.dumps(r.as_dict(), default=alchemyencoder)

# you will need this alchemyencoder where your are calling json.dumps to handle datetime and decimal format
# credit to Joonas @ http://codeandlife.com/2014/12/07/sqlalchemy-results-to-json-the-easy-way/
def alchemyencoder(obj):
    """JSON encoder function for SQLAlchemy special classes."""
    if isinstance(obj, datetime.date):
        return obj.isoformat()
    elif isinstance(obj, decimal.Decimal):
        return float(obj)
Odpovězeno 29/08/2016 v 23:12
zdroj uživatelem

hlasů
1

Flask-Restful 0.3.6 Request analýze doporučit marshmallow

marshmallow je ORM / ODM / rámec agnostik knihovna pro konverzi složité datové typy, jako jsou objekty, do a z nativních Python datové typy.

Jednoduchý marshmallow příklad ukazuje níže.

from marshmallow import Schema, fields

class UserSchema(Schema):
    name = fields.Str()
    email = fields.Email()
    created_at = fields.DateTime()

from marshmallow import pprint

user = User(name="Monty", email="monty@python.org")
schema = UserSchema()
result = schema.dump(user)
pprint(result)
# {"name": "Monty",
#  "email": "monty@python.org",
#  "created_at": "2014-08-17T14:54:16.049594+00:00"}

Základní funkce obsahovat

Deklarovat Schémata
Serializace objektů ( „dumpingu“)
Deserializing Objects ( „Nahrávání“)
Zacházení Kolekce objektů
Validation
Určení názvy atributů
, které specifikují serializace / deserializace Keys
Refaktoring Implicitní Field Tvorba
Řazení Output
„jen pro čtení“ a „Write-only“ Fields
Určení výchozích serializace / deserializace Hodnoty
Hnízdění schémata
Custom Fields

Odpovězeno 22/10/2018 v 03:39
zdroj uživatelem

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more