แปลงวัตถุแถว sqlalchemy เพื่อหลามพจน์


240

มีวิธีง่ายๆในการวนซ้ำของชื่อคอลัมน์และคู่ค่าหรือไม่?

เวอร์ชั่น sqlalchemy ของฉันคือ 0.5.6

นี่คือตัวอย่างรหัสที่ฉันพยายามใช้ dict (แถว) แต่มันมีข้อยกเว้น TypeError: วัตถุ 'ผู้ใช้' ไม่สามารถทำซ้ำได้

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)

ใช้รหัสนี้ในระบบของฉันออก:

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

3
ชื่อของคำถามไม่ตรงกับคำถาม ตามเอกสาร แถวผลลัพธ์ที่ส่งคืนโดยแบบสอบถามที่มีเอนทิตี ORM หลายรายการและ / หรือนิพจน์คอลัมน์ใช้ประโยชน์จากคลาสนี้เพื่อส่งคืนแถว โดยที่คลาสนี้sqlalchemy.util.KeyedTupleซึ่งเป็นวัตถุแถวจากชื่อของคำถาม อย่างไรก็ตามแบบสอบถามในรูปแบบการใช้คำถาม (แมป) ระดับจึงประเภทของวัตถุแถวsqlalchemy.util.KeyedTupleเป็นชั้นรูปแบบแทน
Piotr Dobrogost

2
@PiotrDobrogost คำถามมาจาก 2009 และกล่าวถึงรุ่น sqlalchemy 0.5.6
Anurag Uniyal

คำตอบ:


232

คุณสามารถเข้าถึงภายใน__dict__ของวัตถุ SQLAlchemy เช่นต่อไปนี้ ::

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

24
คำตอบที่ดีที่สุดในชุดนี้ไม่ทราบว่าทำไมคนอื่น ๆ จึงเสนอวิธีแก้ปัญหาที่ซับซ้อนมากขึ้น
Dave Rawks

92
สิ่งนี้จะให้ฟิลด์ '_sa_instance_state' พิเศษอย่างน้อยในเวอร์ชัน 0.7.9
elbear

21
ดังนั้นนี้จะดีกว่า:dictret = dict(row.__dict__); dictret.pop('_sa_instance_state', None)
ลิฟิง

6
สิ่งนี้ดูเหมือนไม่เหมาะเนื่องจากผู้คนได้ชี้ให้เห็นว่า__dict__มี_sa_instance_stateรายการที่จะต้องถูกลบออก หากคุณอัปเกรดเป็นเวอร์ชันในอนาคตและเพิ่มคุณสมบัติอื่น ๆ คุณอาจต้องย้อนกลับและจัดการกับมันด้วยตนเอง ถ้าคุณต้องการเพียงแค่ข้อมูลคอลัมน์ (ตัวอย่างเช่นในการรับรายการอินสแตนซ์จากแบบสอบถามและวางลงในดาต้าดาต้าแพนด้า) แล้ว{col.name: getattr(self, col.name) for col in self.__table__.columns}ตามที่ตอบโดย Anurag Uniyal (ด้วยการแก้ไขที่สำคัญจากความคิดเห็นต่อคำตอบนั้น) ดูเหมือนว่ารวบรัดและผิดพลาดมากกว่า พิสูจน์
kilgoretrout

14
คำตอบนี้ผิด คำถามแม้มีและถูกต้องกล่าวว่ามันพ่นdict(u)TypeError
RazerM

146

ฉันไม่ได้รับคำตอบที่ดีดังนั้นฉันจึงใช้สิ่งนี้:

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

    return d

แก้ไข: หากฟังก์ชั่นด้านบนยาวเกินไปและไม่เหมาะกับบางรสนิยมนี่คือหนึ่งซับ (python 2.7+)

row2dict = lambda r: {c.name: str(getattr(r, c.name)) for c in r.__table__.columns}

17
ชัดถ้อยชัดคำมากขึ้น, return dict((col, getattr(row, col)) for col in row.__table__.columns.keys()).
argentpepper

4
@argentpepper ใช่คุณอาจทำrow2dict = lambda row: dict((col, getattr(row, col)) for col in row.__table__.columns.keys())เพื่อให้เป็นหนึ่งซับจริง ๆ แต่ฉันชอบรหัสของฉันที่จะอ่านสั้นแนวนอนยาวแนวตั้ง
Anurag Uniyal

