Ein SQLAlchemie-Ergebnis in Flasche einstellen

Ich versuche, ein SQLAlchemy-Ergebnis in Flask / Python einzustellen.

Die Flasche Mailing-Liste schlug die folgende Methode http://librelist.com/browser//flask/2011/2/16/jsonify-sqlalchemy-pagination-collection-result/#04a0754b63387f87e59dda564bde426e :

return jsonify(json_list = qryresult) 

Allerdings bekomme ich folgende Fehlermeldung:

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

Was höre ich hier aus?

Ich habe diese Frage gefunden: Wie man SqlAlchemy Ergebnis zu JSON serialisieren? Was sehr ähnlich scheint, aber ich wusste nicht, ob Flask etwas Magie hatte, um es einfacher zu machen, als die Mailingliste nach der Post schlug.

Bearbeiten: Zur Klarstellung sieht das aus wie mein Model aus

 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 

14 Solutions collect form web for “Ein SQLAlchemie-Ergebnis in Flasche einstellen”

Es scheint, dass Sie Ihre Abfrage tatsächlich nicht ausgeführt haben. Versuchen Sie folgendes:

 return jsonify(json_list = qryresult.all()) 

[Bearbeiten] : Problem mit jsonify ist, dass normalerweise die Objekte nicht automatisch gelöscht werden können. Sogar Pythons datetime scheitert;)

Was ich in der Regel getan habe, ist, eine zusätzliche Eigenschaft (wie serialize ) zu Klassen hinzuzufügen, die serialisiert werden müssen:

 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] 

Und jetzt für Ansichten kann ich nur tun:

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

Hoffe das hilft 😉

Ich hatte das gleiche Bedürfnis, in json zu serialisieren. Werfen Sie einen Blick auf diese Frage . Es zeigt, wie man Spalten programmatisch entdeckt. Also, daraus habe ich den unten stehenden Code erstellt. Es funktioniert für mich, und ich werde es in meiner Web-App verwenden. Glückliche Kodierung!

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

Hier ist mein Ansatz:

MODELL:

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

AUSSICHT:

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

Ich bin mir darüber nicht sicher:

 self._sa_instance_state.attrs.items() 

Aber es funktioniert. Ich hatte nicht genug Zeit, um es eleganter zu machen, vielleicht wird jemand einen besseren Weg, um SA-Felder zu bekommen

Hier ist was für mich in der Regel ausreichend:

Ich schaffe ein Serialisierungs-Mixin, das ich mit meinen Modellen verwende. Die Serialisierungsfunktion holt grundsätzlich alle Attribute, die der SQLAlchemy-Inspektor aussetzt und in einen Dict setzt.

 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] 

Alles, was jetzt benötigt wird, ist, das SQLAlchemy-Modell mit der Serializer Mixin-Klasse zu erweitern.

Wenn es Felder gibt, die Sie nicht aussetzen möchten oder die spezielle Formatierung benötigen, überschreiben Sie einfach die Funktion serialize() in der Modellunterklasse.

 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 

In deinen Controllern musst du nur die serialize() Funktion (oder serialize_list(l) aufrufen, wenn die Abfrage in einer Liste resultiert) auf den Ergebnissen:

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

