Ubah objek baris sqlalchemy menjadi python dict

241

Apakah ada cara sederhana untuk beralih pada pasangan nama dan nilai kolom?

Versi saya dari sqlalchemy adalah 0.5.6

Berikut adalah contoh kode tempat saya mencoba menggunakan dict (baris), tetapi ia melempar pengecualian, TypeError: objek 'Pengguna' tidak dapat diubah

import sqlalchemy
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

print "sqlalchemy version:",sqlalchemy.__version__ 

engine = create_engine('sqlite:///:memory:', echo=False)
metadata = MetaData()
users_table = Table('users', metadata,
     Column('id', Integer, primary_key=True),
     Column('name', String),
)
metadata.create_all(engine) 

class User(declarative_base()):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    name = Column(String)

    def __init__(self, name):
        self.name = name

Session = sessionmaker(bind=engine)
session = Session()

user1 = User("anurag")
session.add(user1)
session.commit()

# uncommenting next line throws exception 'TypeError: 'User' object is not iterable'
#print dict(user1)
# this one also throws 'TypeError: 'User' object is not iterable'
for u in session.query(User).all():
    print dict(u)

Menjalankan kode ini pada output sistem saya:

sqlalchemy version: 0.5.6
Traceback (most recent call last):
  File "untitled-1.py", line 37, in <module>
    print dict(u)
TypeError: 'User' object is not iterable
Anurag Uniyal
sumber
3
Judul pertanyaan tidak cocok dengan pertanyaan itu sendiri. Menurut docs, Baris hasil yang dikembalikan oleh Query yang mengandung banyak entitas ORM dan / atau ekspresi kolom menggunakan kelas ini untuk mengembalikan baris. di mana kelas ini adalah sqlalchemy.util.KeyedTupleyang merupakan objek baris dari judul pertanyaan. Namun kueri dalam pertanyaan menggunakan kelas model (dipetakan) sehingga jenis objek baris adalah kelas model, bukan sqlalchemy.util.KeyedTuple.
Piotr Dobrogost
2
@PiotrDobrogost Pertanyaan berasal dari 2009 dan menyebutkan sqlalchemy versi 0.5.6
Anurag Uniyal

Jawaban:

233

Anda dapat mengakses internal __dict__objek SQLAlchemy, seperti berikut ::

for u in session.query(User).all():
    print u.__dict__
Hllau
sumber
24
Jawaban terbaik di utas ini, tidak tahu mengapa orang lain mengusulkan solusi yang jauh lebih rumit.
Dave Rawks
92
Ini memberikan bidang '_sa_instance_state' tambahan, setidaknya dalam versi 0.7.9.
elbear
21
jadi ini akan lebih baik:dictret = dict(row.__dict__); dictret.pop('_sa_instance_state', None)
lyfing
6
ini tampaknya tidak ideal karena seperti yang ditunjukkan orang __dict__termasuk _sa_instance_stateentri yang kemudian harus dihapus. jika Anda memutakhirkan ke versi masa depan dan atribut lainnya ditambahkan, Anda mungkin harus kembali dan menanganinya secara manual. jika Anda hanya menginginkan data kolom (misalnya, untuk mengambil daftar instance dari kueri dan meletakkannya dalam bingkai data panda) maka {col.name: getattr(self, col.name) for col in self.__table__.columns}seperti dijawab oleh Anurag Uniyal (dengan koreksi penting dari komentar untuk jawaban itu) tampaknya lebih ringkas dan salah- bukti.
kilgoretrout
14
Jawaban ini salah. Pertanyaannya bahkan telah dict(u)dan dengan benar menyatakan bahwa ia melempar a TypeError.
RazerM
146

Saya tidak bisa mendapatkan jawaban yang baik jadi saya menggunakan ini:

def row2dict(row):
    d = {}
    for column in row.__table__.columns:
        d[column.name] = str(getattr(row, column.name))

    return d

Sunting: jika fungsi di atas terlalu panjang dan tidak cocok untuk beberapa selera di sini adalah satu liner (python 2.7+)