14
จะเกิดอะไรขึ้นหากคอลัมน์ของฉันไม่ได้ถูกกำหนดให้กับแอตทริบิวต์ที่มีชื่อเดียวกัน IE, x = Column('y', Integer, primary_key=True)? ไม่มีวิธีแก้ไขปัญหาเหล่านี้ในกรณีนี้
Buttons840

13
drdaeman เป็นสิทธิที่นี่เป็นข้อมูลที่ถูกต้อง:return {c.name: getattr(self, c.name) for c in self.__table__.columns}
charlax

5
คำตอบนี้ทำให้สมมติฐานที่ไม่ถูกต้อง: ชื่อคอลัมน์ไม่ตรงกับชื่อแอตทริบิวต์
RazerM

94

ตาม @zzzeek ในความคิดเห็น:

โปรดทราบว่านี่เป็นคำตอบที่ถูกต้องสำหรับ SQLAlchemy เวอร์ชันใหม่สมมติว่า "row" เป็นวัตถุแถวแกนไม่ใช่อินสแตนซ์ที่แมป ORM

for row in resultproxy:
    row_as_dict = dict(row)

13
มันบอกว่า 'วัตถุ XXX ไม่สามารถทำซ้ำได้' ฉันใช้ 0.5.6 ฉันได้รับจาก session.query (Klass) .filter (). all ()
Anurag Uniyal

60
โปรดทราบว่านี่เป็นคำตอบที่ถูกต้องสำหรับ SQLAlchemy เวอร์ชั่นใหม่สมมติว่า "row" เป็นวัตถุแถวแกนไม่ใช่อินสแตนซ์ที่แมป ORM
zzzeek

48
โปรดทราบด้วยว่า zzzeek เป็นผู้สร้าง sqlalchemy
Chris

1
ใครสามารถอธิบายรายละเอียดเกี่ยวกับสิ่งนี้? ฉันเป็นคนที่ไม่ได้รับสิ่งนี้
lameei

1
ความแตกต่างระหว่างวัตถุแถวหลักกับอินสแตนซ์ที่แมป ORM คืออะไร? สิ่งนี้ใช้ไม่ได้กับฉันในแถวจากquery(MyModel).all(): วัตถุ MyModel ไม่สามารถทำซ้ำได้
Jonathan Hartley

81

ใน SQLAlchemy v0.8 และใหม่กว่าใช้ระบบการตรวจสอบ

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)

โปรดทราบว่า.keyเป็นชื่อแอตทริบิวต์ซึ่งอาจแตกต่างจากชื่อคอลัมน์เช่นในกรณีดังต่อไปนี้:

class_ = Column('class', Text)

column_propertyวิธีนี้ยังสามารถใช้ได้กับ


@DukeDougal ฉันคิดว่ามันใช้งานได้จาก v0.8 (เมื่อมีการเพิ่มระบบการตรวจสอบ)
RazerM

1
ใช้งานได้กับ Sqlalchemy v2.0 คำตอบอื่น ๆ ไม่ได้
Thanh Nguyen

สิ่งนี้ไม่คำนึงถึงคอลัมน์รอการตัดบัญชี
ทำเครื่องหมาย

1
@ Mark มันไม่ชัดเจนสำหรับฉันที่ควรยกเว้นโดยค่าเริ่มต้น อย่างไรก็ตามคุณสามารถตรวจสอบว่ากุญแจไม่ได้อยู่ในsqlalchemy.inspect(obj).unloaded
RazerM

5
@ NguyenThanh การทำงานกับ SQLAlchemy v2.0 นั้นน่าประทับใจเป็นพิเศษเพราะไม่มีตัวตน! รุ่นล่าสุด (เบต้า) คือ v1.3.0b1
Mark Amery

39

แถวมี_asdict()ฟังก์ชั่นที่ให้ 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

2
@balki มันค่อนข้างดีและเอกสารดังกล่าวไม่ได้ค่อนข้างเป็นส่วนตัว แม้ว่าขีดเส้นใต้ชั้นนำจะมีความหมายเช่นนั้นใน Python โดยทั่วไปนี่อาจใช้เพื่อไม่ให้เกิดการขัดแย้งกับปุ่มทูเปิลที่เป็นไปได้
Ilja Everilä

