SqlAlchemy sonucu JSON'a nasıl serileştirilir?


192

Django, DB'den JSON formatına döndürülen ORM modellerinin iyi bir otomatik serileştirmesine sahiptir.

SQLAlchemy sorgu sonucu JSON biçimine nasıl serileştirilir?

Denedim jsonpickle.encodeama sorgu nesnesinin kendisini kodlar. Denedim json.dumps(items)ama geri döndü

TypeError: <Product('3', 'some name', 'some desc')> is not JSON serializable

SQLAlchemy ORM nesnelerini JSON / XML olarak serileştirmek gerçekten zor mu? Bunun için herhangi bir varsayılan serileştirici yok mu? Günümüzde ORM sorgu sonuçlarını serileştirmek çok yaygın bir iştir.

İhtiyacım olan sadece SQLAlchemy sorgu sonucunun JSON veya XML veri gösterimini döndürmektir.

Javascript datagird'de (JQGrid http://www.trirand.com/blog/ ) kullanılabilmesi için JSA / XML biçimindeki SQLAlchemy nesneleri sorgu sonucu gereklidir


Bu benim için çalışan bir çözüm. bağlantı açıklamasını buraya girin
octaedro

Yanıtlar:


129

Düz bir uygulama

Bunun gibi bir şey kullanabilirsiniz:

from sqlalchemy.ext.declarative import DeclarativeMeta

class AlchemyEncoder(json.JSONEncoder):

    def default(self, obj):
        if isinstance(obj.__class__, DeclarativeMeta):
            # an SQLAlchemy class
            fields = {}
            for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']:
                data = obj.__getattribute__(field)
                try:
                    json.dumps(data) # this will fail on non-encodable values, like other classes
                    fields[field] = data
                except TypeError:
                    fields[field] = None
            # a json-encodable dict
            return fields

        return json.JSONEncoder.default(self, obj)

ve daha sonra kullanarak JSON'a dönüştürün:

c = YourAlchemyClass()
print json.dumps(c, cls=AlchemyEncoder)

Kodlanamayan alanları yoksayar (onları 'Yok' olarak ayarlayın).

İlişkileri otomatik olarak genişletmez (çünkü bu, kendi referanslarına yol açabilir ve sonsuza dek döngüye girebilir).

Yinelemeli, dairesel olmayan bir uygulama

Ancak, sonsuza kadar döngü yapmayı tercih ederseniz, şunları kullanabilirsiniz:

from sqlalchemy.ext.declarative import DeclarativeMeta

def new_alchemy_encoder():
    _visited_objs = []

    class AlchemyEncoder(json.JSONEncoder):
        def default(self, obj):
            if isinstance(obj.__class__, DeclarativeMeta):
                # don't re-visit self
                if obj in _visited_objs:
                    return None
                _visited_objs.append(obj)

                # an SQLAlchemy class
                fields = {}
                for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']:
                    fields[field] = obj.__getattribute__(field)
                # a json-encodable dict
                return fields

            return json.JSONEncoder.default(self, obj)

    return AlchemyEncoder

Ve sonra nesneleri kullanarak kodlayın:

print json.dumps(e, cls=new_alchemy_encoder(), check_circular=False)

Bu, tüm çocukları, tüm çocuklarını ve tüm çocuklarını kodlar ... Temelde, tüm veritabanınızı potansiyel olarak kodlar. Daha önce kodlanmış bir şeye ulaştığında, onu 'Hiçbiri' olarak kodlayacaktır.

Özyinelemeli, muhtemelen dairesel, seçici bir uygulama

Muhtemelen daha iyi olan başka bir alternatif, genişletmek istediğiniz alanları belirleyebilmektir:

def new_alchemy_encoder(revisit_self = False, fields_to_expand = []):
    _visited_objs = []

    class AlchemyEncoder(json.JSONEncoder):
        def default(self, obj):
            if isinstance(obj.__class__, DeclarativeMeta):
                # don't re-visit self
                if revisit_self:
                    if obj in _visited_objs:
                        return None
                    _visited_objs.append(obj)

                # go through each field in this SQLalchemy class
                fields = {}
                for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']:
                    val = obj.__getattribute__(field)

                    # is this field another SQLalchemy object, or a list of SQLalchemy objects?
                    if isinstance(val.__class__, DeclarativeMeta) or (isinstance(val, list) and len(val) > 0 and isinstance(val[0].__class__, DeclarativeMeta)):
                        # unless we're expanding this field, stop here
                        if field not in fields_to_expand:
                            # not expanding this field: set it to None and continue
                            fields[field] = None
                            continue

                    fields[field] = val
                # a json-encodable dict
                return fields

            return json.JSONEncoder.default(self, obj)

    return AlchemyEncoder

Şimdi şununla arayabilirsiniz:

print json.dumps(e, cls=new_alchemy_encoder(False, ['parents']), check_circular=False)

Örneğin, yalnızca 'ebeveynler' olarak adlandırılan SQLAlchemy alanlarını genişletmek için.


Bu harika bir yanıt, ancak düz olmayan yöntemler, herhangi bir fikir ile bir ilişki vurduğunda "BaseQuery" kodlayamadı "olsun?
Ben Kilah

1
@SashaB Bir ilişkinin tekrarlandığı vakaları daha ayrıntılı olarak hedeflemeye ne dersiniz? Ben varsa Örneğin, online_orderve address, hem de bir ilişki user, ancak online_orderaynı zamanda bir ilişkisi vardır address. Tüm bu seri hale getirmek isteseydim, dahil etmek olurdu addressiçinde fields_to_expandama gereksiz yere serialize istemem addressnedeniyle hem onun ilişkisine userve online_order.
Chrispy

2
@BenKilah Tahmin edeyim, Flask-SqlAlchemy kullanıyorsunuz ve modelleriniz Base'den değil db.Model'den miras alıyor. Bu durumda, okuyacak for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']:şekilde değiştirin for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata' and not x.startswith('query')]:. Bu çözümün 'sorgu' adıyla bir mülke / ilişkiye sahip olmanızı engelleyeceğini unutmayın
Pakman

benim yaptığım gibi, ama çok daha karmaşık. stackoverflow.com/questions/7102754/…
tyan


272

Nesnenizi sözlük olarak çıktılayabilirsiniz:

class User:
   def as_dict(self):
       return {c.name: getattr(self, c.name) for c in self.__table__.columns}

Ve sonra User.as_dict()nesnenizi serileştirmek için kullanın .

Sqlalchemy satır nesnesini python dict'e dönüştürme bölümünde açıklandığı gibi


2
@charlax, DateTime'ı nasıl düzeltebilirim? Bunu kullanarak json.dumps yaptığımda 'datetime.datetime (2013, 3, 22, 16, 50, 11) JSON serileştirilemez' olsun
Asken

1
JSONEncoderNesnenin sorumluluğu budur . Datetime dahil olmak üzere bazı nesneler için kendi kodlayıcınızı tanımlamak üzere alt sınıflandırabilirsiniz. FlaskÖrneğin, JSON'da kutunun dışında (en son sürümle) kodlama tarihini desteklediğini unutmayın .
charlax

3
Sqlalchemy'nin "bildirici" yöntemini kullanıyorsanız, özel bir Base sınıfına böyle bir şey ekleyebilirsiniz - bu, sahip olduğunuz herhangi bir ORM nesnesinde my_orm_object.toDict () yöntemini çağırabileceğiniz için oldukça kullanışlıdır. Benzer şekilde, toDict yönteminizi kullanan bir .toJSON () yöntemi ve tarihleri, lekeleri vb.
İşlemek

7
datetime desteklemek için:return {c.name: unicode(getattr(self, c.name)) for c in self.__table__.columns}
Shoham

1
Sınıf değişkenleriniz sütun adlarınızla aynı değilse bu çalışmaz. Bunun yerine sınıf adlarını nasıl alacağınız hakkında bir fikriniz var mı?
James Burke

55

RowProxy'yi aşağıdaki gibi bir diktüre dönüştürebilirsiniz:

 d = dict(row.items())

Sonra bunu JSON'a serileştirin ( datetimedeğerler gibi şeyler için bir kodlayıcı belirtmeniz gerekecek ) Sadece bir kayıt (ve ilgili kayıtların tam hiyerarşisini değil) istiyorsanız o kadar zor değil.

json.dumps([(dict(row.items())) for row in rs])

1
Bu, con: rs = con.execute (sql)
JZ

1
Bu çok daha basit ve işe yarıyor. Bu cevap ile kabul edilen cevap arasındaki fark nedir?
Sundeep

46

Hatmi kullanmanızı öneririm . İlişkiler ve iç içe nesneler desteği ile model örneklerinizi temsil etmek için serileştiriciler oluşturmanıza olanak tanır.

İşte belgelerinden kesilmiş bir örnek. ORM modelini alın Author:

class Author(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    first = db.Column(db.String(80))
    last = db.Column(db.String(80))

Bu sınıf için bir hatmi şeması şu şekilde oluşturulur:

class AuthorSchema(Schema):
    id = fields.Int(dump_only=True)
    first = fields.Str()
    last = fields.Str()
    formatted_name = fields.Method("format_name", dump_only=True)

    def format_name(self, author):
        return "{}, {}".format(author.last, author.first)

... ve şu şekilde kullanılır:

author_schema = AuthorSchema()
author_schema.dump(Author.query.first())

... şöyle bir çıktı üretirdi:

{
        "first": "Tim",
        "formatted_name": "Peters, Tim",
        "id": 1,
        "last": "Peters"
}

Flask-SQLAlchemy Örneklerine bir göz atın .

Denilen bir kütüphane marshmallow-sqlalchemyözellikle SQLAlchemy ve hatmi ile bütünleşir. Bu kitaplıkta, Authoryukarıda açıklanan modelin şeması şöyle görünür:

class AuthorSchema(ModelSchema):
    class Meta:
        model = Author

Entegrasyon, alan türlerinin SQLAlchemy Columntürlerinden çıkarılmasını sağlar.

hatmi-sqlalchemy burada.


12
Ayrıca şema üretimini kolaylaştıran marshmallow-sqlalchemy.readthedocs.io/en/latest buldum
Foo L

44

Python 3.7+ ve Flask 1.1+ yerleşik veri paketi paketini kullanabilir

from dataclasses import dataclass
from datetime import datetime
from flask import Flask, jsonify
from flask_sqlalchemy import SQLAlchemy


app = Flask(__name__)
db = SQLAlchemy(app)


@dataclass
class User(db.Model):
  id: int
  email: str

  id = db.Column(db.Integer, primary_key=True, auto_increment=True)
  email = db.Column(db.String(200), unique=True)


@app.route('/users/')
def users():
  users = User.query.all()
  return jsonify(users)  


if __name__ == "__main__":
  users = User(email="user1@gmail.com"), User(email="user2@gmail.com")
  db.create_all()
  db.session.add_all(users)
  db.session.commit()
  app.run()

/users/Rota artık kullanıcıların listesini döndürür.

[
  {"email": "user1@gmail.com", "id": 1},
  {"email": "user2@gmail.com", "id": 2}
]

İlgili modelleri otomatik serileştir

@dataclass
class Account(db.Model):
  id: int
  users: User

  id = db.Column(db.Integer)
  users = db.relationship(User)  # User model would need a db.ForeignKey field

Bu sorunun yanıtı jsonify(account)bu olacaktır.

{  
   "id":1,
   "users":[  
      {  
         "email":"user1@gmail.com",
         "id":1
      },
      {  
         "email":"user2@gmail.com",
         "id":2
      }
   ]
}

Varsayılan JSON Kodlayıcı'nın üzerine yaz

from flask.json import JSONEncoder


class CustomJSONEncoder(JSONEncoder):
  "Add support for serializing timedeltas"

  def default(o):
    if type(o) == datetime.timedelta:
      return str(o)
    elif type(o) == datetime.datetime:
      return o.isoformat()
    else:
      return super().default(o)

app.json_encoder = CustomJSONEncoder      

1
Bu doğru türde basit gibi görünüyor. Serileştirme için de işe yarıyor mu?
Ender2050

Ayrıştırılmış JSON sözlüğünü bir anahtar kelime bağımsız değişkenini kullanarak bir modele dönüştürebilirsiniz:data = request.json['user']; user = User(**data)
tom

3
id: int = Columnİşe yarayacağını unutmayın , ancak id = Columnçalışmaz, json'un alanı serileştirmesi için statik yazmayı bildirmeniz GEREKİR gibi görünüyor, aksi takdirde boş bir {}nesne elde edersiniz .
Ambroise Rabier

1
Bu benim için çalıştı, bu neden kabul edilen cevap değil? Flask-Marshmallow ile çalışmak için app_context saatlerce oynuyorum.
Nick Dat Le

1
Benim için de çalıştı. Eğer Python 3.6 iseniz, sadece paketi yüklemek isteyeceksiniz o Not: pipenv install dataclasses. Ve sonra işe yarayacak.
AleksandrH

14

Flask-JsonTools paketinin bir JsonSerializableBase uygulaması var modelleriniz için bir Base sınıfı .

Kullanımı:

from sqlalchemy.ext.declarative import declarative_base
from flask.ext.jsontools import JsonSerializableBase

Base = declarative_base(cls=(JsonSerializableBase,))

class User(Base):
    #...

Şimdi User model sihirli bir şekilde serileştirilebilir.

Çerçeveniz Flask değilse , kodu alabilirsiniz


2
Bu, sorunun yalnızca yarısını çözer, çünkü yalnızca tek bir satırı serileştirir. Sorgu sonucunun tamamı nasıl serileştirilir?
Steve Bennett

@SteveBennett yanıtı kodlamak için jsontools 'jsonapi' sini kullanır. Bu otomatik olarak dönüş nesnesini kodlayacaktır
Tjorriemorrie

Ben çok basit bir sqlalchemy modeli var ve alıyorum: TypeError: <ORM.State nesnesi 0x03577A50> JSON serileştirilebilir değil
Matej

1
Sonunda model nesneme açıkça __json __ () çağırarak çalıştı: return my_object .__ json __ ()
Matej

Artık Flask 1.0'da import flask.ext.whateverdesteklenmediği için kütüphane Flask 1.0 ve üstü ile çalışmaz .
Adarsh ​​Madrecha

14

Güvenlik nedeniyle, modelin tüm alanlarını asla iade etmemelisiniz. Onları seçerek seçmeyi tercih ederim.

Artık kodlama balona en json UUID, datetime ve ilişkileri (ve ilave destekler queryve query_classiçin flask_sqlalchemy db.Modelsınıfını). Kodlayıcıyı aşağıdaki gibi güncelledim:

Uygulamanın / json_encoder.py

    from sqlalchemy.ext.declarative import DeclarativeMeta
    from flask import json


    class AlchemyEncoder(json.JSONEncoder):
        def default(self, o):
            if isinstance(o.__class__, DeclarativeMeta):
                data = {}
                fields = o.__json__() if hasattr(o, '__json__') else dir(o)
                for field in [f for f in fields if not f.startswith('_') and f not in ['metadata', 'query', 'query_class']]:
                    value = o.__getattribute__(field)
                    try:
                        json.dumps(value)
                        data[field] = value
                    except TypeError:
                        data[field] = None
                return data
            return json.JSONEncoder.default(self, o)

app/__init__.py

# json encoding
from app.json_encoder import AlchemyEncoder
app.json_encoder = AlchemyEncoder

Bununla isteğe bağlı __json__olarak kodlamak istediğim alanların listesini döndüren bir özellik ekleyebilirim :

app/models.py

class Queue(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    song_id = db.Column(db.Integer, db.ForeignKey('song.id'), unique=True, nullable=False)
    song = db.relationship('Song', lazy='joined')
    type = db.Column(db.String(20), server_default=u'audio/mpeg')
    src = db.Column(db.String(255), nullable=False)
    created_at = db.Column(db.DateTime, server_default=db.func.now())
    updated_at = db.Column(db.DateTime, server_default=db.func.now(), onupdate=db.func.now())

    def __init__(self, song):
        self.song = song
        self.src = song.full_path

    def __json__(self):
        return ['song', 'src', 'type', 'created_at']

Görünümüme @jsonapi ekledim, sonuç listesini döndürdüm ve çıktım şu şekilde:

[

{

    "created_at": "Thu, 23 Jul 2015 11:36:53 GMT",
    "song": 

        {
            "full_path": "/static/music/Audioslave/Audioslave [2002]/1 Cochise.mp3",
            "id": 2,
            "path_name": "Audioslave/Audioslave [2002]/1 Cochise.mp3"
        },
    "src": "/static/music/Audioslave/Audioslave [2002]/1 Cochise.mp3",
    "type": "audio/mpeg"
}

]

Güzel! Bir kez daha, bazen her aptal küçük görev için yağ paketine ihtiyacınız olmadığının kanıtı - DSL öğrenmenin "zor" şekilde yapmaktan daha zor olabileceğini kanıtlayın. Buraya inmeden önce birçok JSON ve REST paketine baktım. Doğru, bu hala bir paket gerektirir flask_jsontools (eklemek @jsonapiiçin @app.routede views.py vs), ama bunun sadeliği seviyorum. Bence ucuz Flask datetime eklendi ama tarih değil, bu yüzden kendimi json_encoder.py'ye ekledim : value=...^ if isinstance(value, date):^ data[field] = datetime.combine(value, time.min).isoformat()^ else:^try:...
juanitogan

10

SqlAlchemy'nin içgözlemini şu şekilde kullanabilirsiniz:

mysql = SQLAlchemy()
from sqlalchemy import inspect

class Contacts(mysql.Model):  
    __tablename__ = 'CONTACTS'
    id = mysql.Column(mysql.Integer, primary_key=True)
    first_name = mysql.Column(mysql.String(128), nullable=False)
    last_name = mysql.Column(mysql.String(128), nullable=False)
    phone = mysql.Column(mysql.String(128), nullable=False)
    email = mysql.Column(mysql.String(128), nullable=False)
    street = mysql.Column(mysql.String(128), nullable=False)
    zip_code = mysql.Column(mysql.String(128), nullable=False)
    city = mysql.Column(mysql.String(128), nullable=False)
    def toDict(self):
        return { c.key: getattr(self, c.key) for c in inspect(self).mapper.column_attrs }

@app.route('/contacts',methods=['GET'])
def getContacts():
    contacts = Contacts.query.all()
    contactsArr = []
    for contact in contacts:
        contactsArr.append(contact.toDict()) 
    return jsonify(contactsArr)

@app.route('/contacts/<int:id>',methods=['GET'])
def getContact(id):
    contact = Contacts.query.get(id)
    return jsonify(contact.toDict())

Burada bir yanıttan ilham alın: sqlalchemy satır nesnesini python dict'e dönüştürün


5

Daha ayrıntılı bir açıklama. Modelinize şunları ekleyin:

def as_dict(self):
       return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns}

str()Python 2 kullanımını kullanarak eğer öyleyse piton 3 içindir unicode(). Tarihleri ​​serileştirmeye yardımcı olmalıdır. Bunlarla uğraşmazsanız kaldırabilirsiniz.

Artık veritabanını böyle sorgulayabilirsiniz

some_result = User.query.filter_by(id=current_user.id).first().as_dict()

First()garip hataları önlemek için gereklidir. as_dict()artık sonucun serisini kaldıracak. Serileştirmeden sonra json'a dönmeye hazır

jsonify(some_result)

3

O kadar da zor değil. Bunu yapmak için bir kod yazdım. Hala üzerinde çalışıyorum ve MochiKit çerçevesini kullanıyor. Temel olarak bir proxy ve kayıtlı JSON dönüştürücüler kullanarak Python ve Javascript arasında bileşik nesneleri çevirir.

Veritabanı nesneleri için tarayıcı tarafı db.js'dir Proxy.js'de temel Python proxy kaynağına ihtiyaç duyar .

Python tarafında temel proxy modülü vardır . Son olarak webserver.py dosyasında SqlAlchemy nesne kodlayıcısı . Models.py dosyasında bulunan meta veri çıkarıcılarına da bağlıdır .


Oldukça ben gerekenler ... İlk bakışta karmaşık - SQLAlchemy JSON sorgu sonucu nesneleri elde etmektir / XML formatı javascript datagird (JqGrid kullanmak için trirand.com/blog )
Zelid

Bazen sorunlar ilk bakışta beklediğinizden daha karmaşıktır ... Bu, yabancı anahtar olarak döndürülen nesneleri ele alır ve derin iç içe ilişkilerle meydana gelen sonsuz özyinelemeden kaçınmaya çalışır. Bununla birlikte, muhtemelen yalnızca temel türleri döndüren ve doğrudan simplejson ile serileştiren bazı özel sorgular yazabilirsiniz.
Keith

1
Doğru, belki de SQLAlchemy kullanarak dicts için sorgulama ile gideceğim ve sadece kaydetme / güncelleme eylemleri gerçekleştiren ORM faydalarını kullanacağım.
Zelid

3

Orijinal soru bir süre geriye giderken, buradaki cevapların sayısı (ve kendi deneyimlerim), farklı ödünleşmelerle değişen farklı karmaşıklık yaklaşımlarına sahip çok önemli olmayan bir soru olduğunu göstermektedir.

Bu yüzden SQLAthanor'u oluşturdum , SQLAlchemy'nin yapılandırılabilir serileştirme / serileştirme desteğiyle genişleten kütüphanesini oluşturdum.

Kütüphane şunları destekler:

  • Python 2.7, 3.4, 3.5 ve 3.6.
  • SQLAlchemy sürüm 0.9 ve üstü
  • JSON, CSV, YAML ve Python'a seri serileştirme / serileştirme dict
  • sütunların / niteliklerin, ilişkilerin, karma özelliklerin ve ilişkilendirme vekillerinin serileştirilmesi / serileştirilmesi
  • belirli biçimler ve sütunlar / ilişkiler / öznitelikler için serileştirmeyi etkinleştirme ve devre dışı bırakma (örneğin, gelen bir password değeri desteklemek istiyorsunuz , ancak asla giden bir değeri içermez )
  • serileştirme öncesi ve seriden çıkarma sonrası değer işleme (doğrulama veya tür baskısı için)
  • hem Pythonic hem de SQLAlchemy'nin kendi yaklaşımıyla sorunsuz bir şekilde tutarlı olan oldukça basit bir sözdizimi

(Umarım!) Kapsamlı dokümanlara buradan göz atabilirsiniz: https://sqlathanor.readthedocs.io/en/latest

Bu yardımcı olur umarım!


2

Özel serileştirme ve serileştirme.

"from_json" (sınıf yöntemi), json verilerine dayalı bir Model nesnesi oluşturur.

"serileştirme" yalnızca örnek olarak çağrılabilir ve json'daki tüm verileri Model örneğiyle birleştirebilir.

"serileştir" - özyinelemeli serileştirme

__write_only__ özelliği yalnızca yazma özelliklerini tanımlamak için gereklidir (örneğin "password_hash").

class Serializable(object):
    __exclude__ = ('id',)
    __include__ = ()
    __write_only__ = ()

    @classmethod
    def from_json(cls, json, selfObj=None):
        if selfObj is None:
            self = cls()
        else:
            self = selfObj
        exclude = (cls.__exclude__ or ()) + Serializable.__exclude__
        include = cls.__include__ or ()
        if json:
            for prop, value in json.iteritems():
                # ignore all non user data, e.g. only
                if (not (prop in exclude) | (prop in include)) and isinstance(
                        getattr(cls, prop, None), QueryableAttribute):
                    setattr(self, prop, value)
        return self

    def deserialize(self, json):
        if not json:
            return None
        return self.__class__.from_json(json, selfObj=self)

    @classmethod
    def serialize_list(cls, object_list=[]):
        output = []
        for li in object_list:
            if isinstance(li, Serializable):
                output.append(li.serialize())
            else:
                output.append(li)
        return output

    def serialize(self, **kwargs):

        # init write only props
        if len(getattr(self.__class__, '__write_only__', ())) == 0:
            self.__class__.__write_only__ = ()
        dictionary = {}
        expand = kwargs.get('expand', ()) or ()
        prop = 'props'
        if expand:
            # expand all the fields
            for key in expand:
                getattr(self, key)
        iterable = self.__dict__.items()
        is_custom_property_set = False
        # include only properties passed as parameter
        if (prop in kwargs) and (kwargs.get(prop, None) is not None):
            is_custom_property_set = True
            iterable = kwargs.get(prop, None)
        # loop trough all accessible properties
        for key in iterable:
            accessor = key
            if isinstance(key, tuple):
                accessor = key[0]
            if not (accessor in self.__class__.__write_only__) and not accessor.startswith('_'):
                # force select from db to be able get relationships
                if is_custom_property_set:
                    getattr(self, accessor, None)
                if isinstance(self.__dict__.get(accessor), list):
                    dictionary[accessor] = self.__class__.serialize_list(object_list=self.__dict__.get(accessor))
                # check if those properties are read only
                elif isinstance(self.__dict__.get(accessor), Serializable):
                    dictionary[accessor] = self.__dict__.get(accessor).serialize()
                else:
                    dictionary[accessor] = self.__dict__.get(accessor)
        return dictionary

2

Çıktınıza dahil etmek istediğiniz ilişkileri gitmek istediğiniz kadar derin seçmenizi sağlayan bir çözüm. Not: Bu bir dict / str bir liste yerine arg olarak alarak tam bir yeniden yazma. bazı şeyleri düzeltir ..

def deep_dict(self, relations={}):
    """Output a dict of an SA object recursing as deep as you want.

    Takes one argument, relations which is a dictionary of relations we'd
    like to pull out. The relations dict items can be a single relation
    name or deeper relation names connected by sub dicts

    Example:
        Say we have a Person object with a family relationship
            person.deep_dict(relations={'family':None})
        Say the family object has homes as a relation then we can do
            person.deep_dict(relations={'family':{'homes':None}})
            OR
            person.deep_dict(relations={'family':'homes'})
        Say homes has a relation like rooms you can do
            person.deep_dict(relations={'family':{'homes':'rooms'}})
            and so on...
    """
    mydict =  dict((c, str(a)) for c, a in
                    self.__dict__.items() if c != '_sa_instance_state')
    if not relations:
        # just return ourselves
        return mydict

    # otherwise we need to go deeper
    if not isinstance(relations, dict) and not isinstance(relations, str):
        raise Exception("relations should be a dict, it is of type {}".format(type(relations)))

    # got here so check and handle if we were passed a dict
    if isinstance(relations, dict):
        # we were passed deeper info
        for left, right in relations.items():
            myrel = getattr(self, left)
            if isinstance(myrel, list):
                mydict[left] = [rel.deep_dict(relations=right) for rel in myrel]
            else:
                mydict[left] = myrel.deep_dict(relations=right)
    # if we get here check and handle if we were passed a string
    elif isinstance(relations, str):
        # passed a single item
        myrel = getattr(self, relations)
        left = relations
        if isinstance(myrel, list):
            mydict[left] = [rel.deep_dict(relations=None)
                                 for rel in myrel]
        else:
            mydict[left] = myrel.deep_dict(relations=None)

    return mydict

kişi / aile / ev / oda kullanan bir örnek için ... json'a çevirmek için tek ihtiyacınız olan şey

json.dumps(person.deep_dict(relations={'family':{'homes':'rooms'}}))

Bu iyi, ben sadece tüm nesneler onu alacak böylece temel sınıf koymak düşünüyorum. Ben size kodlama json bırakacağım ...
tahoe

Bu sürümün tüm liste ilişkilerini alacağını unutmayın, bu yüzden bir ton öğe ile ilişkiler sağlayarak dikkatli olun ...
tahoe

1
def alc2json(row):
    return dict([(col, str(getattr(row,col))) for col in row.__table__.columns.keys()])

Ben bununla küçük bir kod golf oynamak düşündüm.

FYI: automap_base kullanıyorum İş gereksinimlerine göre ayrı olarak tasarlanmış bir . Bugün SQLAlchemy kullanmaya başladım, ancak belgelerde automap_base'in SQLAlchemy ORM'deki tipik paradigma gibi görünen declarative_base için bir uzantı olduğunu belirtiyor, bu yüzden bunun çalışması gerektiğine inanıyorum.

Tjorriemorrie'nin çözümü başına aşağıdaki yabancı anahtarlarla süslenmez , ancak sütunları değerlerle eşleştirir ve sütun değerlerini str () ile Python türlerini işler. Değerlerimiz Python datetime.time ve ondalık değerlerden oluşur.

Umarım bu yoldan geçen herkese yardımcı olur!


1

Bunun oldukça eski bir yazı olduğunu biliyorum. @SashaB tarafından verilen çözümü aldım ve ihtiyacım doğrultusunda değiştirdim.

Aşağıdaki şeyleri ekledim:

  1. Alan yoksayma listesi: Serileştirme sırasında yok sayılacak alanların listesi
  2. Alan değiştirme listesi: Serileştirilirken değerlerle değiştirilecek alan adlarını içeren bir sözlük.
  3. Kaldırılan yöntemler ve BaseQuery serileştiriliyor

Kodum aşağıdaki gibidir:

def alchemy_json_encoder(revisit_self = False, fields_to_expand = [], fields_to_ignore = [], fields_to_replace = {}):
   """
   Serialize SQLAlchemy result into JSon
   :param revisit_self: True / False
   :param fields_to_expand: Fields which are to be expanded for including their children and all
   :param fields_to_ignore: Fields to be ignored while encoding
   :param fields_to_replace: Field keys to be replaced by values assigned in dictionary
   :return: Json serialized SQLAlchemy object
   """
   _visited_objs = []
   class AlchemyEncoder(json.JSONEncoder):
      def default(self, obj):
        if isinstance(obj.__class__, DeclarativeMeta):
            # don't re-visit self
            if revisit_self:
                if obj in _visited_objs:
                    return None
                _visited_objs.append(obj)

            # go through each field in this SQLalchemy class
            fields = {}
            for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata' and x not in fields_to_ignore]:
                val = obj.__getattribute__(field)
                # is this field method defination, or an SQLalchemy object
                if not hasattr(val, "__call__") and not isinstance(val, BaseQuery):
                    field_name = fields_to_replace[field] if field in fields_to_replace else field
                    # is this field another SQLalchemy object, or a list of SQLalchemy objects?
                    if isinstance(val.__class__, DeclarativeMeta) or \
                            (isinstance(val, list) and len(val) > 0 and isinstance(val[0].__class__, DeclarativeMeta)):
                        # unless we're expanding this field, stop here
                        if field not in fields_to_expand:
                            # not expanding this field: set it to None and continue
                            fields[field_name] = None
                            continue

                    fields[field_name] = val
            # a json-encodable dict
            return fields

        return json.JSONEncoder.default(self, obj)
   return AlchemyEncoder

Umarım birine yardım eder!


1

SQLAlchemy'de yerleşik serileştiriciyi kullanın :

from sqlalchemy.ext.serializer import loads, dumps
obj = MyAlchemyObject()
# serialize object
serialized_obj = dumps(obj)

# deserialize object
obj = loads(serialized_obj)

Nesneyi oturumlar arasında aktarıyorsanız, nesneyi kullanarak geçerli oturumdan ayırmayı unutmayın session.expunge(obj). Tekrar takmak için yapmanız yeterlidir session.add(obj).


Şık, ancak JSON dönüştürmez.
blakev

2
JSON 'serileştirme' için hatmi-sqlalchemy'ye göz atın . Nesneleri istemcilere sunarken kesinlikle en iyi çözüm. marshmallow-sqlalchemy.readthedocs.io
chribsen

Serileştirici modülü yalnızca sorgu yapıları için uygundur. Şunlar için gerekli değildir: kullanıcı tanımlı sınıf örnekleri. Bunlar tipik durumda motorlara, oturumlara veya ifade yapılarına referans içermez ve doğrudan serileştirilebilir.
thomasd

1

Aşağıdaki kod sqlalchemy sonucunu json'a serileştirir.

import json
from collections import OrderedDict


def asdict(self):
    result = OrderedDict()
    for key in self.__mapper__.c.keys():
        if getattr(self, key) is not None:
            result[key] = str(getattr(self, key))
        else:
            result[key] = getattr(self, key)
    return result


def to_array(all_vendors):
    v = [ ven.asdict() for ven in all_vendors ]
    return json.dumps(v) 

Eğlenceyi aramak,

def all_products():
    all_products = Products.query.all()
    return to_array(all_products)

1

AlchemyEncoder harika ama bazen Ondalık değerlerle başarısız oluyor. İşte ondalık sorunu çözen geliştirilmiş bir kodlayıcı -

class AlchemyEncoder(json.JSONEncoder):
# To serialize SQLalchemy objects 
def default(self, obj):
    if isinstance(obj.__class__, DeclarativeMeta):
        model_fields = {}
        for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']:
            data = obj.__getattribute__(field)
            print data
            try:
                json.dumps(data)  # this will fail on non-encodable values, like other classes
                model_fields[field] = data
            except TypeError:
                model_fields[field] = None
        return model_fields
    if isinstance(obj, Decimal):
        return float(obj)
    return json.JSONEncoder.default(self, obj)

1

Bir db I bağlamak için sqlalchemy kullanırken bu son derece yapılandırılabilir basit bir çözümdür. Pandalar kullanın.

import pandas as pd
import sqlalchemy

#sqlalchemy engine configuration
engine = sqlalchemy.create_engine....

def my_function():
  #read in from sql directly into a pandas dataframe
  #check the pandas documentation for additional config options
  sql_DF = pd.read_sql_table("table_name", con=engine)

  # "orient" is optional here but allows you to specify the json formatting you require
  sql_json = sql_DF.to_json(orient="index")

  return sql_json

1
step1:
class CNAME:
   ...
   def as_dict(self):
       return {item.name: getattr(self, item.name) for item in self.__table__.columns}

step2:
list = []
for data in session.query(CNAME).all():
    list.append(data.as_dict())

step3:
return jsonify(list)

3
Herhangi bir açıklama yapmadan kod dökümleri nadiren yardımcı olur. Yığın Taşması öğrenme ile ilgilidir, körü körüne kopyalayıp yapıştırmak için parçacıklar sağlamaz. Lütfen sorunuzu düzenleyin ve OP'nin sağladığından daha iyi nasıl çalıştığını açıklayın.
Chris

0

Flask altında, işler ve kolları bu tip bir alan dönüştüren, alanları DataTime
'time': datetime.datetime(2018, 3, 22, 15, 40)içine
"time": "2018-03-22 15:40:00":

obj = {c.name: str(getattr(self, c.name)) for c in self.__table__.columns}

# This to get the JSON body
return json.dumps(obj)

# Or this to get a response object
return jsonify(obj)

0

Utf-8 içeren yerleşik serileştirici bobinleri, bazı girişler için geçersiz başlangıç ​​baytının kodunu çözemez. Bunun yerine:

def row_to_dict(row):
    temp = row.__dict__
    temp.pop('_sa_instance_state', None)
    return temp


def rows_to_list(rows):
    ret_rows = []
    for row in rows:
        ret_rows.append(row_to_dict(row))
    return ret_rows


@website_blueprint.route('/api/v1/some/endpoint', methods=['GET'])
def some_api():
    '''
    /some_endpoint
    '''
    rows = rows_to_list(SomeModel.query.all())
    response = app.response_class(
        response=jsonplus.dumps(rows),
        status=200,
        mimetype='application/json'
    )
    return response

0

Belki böyle bir sınıf kullanabilirsiniz

from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy import Table


class Custom:
    """Some custom logic here!"""

    __table__: Table  # def for mypy

    @declared_attr
    def __tablename__(cls):  # pylint: disable=no-self-argument
        return cls.__name__  # pylint: disable= no-member

    def to_dict(self) -> Dict[str, Any]:
        """Serializes only column data."""
        return {c.name: getattr(self, c.name) for c in self.__table__.columns}

Base = declarative_base(cls=Custom)

class MyOwnTable(Base):
    #COLUMNS!

Bununla tüm nesnelerin to_dictyöntemi var


0

Bazı ham sql ve tanımsız nesneleri kullanırken cursor.description, aradığımı almak için kullanarak ortaya çıktı:

with connection.cursor() as cur:
    print(query)
    cur.execute(query)
    for item in cur.fetchall():
        row = {column.name: item[i] for i, column in enumerate(cur.description)}
        print(row)

-2

(Çok fazla mı?) Sözlükleri kullanıyorum:

def serialize(_query):
    #d = dictionary written to per row
    #D = dictionary d is written to each time, then reset
    #Master = dictionary of dictionaries; the id Key (int, unique from database) 
    from D is used as the Key for the dictionary D entry in Master
    Master = {}
    D = {}
    x = 0
    for u in _query:
        d = u.__dict__
        D = {}
        for n in d.keys():
           if n != '_sa_instance_state':
                    D[n] = d[n]
        x = d['id']
        Master[x] = D
    return Master

Çıktıları JSON olarak yazdırmak için flask (jsonify dahil) ve flask_sqlalchemy ile çalışıyor.

İşlevi jsonify (serialize ()) ile çağırın.

Şimdiye kadar denediğim tüm SQLAlchemy sorgularıyla çalışır (SQLite3 çalıştırıyor)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.