row2dict = lambda r: {c.name: str(getattr(r, c.name)) for c in r.__table__.columns}
Anurag Uniyal
sumber
17
Lebih ringkas return dict((col, getattr(row, col)) for col in row.__table__.columns.keys()),.
argentpepper
4
@argentpepper ya Anda bahkan row2dict = lambda row: dict((col, getattr(row, col)) for col in row.__table__.columns.keys())dapat membuatnya menjadi liner nyata, tetapi saya lebih suka kode saya dapat dibaca, pendek secara horizontal, panjang secara vertikal
Anurag Uniyal
14
Bagaimana jika Kolom saya tidak ditugaskan ke atribut dengan nama yang sama? Yaitu x = Column('y', Integer, primary_key=True),? Tidak ada solusi yang berfungsi dalam kasus ini.
Buttons840
13
drdaeman benar, ini cuplikan yang benar:return {c.name: getattr(self, c.name) for c in self.__table__.columns}
charlax
5
Jawaban ini membuat asumsi yang tidak valid: nama kolom tidak harus cocok dengan nama atribut.
RazerM
95

Sesuai @zzzeek dalam komentar:

perhatikan bahwa ini adalah jawaban yang benar untuk versi SQLAlchemy modern, dengan anggapan "baris" adalah objek baris inti, bukan turunan ORM yang dipetakan.

for row in resultproxy:
    row_as_dict = dict(row)
Alex Brasetvik
sumber
13
Ia mengatakan 'objek XXX tidak iterable', saya menggunakan 0,5.6, saya dapatkan oleh session.query (Klass) .filter (). All ()
Anurag Uniyal
60
perhatikan bahwa ini adalah jawaban yang benar untuk versi SQLAlchemy modern, dengan anggapan "baris" adalah objek baris inti, bukan turunan ORM yang dipetakan.
zzzeek
48
Perhatikan juga bahwa zzzeek adalah pencipta sqlalchemy.
chris
1
Adakah yang bisa menjelaskan hal ini? Saya seorang pemula dan tidak mengerti.
lameei
1
Apa perbedaan antara objek baris inti versus instance yang dipetakan ORM? Ini tidak berfungsi untuk saya pada baris dari query(MyModel).all(): Objek MyModel tidak dapat diubah.
Jonathan Hartley
81

Dalam SQLAlchemy v0.8 dan yang lebih baru, gunakan sistem inspeksi .

from sqlalchemy import inspect

def object_as_dict(obj):
    return {c.key: getattr(obj, c.key)
            for c in inspect(obj).mapper.column_attrs}

user = session.query(User).first()

d = object_as_dict(user)

Perhatikan bahwa itu .keyadalah nama atribut, yang dapat berbeda dari nama kolom, misalnya dalam kasus berikut:

class_ = Column('class', Text)

Metode ini juga berfungsi untuk column_property.

RazerM
sumber
@ DukeDougal Saya pikir ini bekerja dari v0.8 (ketika sistem inspeksi ditambahkan).
RazerM
1
Ini bekerja dengan Sqlalchemy v2.0. Jawaban lain tidak.
Thanh Nguyen
Ini tidak memperhitungkan kolom tangguhan akun
Tandai
1
@ Mark Tidak jelas bagi saya bahwa mereka harus dikecualikan secara default. Namun demikian, Anda dapat memeriksa bahwa kunci tidak disqlalchemy.inspect(obj).unloaded
RazerM
5
@NguyenThanh Bekerja dengan SQLAlchemy v2.0 sangat mengesankan mengingat tidak adanya! Rilis terbaru (beta) adalah v1.3.0b1.
Mark Amery
39

baris memiliki _asdict()fungsi yang memberikan dict

In [8]: r1 = db.session.query(Topic.name).first()

In [9]: r1
Out[9]: (u'blah')

In [10]: r1.name
Out[10]: u'blah'

In [11]: r1._asdict()
Out[11]: {'name': u'blah'}
balki
sumber
Itu seharusnya bersifat pribadi dan tidak mungkin bisa dihapus / diubah dalam versi masa depan.
balki
2
@ Balalk Ini didokumentasikan dengan cukup baik dan karenanya tidak terlalu pribadi. Meskipun garis bawah terkemuka memiliki arti dalam Python secara umum, di sini mungkin digunakan untuk tidak berbenturan dengan kunci tuple yang mungkin.
Ilja Everilä
5
Ini hanya berfungsi dengan s KeyedTuple, yang hanya dikembalikan ketika menanyakan bidang tertentu dari suatu baris. yaitu .query (Topic.name) mengembalikan KeyedTuple, sedangkan .query (Topic) mengembalikan Topik, yang tidak memiliki ._asdict () - Derp. lihat saja jawaban STB di bawah ini.
Chad Lowe
20