5
สิ่งนี้ใช้ได้กับ KeyedTuple s เท่านั้นซึ่งจะถูกส่งคืนเฉพาะเมื่อทำการสอบถามฟิลด์เฉพาะของแถว เช่น .query (Topic.name) จะส่งกลับ KeyedTuple ในขณะที่. Query (หัวข้อ) จะส่งคืนหัวข้อซึ่งไม่มี. _asdict () - Derp เพิ่งเห็นคำตอบของ STB ด้านล่าง
Chad Lowe

20

ตามที่ @balki พูดถึง:

_asdict()วิธีสามารถนำมาใช้ถ้าคุณกำลังสอบถามข้อมูลที่เฉพาะเจาะจงเพราะมันจะถูกส่งกลับเป็นKeyedTuple

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

โดยที่ถ้าคุณไม่ระบุคอลัมน์คุณสามารถใช้หนึ่งในวิธีการอื่น ๆ ที่เสนอ - เช่นที่มีให้โดย @charlax โปรดทราบว่าวิธีนี้ใช้ได้กับ 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'}

หากแอ็ตทริบิวต์ python ORM คลาสมีชื่อแตกต่างจากคอลัมน์ฐานข้อมูลให้ลองใช้วิธีแก้ไขปัญหานี้: stackoverflow.com/questions/27947294/…
TaiwanGrapefruitTea

2
ที่จริงแล้วทางออกที่ดีกว่าสำหรับทุกกรณีนั้นมีให้โดยผู้เขียน sqlalchemy ที่stackoverflow.com/a/27948279/1023033
TaiwanGrapefruitTea

เมื่อฉันลองทำสิ่งนี้ฉันจะได้รับออบเจกต์ ResultProxy ไม่มีแอตทริบิวต์ '_asdict'
slashdottir

@slashdottir คุณกำลังดำเนินการวัตถุแบบสอบถามของคุณ (เรียก.first()วิธีการ)?
Sam Bourne

1
คำตอบนี้ทำให้สมมติฐานไม่ถูกต้อง: ชื่อคอลัมน์ไม่ตรงกับชื่อแอตทริบิวต์ - ดูคำตอบของ RazerM
Piotr Dobrogost

18

คำถามเก่า แต่ตั้งแต่นี้ผลลัพธ์แรกสำหรับ "sqlalchemy row to dict" ใน Google มันสมควรได้รับคำตอบที่ดีกว่า

วัตถุ RowProxy ที่ SqlAlchemy ส่งคืนมีเมธอด items (): http://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.RowProxy.items

มันจะส่งคืนรายการของ tuples (คีย์ค่า) ดังนั้นหนึ่งสามารถแปลงแถวเพื่อเขียนตามคำบอกโดยใช้ต่อไปนี้:

ใน Python <= 2.6:

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

ใน Python> = 2.7:

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

13
คุณสามารถทำได้ list_of_dicts = [dict(row.items()) for row in rows]
Markus Meskanen

หนึ่งอุปสรรคคือชื่อคอลัมน์ที่ SQLAlchemy ใช้ในชุดผลลัพธ์คือtable_name_column_nameถ้าคุณต้องการชื่อที่แตกต่าง (เช่นเพียงcolumn_name) ใช้.labelเมธอดsession.query( MyTable.column_name.label('column_name'), ... )
Aneel

สวัสดีฉันได้รับปัญหานี้โปรดช่วยฉัน * datetime.datetime (2018, 11, 24, 18, 52, 50) ไม่ใช่ JSON ต่อเนื่องได้ *
Saravanan Nandhan

13

สมมติว่าฟังก์ชั่นต่อไปนี้จะถูกเพิ่มในclass Userรายการต่อไปนี้จะส่งคืนคู่คีย์ - ค่าทั้งหมดของคอลัมน์ทั้งหมด:

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

ต่างจากคำตอบอื่น ๆ ทั้งหมด แต่จะส่งคืนเฉพาะคุณสมบัติของวัตถุซึ่งเป็นColumnคุณลักษณะที่ระดับชั้นของวัตถุ ดังนั้นจึงไม่มีSQLalchemyหรือ_sa_instance_stateคุณลักษณะอื่นใดหรือคุณเพิ่มลงในวัตถุ การอ้างอิง