Ich habe dieses Problem für den besseren Teil eines Tages gesehen, und hier ist, was ich komme mit (Kredit zu https://stackoverflow.com/a/5249214/196358 für das Zeigen mich in diese Richtung).

(Anmerkung: Ich bin mit Flasche-sqlalchemy, so dass meine Modell-Deklaration Format ist ein bisschen anders als gerade sqlalchemy).

In meiner models.py Datei:

 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 

Und alle meine Modellobjekte sehen so aus:

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

In meinen Ansichten nenne ich SWJsonify, wo immer ich Jsonify genannt Jsonify , so wie:

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

Scheint gut zu arbeiten. Auch auf Beziehungen. Ich bin nicht weit damit weit gekommen, also YMMV, aber so weit fühlt es sich ziemlich gut "richtig" für mich.

Vorschläge willkommen

Für eine flache Abfrage (keine Joins) können Sie dies tun

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

Und wenn du nur bestimmte Spalten aus der Datenbank zurückgeben möchtest, kannst du das machen

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

Wenn du mit flask-restful bist, kannst du Marschall benutzen:

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

Sie müssen explizit auflisten, was Sie zurückgeben und welche Art es ist, was ich sowieso für eine APP bevorzuge. Serialisierung ist leicht versorgt (keine Notwendigkeit für jsonify ), Daten sind auch kein Problem. Beachten Sie, dass der Inhalt für das uri Feld automatisch basierend auf dem topic Endpunkt und der ID generiert wird.

Ok, ich habe seit ein paar Stunden daran gearbeitet, und ich habe entwickelt, was ich glaube, die pythonischste Lösung zu sein. Die folgenden Code-Snippets sind python3 aber sollte nicht zu schrecklich schmerzhaft zu backport, wenn Sie brauchen.

Das erste, was wir tun werden, ist mit einem Mixin zu beginnen, der deine db-Modelle so gut macht wie dict s:

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

Jetzt werden wir unser Modell definieren und das mixin erben:

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

Das ist alles, was es braucht, um eine Instanz von MyModel() zu dict() bekommen und eine echte Live- dict Instanz aus ihm herauszuholen, was uns einen ziemlich langen Weg dorthin jsonify() um es zu machen. Als nächstes müssen wir JSONEncoder verlängern, um uns den Rest des Weges zu bekommen:

 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 

Bonuspunkte: Wenn Ihr Modell berechnete Felder enthält (das heißt, Sie möchten, dass Ihre JSON-Ausgabe Felder enthält, die noch nicht in der Datenbank gespeichert sind), ist das auch einfach. Definieren Sie einfach Ihre berechneten Felder als @property s und erweitern Sie die keys() Methode wie @property :

 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'] 

Jetzt ist es trivial, um zu sehen:

 @app.route('/whatever', methods=['GET']) def whatever(): return jsonify(dict(results=MyModel.query.all())) 

Ich arbeitete mit einer SQL-Abfrage defaultdict von Listen von RowProxy-Objekten namens jobDict Es dauerte eine Weile, um herauszufinden, welche Art die Objekte waren.

Dies war eine wirklich einfache schnelle Möglichkeit, um einige saubere jsonEncoding nur durch Typcasting der Zeile auf eine Liste zu lösen und zunächst die Definition der Dict mit einem Wert der Liste.

  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) 

Hier ist eine Möglichkeit, eine as_dict () – Methode für jede Klasse hinzuzufügen, sowie jede andere Methode, die du auf jeder einzelnen Klasse haben willst. Nicht sicher, ob dies der gewünschte Weg ist oder nicht, aber es funktioniert …

 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) 

Ich möchte nur meine Methode hinzufügen, um dies zu tun.

Definiere einfach einen custome json Encoder, um deine db Modelle zu serilisieren.

 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 

Dann in deiner Ansicht Funktion

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

Es funktioniert gut, obwohl die Eltern haben Beziehungen

Ich war auf der Suche nach etwas wie die Schienen-Ansatz in ActiveRecord verwendet, um_json und implementiert etwas ähnliches mit diesem Mixin nach unbefriedigt mit anderen Anregungen. Es behandelt verschachtelte Modelle und schließt oder schließt Attribute der obersten Ebene oder verschachtelten Modelle ein.

 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 

Dann, um die BaseQuery serialisierbar zu erweitern, erweiterte ich BaseQuery

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

Für folgende Modelle

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

Du könntest so etwas machen

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

Hier ist meine Antwort, wenn Sie die deklarative Basis verwenden (mit Hilfe von einigen der bereits gebuchten Antworten):

 # 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, eg 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) 

Es ist schon eine Menge Zeit und es gibt viele gültige Antworten, aber der folgende Codeblock scheint zu funktionieren:

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

Ich bin mir bewusst, dass dies keine perfekte Lösung ist, noch so elegant wie die anderen, aber für diejenigen, die o schnell reparieren wollen, könnten sie das versuchen.

  • Sqlalchemy Verbindung zur VPS Datenbank
  • Alembic und immer den letzten eingefügten Wert
  • Kann SQLAlchemy nicht blockiert werden?
  • Konvertieren von String zum Float SQLAlchemy
  • Nicht funktionierendes Beispiel mit "passiv-deletes" -Richtlinie in sqlalchemy doc
  • Wie kann man wörtlich basierte Abfrage in sqlalchemy erstellen?
  • Serve-Bild in SQLAlchemy LargeBinary Spalte gespeichert
  • Bedingte Filterung in SQLAlchemy
  • SQLAlchemy eins zu viele Beziehungsmöglichkeiten, wie man die Sammlung filtert
  • Kann nicht fangen SQLAlchemy IntegrityError
  • Fehler beim Ausführen von imdbpy2sql.py mit MySQL-Datenbank
  • Python ist die beste Programmiersprache der Welt.