seperti yang disebutkan @balki:

The _asdict()metode dapat digunakan jika Anda query bidang tertentu karena dikembalikan sebagai KeyedTuple .

In [1]: foo = db.session.query(Topic.name).first()
In [2]: foo._asdict()
Out[2]: {'name': u'blah'}

Sedangkan, jika Anda tidak menentukan kolom, Anda dapat menggunakan salah satu metode yang diusulkan lainnya - seperti yang disediakan oleh @charlax. Perhatikan bahwa metode ini hanya valid untuk 2.7+.

In [1]: foo = db.session.query(Topic).first()
In [2]: {x.name: getattr(foo, x.name) for x in foo.__table__.columns}
Out[2]: {'name': u'blah'}
Sam Bourne
sumber
Jika atribut kelas python ORM memiliki nama yang berbeda dari kolom basis data, coba solusi ini: stackoverflow.com/questions/27947294/...
TaiwanGrapefruitTea
2
sebenarnya, solusi yang lebih baik untuk semua kasus disediakan oleh penulis sqlalchemy di stackoverflow.com/a/27948279/1023033
TaiwanGrapefruitTea
Ketika saya mencoba ini saya mendapatkan objek ResultProxy tidak memiliki atribut '_asdict'
slashdottir
@ slashdottir, apakah Anda mengeksekusi objek kueri Anda (memanggil .first()metode)?
Sam Bourne
1
Jawaban ini membuat asumsi yang tidak valid: nama kolom tidak harus cocok dengan nama atribut - lihat jawaban RazerM.
Piotr Dobrogost
18

Pertanyaan lama, tetapi karena ini hasil pertama untuk "baris sqlalchemy untuk mendiktekan" di Google, ia pantas mendapatkan jawaban yang lebih baik.

Objek RowProxy yang dikembalikan SqlAlchemy memiliki metode items (): http://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.RowProxy.items

Itu hanya mengembalikan daftar (kunci, nilai) tupel. Jadi seseorang dapat mengonversi baris menjadi dikte menggunakan yang berikut:

Dengan Python <= 2.6:

rows = conn.execute(query)
list_of_dicts = [dict((key, value) for key, value in row.items()) for row in rows]

Dengan Python> = 2.7:

rows = conn.execute(query)
list_of_dicts = [{key: value for (key, value) in row.items()} for row in rows]
fgasparini
sumber
13
Anda bisa melakukannyalist_of_dicts = [dict(row.items()) for row in rows]
Markus Meskanen
Satu halangan adalah bahwa nama kolom yang digunakan SQLAlchemy dalam set hasil adalah table_name_column_name, jika Anda ingin nama yang berbeda (mis. Hanya column_name), gunakan .labelmetode ini. session.query( MyTable.column_name.label('column_name'), ... )
Aneel
Hai Saya mendapatkan masalah ini, tolong bantu saya * datetime.datetime (2018, 11, 24, 18, 52, 50) bukan JSON yang dapat
diubah-ubah
13

Dengan asumsi fungsi berikut akan ditambahkan ke class Useryang berikut ini akan mengembalikan semua pasangan nilai kunci dari semua kolom:

def columns_to_dict(self):
    dict_ = {}
    for key in self.__mapper__.c.keys():
        dict_[key] = getattr(self, key)
    return dict_

tidak seperti jawaban yang lain, semua hanya atribut tersebut dari objek yang dikembalikan yang merupakan Columnatribut di tingkat kelas objek. Oleh karena itu tidak ada _sa_instance_stateatau atribut SQLalchemy lainnya atau Anda tambahkan ke objek disertakan. Referensi

EDIT: Lupa untuk mengatakan, bahwa ini juga berfungsi pada Kolom bawaan.

hybrid_propery perpanjangan

Jika Anda juga ingin menyertakan hybrid_propertyatribut berikut ini akan berfungsi:

from sqlalchemy import inspect
from sqlalchemy.ext.hybrid import hybrid_property