แก้ไข: ลืมที่จะพูดว่านี้ยังทำงานในคอลัมน์ที่สืบทอด

hybrid_propery ขยาย

หากคุณต้องการรวมhybrid_propertyคุณสมบัติดังต่อไปนี้จะทำงาน:

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_

ฉันสมมติที่นี่ว่าคุณทำเครื่องหมายคอลัมน์ด้วยจุดเริ่มต้น_เพื่อระบุว่าคุณต้องการซ่อนพวกเขาเพราะคุณเข้าถึงคุณลักษณะโดยhybrid_propertyหรือคุณเพียงแค่ไม่ต้องการแสดง การอ้างอิง

Tipp all_orm_descriptorsจะส่งกลับไฮบริดสลีเมธอดและAssociationProxyหากคุณต้องการรวมไว้ด้วย

พูดถึงคำตอบอื่น ๆ

ทุกคำตอบ (เช่น 1 , 2 ) ซึ่งยึดตาม__dict__แอ็ตทริบิวต์จะส่งคืนคุณลักษณะทั้งหมดของวัตถุ นี่อาจเป็นแอตทริบิวต์เพิ่มเติมอีกมากมายที่คุณต้องการ เช่นเดียวกับที่ฉันเศร้าสิ่งนี้รวมถึง_sa_instance_stateหรือคุณลักษณะอื่น ๆ ที่คุณกำหนดไว้ในวัตถุนี้

ทุกคำตอบ (เช่น1 , 2 ) ซึ่งขึ้นอยู่กับdict()ฟังก์ชั่นจะทำงานเฉพาะกับวัตถุแถวSQLalchemyที่ส่งคืนโดยsession.execute()ไม่ได้อยู่ในคลาสที่คุณกำหนดให้ทำงานด้วยเช่นclass Userกับคำถาม

คำตอบการแก้ซึ่งอยู่บนพื้นฐานrow.__table__.columnsแน่นอนจะไม่ทำงาน row.__table__.columnsมีชื่อคอลัมน์ของฐานข้อมูล SQL สิ่งเหล่านี้สามารถเท่ากับชื่อแอตทริบิวต์ของวัตถุหลาม AttributeErrorถ้าไม่ได้คุณจะได้รับ สำหรับคำตอบ (เช่น1 , 2 ) ขึ้นอยู่กับclass_mapper(obj.__class__).mapped_table.cว่ามันเหมือนกัน


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)

4
ระวังความแตกต่างระหว่าง local_table และ mapped_table ตัวอย่างเช่นถ้าคุณใช้การสืบทอดตารางบางประเภทใน db (tbl_employees> tbl_managers, tbl_employees> tbl_staff) คลาสที่แม็พของคุณจะต้องแสดงถึงสิ่งนี้ (ผู้จัดการ (พนักงาน), พนักงาน (พนักงาน)) mapped_table.c จะให้ชื่อคอลัมน์ของคุณทั้งในตารางฐานและตารางสืบทอด local_table ให้ชื่อของตาราง (สืบทอด) ของคุณเท่านั้น
Michael Ekoka

วิธีนี้จะหลีกเลี่ยงการให้ฟิลด์ '_sa_instance_state' อย่างน้อยในเวอร์ชัน 0.8+
Evan Siroky

4
คำตอบนี้ทำให้สมมติฐานที่ไม่ถูกต้อง: ชื่อคอลัมน์ไม่ตรงกับชื่อแอตทริบิวต์
RazerM

11

ทำตามคำตอบ @balki เนื่องจาก SQLAlchemy 0.8คุณสามารถใช้ _asdict () พร้อมใช้งานสำหรับวัตถุ KeyedTuple สิ่งนี้แสดงคำตอบที่ตรงไปตรงมาสำหรับคำถามต้นฉบับ เพียงแค่เปลี่ยนตัวอย่างสองบรรทัดสุดท้าย (สำหรับ for loop) สำหรับตัวอย่างนี้:

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

ใช้งานได้เนื่องจากในโค้ดข้างต้น u เป็นวัตถุประเภทคลาสKeyedTupleเนื่องจาก. all () ส่งคืนรายการของ KeyedTuple ดังนั้นมันจึงมีเมธอด_asdict ()ซึ่งส่งคืน u เป็นพจนานุกรมอย่างดี