def publics_to_dict(self) -> {}:
    dict_ = {}
    for key in self.__mapper__.c.keys():
        if not key.startswith('_'):
            dict_[key] = getattr(self, key)

    for key, prop in inspect(self.__class__).all_orm_descriptors.items():
        if isinstance(prop, hybrid_property):
            dict_[key] = getattr(self, key)
    return dict_

Saya berasumsi di sini bahwa Anda menandai Kolom dengan awal _untuk menunjukkan bahwa Anda ingin menyembunyikannya, baik karena Anda mengakses atribut dengan hybrid_propertyatau Anda tidak ingin menunjukkannya. Referensi

Tipp all_orm_descriptors juga mengembalikan hybrid_method dan AssociationProxy jika Anda juga ingin memasukkannya.

Keterangan untuk jawaban lain

Setiap jawaban (seperti 1 , 2 ) yang didasarkan pada __dict__atribut hanya mengembalikan semua atribut objek. Ini bisa menjadi atribut yang lebih banyak daripada yang Anda inginkan. Seperti saya sedih ini termasuk _sa_instance_stateatau atribut lain yang Anda tetapkan pada objek ini.

Setiap jawaban (seperti 1 , 2 ) yang didasarkan pada dict()fungsi hanya bekerja pada objek baris SQLalchemy yang dikembalikan oleh session.execute()bukan pada kelas yang Anda tentukan untuk bekerja, seperti class Userdari pertanyaan.

The jawaban pemecahan yang didasarkan pada row.__table__.columnspasti akan tidak bekerja. row.__table__.columnsberisi nama kolom dari Database SQL. Ini hanya bisa sama dengan nama atribut dari objek python. Jika tidak, Anda mendapatkan AttributeError. Untuk jawaban (seperti 1 , 2 ) berdasarkan class_mapper(obj.__class__).mapped_table.citu sama.

F. Raab
sumber
12
from sqlalchemy.orm import class_mapper

def asdict(obj):
    return dict((col.name, getattr(obj, col.name))
                for col in class_mapper(obj.__class__).mapped_table.c)
Marco Mariani
sumber
4
Waspadai perbedaan antara local_table dan mapped_table. Misalnya, jika Anda menerapkan semacam pewarisan tabel di db Anda (tbl_employees> tbl_managers, tbl_employees> tbl_staff), kelas yang dipetakan perlu mencerminkan ini (Manajer (Karyawan), Staf (Karyawan)). mapped_table.c akan memberi Anda nama kolom dari tabel dasar dan tabel pewarisan. local_table hanya memberi Anda nama tabel (pewarisan) Anda.
Michael Ekoka
Ini menghindari memberikan bidang '_sa_instance_state', setidaknya dalam versi 0.8+.
Evan Siroky
4
Jawaban ini membuat asumsi yang tidak valid: nama kolom tidak harus cocok dengan nama atribut.
RazerM
11

Mengikuti jawaban @balki, karena SQLAlchemy 0.8 Anda dapat menggunakan _asdict (), tersedia untuk objek-objek KeyedTuple. Ini memberikan jawaban yang cukup mudah untuk pertanyaan awal. Hanya, ubah dalam contoh Anda dua baris terakhir (untuk loop) untuk yang satu ini:

for u in session.query(User).all():
   print u._asdict()

Ini berfungsi karena dalam kode di atas Anda adalah objek dari kelas tipe KeyedTuple , karena .all () mengembalikan daftar KeyedTuple. Oleh karena itu ia memiliki metode _asdict () , yang dengan baik mengembalikan Anda sebagai kamus.

WRT jawabannya oleh @STB: AFAIK, dan di samping itu .all () kembali adalah daftar KeypedTuple. Oleh karena itu, di atas berfungsi baik jika Anda menentukan kolom atau tidak, selama Anda berurusan dengan hasil .all () seperti yang diterapkan pada objek kueri.

jgbarah
sumber
6
Ini mungkin benar di masa lalu, tetapi pada SQLAlchemy v1.0 .all()mengembalikan daftar Userinstance, jadi ini tidak berfungsi.
RazerM
@RazerM, maaf, tapi saya tidak mengerti maksud Anda. Untuk loop harus tepat loop melalui daftar instance Pengguna, mengubahnya (u) ke kamus, dan kemudian mencetaknya ...
jgbarah
3
Usercontoh tidak memiliki _asdictmetode. Lihat gist.github.com/RazerM/2eff51571b3c70e8aeecd303c2a2bc8d
RazerM
2
Sekarang saya paham. Terima kasih. Alih-alih KeyedTuple, sekarang .all () mengembalikan objek Pengguna. Jadi masalah untuk v1.0 (dan yang lebih tinggi, saya berasumsi) adalah bagaimana cara mengeluarkan kamus dari objek Pengguna. Terimakasih atas klarifikasinya.
jgbarah
10

Ekspresi yang Anda iterasi melalui mengevaluasi ke daftar objek model , bukan baris. Jadi berikut ini adalah penggunaan yang benar dari mereka:

for u in session.query(User).all():
    print u.id, u.name

Apakah Anda benar-benar perlu mengubahnya menjadi dikt? Tentu, ada banyak cara, tetapi Anda tidak perlu ORM bagian dari SQLAlchemy:

result = session.execute(User.__table__.select())
for row in result:
    print dict(row)

Pembaruan : Lihatlah sqlalchemy.orm.attributesmodul. Ini memiliki seperangkat fungsi untuk bekerja dengan keadaan objek, yang mungkin berguna bagi Anda, terutama instance_dict().

Denis Otkidach
sumber
2
Saya ingin mengkonversikannya ke dict untuk, karena beberapa kode lain membutuhkan data sebagai dict, dan saya ingin cara generik karena saya tidak akan tahu apa yang kolom memiliki model objek
Anurag Uniyal
dan ketika saya mendapatkan pegangan untuk mereka saya memiliki akses ke Model objek hanya jadi saya tidak dapat menggunakan session.execute dll
Anurag Uniyal
8

Lihat Jawaban Alex Brasetvik , Anda dapat menggunakan satu baris kode untuk menyelesaikan masalah

row_as_dict = [dict(row) for row in resultproxy]

Di bawah bagian komentar Jawaban Alex Brasetvik, zzzeek pencipta SQLAlchemy menyatakan ini adalah "Metode yang Benar" untuk masalah tersebut.

Norway
sumber
1
@ Greenonline Tentu, komentar persetujuan ada di bawah jawaban Alex Brasetvik. Diedit untuk menambahkan tautan ke jawabannya
NorWay
Apa itu resultproxy?
lameei
8

Anda dapat mencoba melakukannya dengan cara ini.

for u in session.query(User).all():
    print(u._asdict())

Itu menggunakan metode built-in dalam objek kueri yang mengembalikan objek diktoner objek kueri.

referensi: https://docs.sqlalchemy.org/en/latest/orm/query.html

Enmanuel Medina
sumber
1
Tambahkan lebih banyak penjelasan mungkin?
Tiw
1
Tidak ada yang lebih untuk dijelaskan. Ini adalah metode bawaan pada objek hasil. Jadi apakah Anda melakukan ini untuk semua hasil, atau satu baris, ada _asdict()metode bawaan yang pada dasarnya zip nama bidang dengan nilai bidang dan mengembalikan hasilnya sebagai kamus.
Matius
Sangat ringkas dan saya berharap itu berhasil tetapi udalam kasus saya adalah string, dan saya mendapatkan kesalahan `` Model 'objek tidak memiliki atribut' _asdict'` @hllau di bawah ini berfungsi untuk saya
Mote Zart
7

Saya telah menemukan posting ini karena saya sedang mencari cara untuk mengubah baris SQLAlchemy menjadi dict. Saya menggunakan SqlSoup ... tapi jawabannya dibangun sendiri, jadi, jika itu bisa membantu seseorang, inilah dua sen saya:

a = db.execute('select * from acquisizioni_motes')
b = a.fetchall()
c = b[0]

# and now, finally...
dict(zip(c.keys(), c.values()))
Mychot sedih
sumber
1
atau, jika Anda lebih suka ..: [dict (zip (i.keys (), i.values ​​())) untuk i in b]
Mychot sad
Ini adalah satu-satunya sintaks yang saya temukan yang benar-benar berfungsi! Saya sudah mencoba hal-hal selama lebih dari satu jam.
slashdottir
Untuk pilihan inti, RowProxy( cdalam jawaban ini) mematuhi protokol pemetaan, jadi Anda bisa menelepon dict(c).
RazerM
4