WRT คำตอบโดย @STB: AFAIK ซึ่งเป็นผลตอบแทนที่. all () คือรายการของ KeypedTuple ดังนั้นการทำงานด้านบนถ้าคุณระบุคอลัมน์หรือไม่ตราบใดที่คุณจัดการกับผลลัพธ์ของ. all () ตามที่ใช้กับวัตถุ Query


6
สิ่งนี้อาจเป็นจริงในอดีต แต่ใน SQLAlchemy v1.0 .all()จะส่งคืนรายการUserอินสแตนซ์ดังนั้นจึงใช้งานไม่ได้
RazerM

@RazerM ขออภัย แต่ฉันไม่เข้าใจว่าคุณหมายถึงอะไร สำหรับการวนรอบควรวนซ้ำอย่างแม่นยำในรายการอินสแตนซ์ผู้ใช้แปลงพวกเขา (u) เป็นพจนานุกรมแล้วพิมพ์พวกเขา ...
jgbarah

3
Userอินสแตนซ์ไม่มี_asdictวิธีการ ดูgist.github.com/RazerM/2eff51571b3c70e8aeecd303c2a2bc8d
RazerM

2
ตอนนี้ฉันเข้าใจแล้ว. ขอบคุณ แทน KeyedTuple ตอนนี้. all () จะส่งคืนวัตถุผู้ใช้ ดังนั้นปัญหาสำหรับ v1.0 (และสูงกว่า, ฉันคิดว่า) คือวิธีนำพจนานุกรมออกจากวัตถุผู้ใช้ ขอขอบคุณสำหรับการชี้แจง.
jgbarah

10

นิพจน์ที่คุณกำลังวนซ้ำจะประเมินค่าเป็นรายการของโมเดลวัตถุไม่ใช่แถว ดังนั้นต่อไปนี้คือการใช้งานที่ถูกต้องของพวกเขา:

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

คุณต้องการแปลงเป็น dicts จริงหรือไม่ แน่นอนว่ามีหลายวิธี แต่คุณไม่ต้องการ ORM ส่วนหนึ่งของ SQLAlchemy:

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

อัปเดต : ดูsqlalchemy.orm.attributesโมดูล instance_dict()มันมีชุดของฟังก์ชั่นการทำงานกับรัฐวัตถุที่อาจเป็นประโยชน์สำหรับคุณโดยเฉพาะอย่างยิ่ง


2
ฉันต้องการที่จะแปลงให้ Dict ไปเพราะบางรหัสอื่น ๆ ที่ต้องการข้อมูลเป็น Dict และฉันต้องการวิธีทั่วไปเพราะผมจะไม่ทราบว่าคอลัมน์วัตถุรุ่นมี
อนุรักษ์ Uniyal

และเมื่อฉันได้รับการจัดการกับพวกเขาฉันสามารถเข้าถึงวัตถุแบบจำลองเท่านั้นดังนั้นฉันไม่สามารถใช้ session.execute และอื่น ๆ
Anurag Uniyal

8

อ้างถึงคำตอบของ Alex Brasetvikคุณสามารถใช้รหัสหนึ่งบรรทัดเพื่อแก้ปัญหา

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

ภายใต้ส่วนความคิดเห็นของคำตอบของ Alex Brasetvik, zzzeek ผู้สร้าง SQLAlchemy ระบุว่านี่เป็น "วิธีการแก้ไข" สำหรับปัญหา


1
@Greenonline แน่นอนความคิดเห็นการอนุมัติอยู่ภายใต้คำตอบของ Alex Brasetvik แก้ไขเพื่อเพิ่มลิงก์ไปยังคำตอบของเขา
NorWay

คือresultproxyอะไร
lameei

8

คุณสามารถลองทำด้วยวิธีนี้

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

มันใช้วิธีการในตัวในวัตถุแบบสอบถามที่ส่งกลับวัตถุ dictonary ของวัตถุแบบสอบถาม

การอ้างอิง: https://docs.sqlalchemy.org/en/latest/orm/query.html


1
เพิ่มคำอธิบายเพิ่มเติมหรือไม่
ทิว

1
ไม่มีอะไรจะอธิบายได้อีกแล้ว มันเป็นวิธีการในตัวของวัตถุผลลัพธ์ ดังนั้นไม่ว่าคุณจะทำสิ่งนี้เพื่อผลการค้นหาทั้งหมดหรือแถวเดียวมี_asdict()วิธีการในตัวที่เป็นหลักในการบีบชื่อฟิลด์ด้วยค่าฟิลด์และส่งคืนผลลัพธ์เป็นพจนานุกรม
Matthew

รัดกุมมากและฉันหวังว่ามันจะทำงานได้ แต่uในกรณีของฉันคือสตริงและฉันได้รับข้อผิดพลาด `` วัตถุแบบ 'ไม่มีแอตทริบิวต์' _asdict'` @hllau ด้านล่างใช้งานได้สำหรับฉัน
Mote Zart

7

ฉันได้พบโพสต์นี้เพราะฉันกำลังมองหาวิธีการแปลงแถว SQLAlchemy เป็น dict ฉันใช้ SqlSoup ... แต่คำตอบนั้นถูกสร้างขึ้นด้วยตัวเองดังนั้นถ้ามันสามารถช่วยคนที่นี่เป็นสองเซ็นต์ของฉัน:

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

# and now, finally...
dict(zip(c.keys(), c.values()))

1
หรือหากคุณต้องการ .. : [dict (zip (i.keys (), i.values ​​())) สำหรับ i in b]
Mychot เศร้า

นี่เป็นไวยากรณ์เดียวที่ฉันพบว่าใช้งานได้จริง! ฉันพยายามทำสิ่งต่างๆนานกว่าหนึ่งชั่วโมง
slashdottir

สำหรับการเลือกคอร์นั้นRowProxy( cในคำตอบนี้) จะปฏิบัติตามโปรโตคอลการจับคู่ดังนั้นคุณจึงสามารถโทรdict(c)ได้
RazerM

4

คุณสามารถแปลงวัตถุ sqlalchemy เป็นพจนานุกรมเช่นนี้และส่งคืนเป็น json / dictionary

ฟังก์ชั่นผู้ช่วย:

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) 

ฟังก์ชั่นไดร์เวอร์:

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

3

สองทาง:

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


2

นี่คือสิ่งที่ Elixir ทำ มูลค่าของโซลูชันนี้คืออนุญาตให้เรียกซ้ำรวมถึงการแสดงความสัมพันธ์แบบพจนานุกรม

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

ลิงค์เสียชีวิต ครั้งต่อไปโปรดคัดลอกรหัสที่เกี่ยวข้องที่นี่เช่นกัน
Gus E

จะทำในครั้งต่อไป ถ้าฉันจำได้ถูกต้องฟังก์ชั่นนั้นค่อนข้างนาน
argentpepper

2

ด้วยรหัสนี้คุณสามารถเพิ่มคำค้นหา "ตัวกรอง" หรือ "เข้าร่วม" ของคุณและใช้งานได้!

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]

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

ว่าควรจะทำงาน


1
จะเกิดอะไรขึ้นหากชื่อคอลัมน์เริ่มต้นด้วย "_"
Anurag Uniyal

4
ฉันคิดว่าคุณไม่ควรตั้งชื่อคอลัมน์ของคุณด้วยเครื่องหมายขีดเส้นใต้ ถ้าคุณทำมันจะไม่ทำงาน หากเป็นเพียงคี่ที่คุณรู้คุณสามารถแก้ไขเพื่อเพิ่มคอลัมน์เหล่านั้น
ซิงเกิลที่

1

ฉันเปลี่ยนแปลงคำตอบของ Marco Mariani ซึ่งแสดงออกเป็นมัณฑนากร ความแตกต่างที่สำคัญคือมันจะจัดการรายการของเอนทิตีเช่นเดียวกับการละเว้นค่าตอบแทนประเภทอื่น ๆ (ซึ่งมีประโยชน์มากเมื่อเขียนการทดสอบโดยใช้ mocks):

@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

1

หากต้องการตอบ @Anurag Uniyal ให้สำเร็จนี่เป็นวิธีการที่จะติดตามความสัมพันธ์แบบวนซ้ำ:

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}

ในกรณีที่ค่าเริ่มต้นสำหรับ 'with_relationships' เปลี่ยนเป็นเท็จให้ส่งค่านี้ไปยังการเรียกซ้ำ เช่น: d[relationship.key] = to_dict(val,with_relationships) if val else None
Nicholas Hamilton