Anda dapat mengonversi objek sqlalchemy ke kamus seperti ini dan mengembalikannya sebagai json / kamus.

Fungsi pembantu:

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) 

Fungsi Pengemudi:

def all_products():
    all_products = Products.query.all()
    return to_array(all_products)
Chirag Vora
sumber
3

Dua arah:

1.

for row in session.execute(session.query(User).statement):
    print(dict(row))

2.

selected_columns = User.__table__.columns
rows = session.query(User).with_entities(*selected_columns).all()
for row in rows :
    print(row._asdict())
FrostSigh
sumber
3

Dokumen menawarkan solusi yang sangat sederhana: ResultRow._asdict()

def to_array(rows):
    return [r._asdict() for r in rows]

def query():
    data = session.query(Table).all()
    return to_array(data)
orang suci kuning
sumber
2

Inilah cara Elixir melakukannya. Nilai dari solusi ini adalah memungkinkannya secara rekursif termasuk representasi kamus hubungan.

def to_dict(self, deep={}, exclude=[]):
    """Generate a JSON-style nested dict/list structure from an object."""
    col_prop_names = [p.key for p in self.mapper.iterate_properties \
                                  if isinstance(p, ColumnProperty)]
    data = dict([(name, getattr(self, name))
                 for name in col_prop_names if name not in exclude])
    for rname, rdeep in deep.iteritems():
        dbdata = getattr(self, rname)
        #FIXME: use attribute names (ie coltoprop) instead of column names
        fks = self.mapper.get_property(rname).remote_side
        exclude = [c.name for c in fks]
        if dbdata is None:
            data[rname] = None
        elif isinstance(dbdata, list):
            data[rname] = [o.to_dict(rdeep, exclude) for o in dbdata]
        else:
            data[rname] = dbdata.to_dict(rdeep, exclude)
    return data
argentpepper
sumber
Tautan sudah mati. Lain kali silakan salin kode yang relevan di sini juga.
Gus E
Akan lakukan lain kali. Jika saya ingat dengan benar, fungsinya cukup panjang.
argentpepper
2

Dengan kode ini Anda juga dapat menambahkan kueri "filter" atau "gabung" dan ini berfungsi!

query = session.query(User)
def query_to_dict(query):
        def _create_dict(r):
            return {c.get('name'): getattr(r, c.get('name')) for c in query.column_descriptions}

    return [_create_dict(r) for r in query]
Yakir Tsuberi
sumber
1
class User(object):
    def to_dict(self):
        return dict([(k, getattr(self, k)) for k in self.__dict__.keys() if not k.startswith("_")])

Itu seharusnya bekerja.

Singletoned
sumber
1
apa yang terjadi jika nama kolom dimulai dengan "_"?
Anurag Uniyal
4
Saya akan membayangkan bahwa Anda seharusnya tidak memberi nama kolom Anda dengan garis bawah utama. Jika Anda melakukannya, itu tidak akan berhasil. Jika hanya yang aneh, yang Anda ketahui, Anda dapat memodifikasinya untuk menambahkan kolom tersebut.
Singletoned
1

Saya memiliki variasi pada jawaban Marco Mariani, yang dinyatakan sebagai dekorator. Perbedaan utama adalah bahwa ia akan menangani daftar entitas, serta dengan aman mengabaikan beberapa jenis nilai pengembalian lainnya (yang sangat berguna saat menulis tes menggunakan mengejek):

@decorator
def to_dict(f, *args, **kwargs):
  result = f(*args, **kwargs)
  if is_iterable(result) and not is_dict(result):
    return map(asdict, result)

  return asdict(result)

def asdict(obj):
  return dict((col.name, getattr(obj, col.name))
              for col in class_mapper(obj.__class__).mapped_table.c)

def is_dict(obj):
  return isinstance(obj, dict)

def is_iterable(obj):
  return True if getattr(obj, '__iter__', False) else False
Chris R
sumber
1

Untuk melengkapi jawaban @Anurag Uniyal, berikut adalah metode yang akan mengikuti hubungan secara rekursif:

from sqlalchemy.inspection import inspect