ฉันจะบรรลุผลได้อย่างไรถ้าฉันต้องการเข้าร่วมผู้ใช้และตารางที่อยู่ตามคอลัมน์ address_id และดึงข้อมูลคอลัมน์ทั้งหมดจากตารางผู้ใช้และคอลัมน์ id เท่านั้นจากตารางที่อยู่
Sudhakar

1

ด้วย python 3.8+ เราสามารถใช้ดาต้าคลาสและasdictวิธีการที่มาพร้อมกับมัน:

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 = 'hello@example.com'


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, query_result.email=hello@example.com

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

กุญแจสำคัญคือการใช้@dataclassมัณฑนากรและใส่คำอธิบายประกอบแต่ละคอลัมน์ด้วยประเภทของมัน ( : strส่วนหนึ่งของname: str = Column(String)บรรทัด)

นอกจากนี้ยังทราบว่าตั้งแต่ไม่ได้ข้อเขียนก็ไม่ได้รวมอยู่ในemailquery_result_dict


ใน Python3.7 ฉันได้รับ "NameError: ไม่ได้กำหนดชื่อ 'asdict'"
devnull

ความผิดฉันเอง! เป็นฟังก์ชั่นที่เพิ่มเข้ามาใน python 3.8 แก้ไขคำตอบของฉัน
toaruScar

ดังนั้นไพ ธ อน 3.8 ยอดเยี่ยมมาก แต่คุณไม่ต้องการวิธีการเริ่มต้นจริงเหรอ? declarative และ dataclass จัดเตรียมเมธอด init ทั่วไป
Jeff Laughlin

0

ฉันเป็นโปรแกรมเมอร์ Python ที่เพิ่งทำเสร็จใหม่และพบปัญหาในการเข้าสู่ JSON ด้วยตารางที่เข้าร่วม การใช้ข้อมูลจากคำตอบที่นี่ฉันสร้างฟังก์ชันเพื่อส่งคืนผลลัพธ์ที่สมเหตุสมผลไปยัง JSON โดยที่ชื่อตารางถูกรวมไว้โดยหลีกเลี่ยงการใช้นามแฝงหรือมีการชนกันของฟิลด์

เพียงส่งผลลัพธ์ของการค้นหาเซสชัน:

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

json = sqlAl2json (ทดสอบ)

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)

0

หากคอลัมน์ตารางโมเดลของคุณไม่ใช่คอลัมน์ mysql

เช่น:

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

จำเป็นต้องใช้:

 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}

ถ้าคุณใช้วิธีนี้คุณจะได้รับ modified_time และ create_time ทั้งคู่เป็น 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}

เนื่องจากชื่อแอตทริบิวต์ของคลาสไม่เท่ากันกับที่จัดเก็บคอลัมน์ใน mysql


0

ส่งคืนเนื้อหาของสิ่งนี้: class: .KeyedTupleเป็นพจนานุกรม

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

0

เพื่อประโยชน์ของทุกคนและตัวฉันเองนี่คือวิธีที่ฉันใช้:

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]

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

ฟังก์ชั่นนี้อาจช่วยได้ ฉันไม่สามารถหาทางออกที่ดีกว่าในการแก้ปัญหาเมื่อชื่อแอตทริบิวต์แตกต่างจากชื่อคอลัมน์


0

คุณจะต้องการมันทุกที่ในโครงการของคุณฉันขอโทษ @anurag ตอบว่าใช้ได้ดี จนถึงจุดนี้ฉันใช้มัน แต่มันจะเลอะรหัสของคุณทั้งหมดและจะไม่ทำงานกับการเปลี่ยนเอนทิตี

ลองทำเช่นนี้สืบทอดคลาสคิวรีฐานของคุณใน 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)

หลังจากนั้นทุกที่ที่คุณจะกำหนดวิธีการ "as_dict" วัตถุของคุณจะมี


-1

ในสถานการณ์ส่วนใหญ่ชื่อคอลัมน์จะเหมาะสำหรับพวกเขา แต่บางทีคุณอาจเขียนโค้ดดังนี้:

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

column.name "user_email" ในขณะที่ชื่อฟิลด์คือ "email", column.name อาจทำงานได้ไม่ดีเหมือนเมื่อก่อน

sqlalchemy_base_model.py

ฉันยังเขียนคำตอบที่นี่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.