def to_dict(obj, with_relationships=True):
    d = {}
    for column in obj.__table__.columns:
        if with_relationships and len(column.foreign_keys) > 0:
             # Skip foreign keys
            continue
        d[column.name] = getattr(obj, column.name)

    if with_relationships:
        for relationship in inspect(type(obj)).relationships:
            val = getattr(obj, relationship.key)
            d[relationship.key] = to_dict(val) if val else None
    return d

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    first_name = Column(TEXT)
    address_id = Column(Integer, ForeignKey('addresses.id')
    address = relationship('Address')

class Address(Base):
    __tablename__ = 'addresses'
    id = Column(Integer, primary_key=True)
    city = Column(TEXT)


user = User(first_name='Nathan', address=Address(city='Lyon'))
# Add and commit user to session to create ids

to_dict(user)
# {'id': 1, 'first_name': 'Nathan', 'address': {'city': 'Lyon'}}
to_dict(user, with_relationship=False)
# {'id': 1, 'first_name': 'Nathan', 'address_id': 1}
nbarraille
sumber
dalam hal default untuk 'with_relationships' diubah menjadi false, lebih baik sampaikan nilai ini ke panggilan rekursif. yaitu: d[relationship.key] = to_dict(val,with_relationships) if val else None
Nicholas Hamilton
bagaimana saya bisa mencapai hasil, jika saya ingin bergabung dengan pengguna dan tabel alamat berdasarkan kolom address_id dan mengambil semua kolom dari tabel pengguna dan hanya kolom id dari tabel alamat.
Sudhakar
1

Dengan python 3.8+, kita bisa melakukan ini dengan dataclass, dan asdictmetode yang menyertainya:

from dataclasses import dataclass, asdict

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy import Column, String, Integer, create_engine

Base = declarative_base()
engine = create_engine('sqlite:///:memory:', echo=False)


@dataclass
class User(Base):
    __tablename__ = 'users'

    id: int = Column(Integer, primary_key=True)
    name: str = Column(String)
    email = Column(String)

    def __init__(self, name):
        self.name = name
        self.email = '[email protected]'


Base.metadata.create_all(engine)

SessionMaker = sessionmaker(bind=engine)
session = SessionMaker()

user1 = User("anurag")
session.add(user1)
session.commit()

query_result = session.query(User).one()  # type: User
print(f'{query_result.id=:}, {query_result.name=:}, {query_result.email=:}')
# query_result.id=1, query_result.name=anurag, [email protected]

query_result_dict = asdict(query_result)
print(query_result_dict)
# {'id': 1, 'name': 'anurag'}

Kuncinya adalah menggunakan @dataclassdekorator, dan membubuhi keterangan setiap kolom dengan tipenya ( : strbagian dari name: str = Column(String)garis).

Perhatikan juga bahwa karena emailtidak dijelaskan, itu tidak termasuk dalam query_result_dict.

toaruScar
sumber
Pada Python3.7 saya mendapatkan "NameError: nama 'asdict' tidak didefinisikan"
devnull
Salahku! Ini adalah fungsi yang ditambahkan dalam python 3.8. Memperbaiki jawaban saya.
toaruScar
Sangat pythonic. 3.8 mengagumkan. Tetapi Anda tidak benar-benar membutuhkan metode init, bukan? deklaratif dan dataclass keduanya menyediakan metode init generik.
Jeff Laughlin
0

Saya seorang programmer Python yang baru dicetak dan mengalami masalah mendapatkan ke JSON dengan tabel Bergabung. Dengan menggunakan informasi dari jawaban di sini saya membangun sebuah fungsi untuk mengembalikan hasil yang masuk akal ke JSON di mana nama tabel dimasukkan untuk menghindari keharusan alias, atau membuat bidang bertabrakan.

Cukup berikan hasil kueri sesi:

test = Session (). query (VMInfo, Customer) .join (Customer) .order_by (VMInfo.vm_name) .limit (50) .offset (10)

json = sqlAl2json (tes)

def sqlAl2json(self, result):
    arr = []
    for rs in result.all():
        proc = []
        try:
            iterator = iter(rs)
        except TypeError:
            proc.append(rs)
        else:
            for t in rs:
                proc.append(t)

        dict = {}
        for p in proc:
            tname = type(p).__name__
            for d in dir(p):
                if d.startswith('_') | d.startswith('metadata'):
                    pass
                else:
                    key = '%s_%s' %(tname, d)
                    dict[key] = getattr(p, d)
        arr.append(dict)
    return json.dumps(arr)
tknightowl
sumber
0

jika kolom tabel model Anda bukan kolom mysql equie.

seperti :

class People:
    id: int = Column(name='id', type_=Integer, primary_key=True)
    createdTime: datetime = Column(name='create_time', type_=TIMESTAMP,
                               nullable=False,
                               server_default=text("CURRENT_TIMESTAMP"),
                               default=func.now())
    modifiedTime: datetime = Column(name='modify_time', type_=TIMESTAMP,
                                server_default=text("CURRENT_TIMESTAMP"),
                                default=func.now())

Perlu digunakan:

 from sqlalchemy.orm import class_mapper 
 def asDict(self):
        return {x.key: getattr(self, x.key, None) for x in
            class_mapper(Application).iterate_properties}

jika Anda menggunakan cara ini, Anda bisa mendapatkan modified_time dan create_time keduanya adalah None

{'id': 1, 'create_time': None, 'modify_time': None}


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

Karena nama Atribut Kelas tidak sama dengan penyimpanan kolom di mysql

张小诚
sumber
0

Kembalikan konten ini: kelas: .KeyedTuplesebagai kamus

In [46]: result = aggregate_events[0]

In [47]: type(result)
Out[47]: sqlalchemy.util._collections.result

In [48]: def to_dict(query_result=None):
    ...:     cover_dict = {key: getattr(query_result, key) for key in query_result.keys()}
    ...:     return cover_dict
    ...: 
    ...:     

In [49]: to_dict(result)
Out[49]: 
{'calculate_avg': None,
 'calculate_max': None,
 'calculate_min': None,
 'calculate_sum': None,
 'dataPointIntID': 6,
 'data_avg': 10.0,
 'data_max': 10.0,
 'data_min': 10.0,
 'data_sum': 60.0,
 'deviceID': u'asas',
 'productID': u'U7qUDa',
 'tenantID': u'CvdQcYzUM'}
Eds_k
sumber
0

Demi semua orang dan saya sendiri, inilah cara saya menggunakannya:

def run_sql(conn_String):
  output_connection = engine.create_engine(conn_string, poolclass=NullPool).connect()
  rows = output_connection.execute('select * from db1.t1').fetchall()  
  return [dict(row) for row in rows]
manusia
sumber
0
def to_dict(row):
    return {column.name: getattr(row, row.__mapper__.get_property_by_column(column).key) for column in row.__table__.columns}


for u in session.query(User).all():
    print(to_dict(u))

Fungsi ini mungkin membantu. Saya tidak dapat menemukan solusi yang lebih baik untuk menyelesaikan masalah ketika nama atribut berbeda dari nama kolom.

Александр Немиров
sumber
0

Anda akan membutuhkannya di mana-mana dalam proyek Anda, saya apriciate @anurag menjawabnya berfungsi dengan baik. sampai saat ini saya menggunakannya, tetapi itu akan mengacaukan semua kode Anda dan juga tidak akan bekerja dengan perubahan entitas.

Alih-alih mencoba ini, mewarisi kelas kueri basis Anda di SQLAlchemy

from flask_sqlalchemy import SQLAlchemy, BaseQuery


class Query(BaseQuery):
    def as_dict(self):
        context = self._compile_context()
        context.statement.use_labels = False
        columns = [column.name for column in context.statement.columns]

        return list(map(lambda row: dict(zip(columns, row)), self.all()))


db = SQLAlchemy(query_class=Query)

setelah itu di mana pun Anda akan mendefinisikan objek Anda "as_dict" metode akan ada di sana.

Yash Pokar
sumber
-1

Dalam sebagian besar skenario, nama kolom cocok untuk mereka. Tetapi mungkin Anda menulis kode seperti berikut:

class UserModel(BaseModel):
    user_id = Column("user_id", INT, primary_key=True)
    email = Column("user_email", STRING)

the column.name "user_email" sementara nama field adalah "email", kolom.name tidak bisa berfungsi dengan baik seperti sebelumnya.

sqlalchemy_base_model.py

Saya juga menulis jawabannya di sini

kaka_ace
sumber