Chuyển đổi đối tượng hàng sqlalchemy thành python dict


240

Có cách nào đơn giản để lặp lại các cặp giá trị và tên cột không?

Phiên bản sqlalchemy của tôi là 0,5,6

Đây là mã mẫu mà tôi đã thử sử dụng dict (hàng), nhưng nó ném ngoại lệ, đối tượng TypeError: 'Người dùng' không thể lặp lại được

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)

Chạy mã này trên đầu ra hệ thống của tôi:

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
Tiêu đề của câu hỏi không phù hợp với chính câu hỏi. Theo tài liệu Các hàng kết quả được trả về bởi Truy vấn có chứa nhiều thực thể ORM và / hoặc biểu thức cột sử dụng lớp này để trả về các hàng. trong đó lớp này sqlalchemy.util.KeyedTupleđối tượng hàng từ tiêu đề của câu hỏi. Tuy nhiên, truy vấn trong câu hỏi sử dụng lớp mô hình (ánh xạ), do đó loại đối tượng hàng là lớp mô hình thay vì sqlalchemy.util.KeyedTuple.
Piotr Dobrogost

2
@PiotrDobrogost Câu hỏi là từ năm 2009 và đề cập đến phiên bản sqlalchemy 0.5.6
Anurag Uniyal

Câu trả lời:


232

Bạn có thể truy cập vào bên trong __dict__của một đối tượng SQLAlchemy, như sau ::

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

24
Câu trả lời hay nhất trong chủ đề này, không biết tại sao mọi người khác lại đề xuất các giải pháp phức tạp hơn nhiều.
Dave Rawks

92
Điều này cung cấp thêm trường '_sa_instance_state', ít nhất là trong phiên bản 0.7.9.
elbear

21
vì vậy điều này sẽ tốt hơn:dictret = dict(row.__dict__); dictret.pop('_sa_instance_state', None)
lyfing

6
điều này có vẻ không lý tưởng vì như mọi người đã chỉ ra __dict__bao gồm một _sa_instance_statemục mà sau đó phải được loại bỏ. nếu bạn nâng cấp lên phiên bản tương lai và các thuộc tính khác được thêm vào, bạn có thể phải quay lại và tự xử lý chúng. nếu bạn chỉ muốn dữ liệu cột (ví dụ: lấy danh sách các trường hợp từ truy vấn và thả chúng vào khung dữ liệu gấu trúc) thì {col.name: getattr(self, col.name) for col in self.__table__.columns}như Anurag Uniyal đã trả lời (với các sửa đổi quan trọng từ nhận xét cho câu trả lời đó) có vẻ vừa súc tích vừa sai sót hơn- bằng chứng.
kilgoretrout

14
Câu trả lời này là sai. Câu hỏi thậm chí có dict(u)và nói chính xác rằng nó ném a TypeError.
RazerM

146

Tôi không thể có được một câu trả lời hay vì vậy tôi sử dụng nó:

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

    return d

Chỉnh sửa: nếu chức năng trên quá dài và không phù hợp với một số sở thích thì đây là một lớp lót (python 2.7+)

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

17
Ngắn gọn hơn , return dict((col, getattr(row, col)) for col in row.__table__.columns.keys()).
argentpepper

4
@argentpepper vâng, bạn thậm chí có thể làm row2dict = lambda row: dict((col, getattr(row, col)) for col in row.__table__.columns.keys())để biến nó thành một lớp lót thực sự, nhưng tôi thích mã của mình có thể đọc được, ngắn theo chiều ngang, dài theo chiều dọc
Anurag Uniyal

14
Điều gì xảy ra nếu Cột của tôi không được gán cho một thuộc tính cùng tên? IE , x = Column('y', Integer, primary_key=True)? Không có giải pháp nào trong số này hoạt động trong trường hợp này.
Nút840

13
drdaeman đã đúng, đây là đoạn trích chính xác:return {c.name: getattr(self, c.name) for c in self.__table__.columns}
charlax

5
Câu trả lời này đưa ra một giả định không hợp lệ: tên cột không nhất thiết phải khớp với tên thuộc tính.
RazerM

93

Theo @zzzeek trong các bình luận:

lưu ý rằng đây là câu trả lời đúng cho các phiên bản hiện đại của SQLAlchemy, giả sử "hàng" là một đối tượng hàng cốt lõi, không phải là một thể hiện ánh xạ ORM.

for row in resultproxy:
    row_as_dict = dict(row)

13
Nó nói 'Đối tượng XXX không thể lặp lại', tôi đang sử dụng 0.5.6, tôi nhận được bởi session.query (Klass) .filter (). All ()
Anurag Uniyal

60
lưu ý rằng đây là câu trả lời đúng cho các phiên bản hiện đại của SQLAlchemy, giả sử "hàng" là một đối tượng hàng cốt lõi, không phải là một thể hiện ánh xạ ORM.
zzzeek

48
Cũng lưu ý rằng zzzeek là người tạo ra sqlalchemy.
chris

1
Bất cứ ai có thể xây dựng trên này? Tôi là một người không nhận được điều này.
lameei

1
Sự khác biệt giữa một đối tượng hàng lõi so với đối tượng được ánh xạ ORM là gì? Điều này không hoạt động đối với tôi trên các hàng từ query(MyModel).all(): Đối tượng MyModel không lặp lại được.
Jonathan Hartley

81

Trong SQLAlchemy v0.8 trở lên, hãy sử dụng hệ thống kiểm tra .

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)

Lưu ý rằng đó .keylà tên thuộc tính, có thể khác với tên cột, ví dụ trong trường hợp sau:

class_ = Column('class', Text)

Phương pháp này cũng hoạt động cho column_property.


@DukeDougal Tôi nghĩ rằng điều này hoạt động từ v0.8 (khi hệ thống kiểm tra đã được thêm vào).
RazerM

1
Điều này hoạt động với Sqlalchemy v2.0. Những câu trả lời khác thì không.
Thanh Nguyễn

Điều này không tính đến các cột bị hoãn lại
Đánh dấu

1
@Mark Tôi không rõ ràng rằng chúng nên được loại trừ theo mặc định. Tuy nhiên, bạn có thể kiểm tra các phím không có trongsqlalchemy.inspect(obj).unloaded
RazerM

5
@NguyenThanh Làm việc với SQLAlchemy v2.0 đặc biệt ấn tượng vì không tồn tại! Bản phát hành (beta) mới nhất là v1.3.0b1.
Đánh dấu Amery

39

các hàng có _asdict()chức năng đưa ra lệnh

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

Nó được coi là riêng tư và không thể xóa / thay đổi trong các phiên bản trong tương lai.
balki

2
@balki Nó là tài liệu khá tốt và như vậy không hoàn toàn riêng tư. Mặc dù một dấu gạch dưới hàng đầu có ý nghĩa như vậy trong Python nói chung, ở đây có lẽ nó được sử dụng để không xung đột với các phím tuple có thể.
Ilja Everilä

5
Điều này chỉ hoạt động với các KeyedTuple, chỉ được trả về khi truy vấn các trường cụ thể của một hàng. tức là .query (Topic.name) trả về KeyedTuple, trong khi .query (Topic) trả về một Topic, không có ._asdict () - Derp. Chỉ cần nhìn thấy câu trả lời của STB dưới đây.
Chad Lowe

20

như @balki đã đề cập:

Các _asdict()phương pháp có thể được sử dụng nếu bạn đang truy vấn một lĩnh vực cụ thể bởi vì nó sẽ được trả về như một KeyedTuple .

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

Trong khi đó, nếu bạn không chỉ định một cột, bạn có thể sử dụng một trong các phương thức được đề xuất khác - chẳng hạn như một phương thức được cung cấp bởi @charlax. Lưu ý rằng phương pháp này chỉ hợp lệ cho 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'}

Nếu các thuộc tính lớp ORM của python có các tên khác với các cột cơ sở dữ liệu, hãy thử giải pháp này: stackoverflow.com/questions/27947294/
Lỗi

2
trên thực tế, một giải pháp tốt hơn cho tất cả các trường hợp được cung cấp bởi tác giả sqlalchemy
TaiwanGrapefbeanTea 14/1/2015

Khi tôi thử điều này, tôi nhận được đối tượng resultProxy không có thuộc tính '_asdict'
slashdottir

@slashdottir, bạn đang thực hiện đối tượng truy vấn của mình (gọi .first()phương thức)?
Sam Bourne

1
Câu trả lời này đưa ra một giả định không hợp lệ: tên cột không nhất thiết phải khớp với tên thuộc tính - xem câu trả lời của RazerM.
Piotr Dobrogost

18

Câu hỏi cũ, nhưng vì đây là kết quả đầu tiên cho "hàng sqlalchemy to dict" trong Google, nó xứng đáng có câu trả lời tốt hơn.

Đối tượng RowProxy mà SqlAlchemy trả về có phương thức items (): http://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.RowProxy.items

Nó chỉ đơn giản trả về một danh sách các bộ dữ liệu (khóa, giá trị). Vì vậy, người ta có thể chuyển đổi một hàng thành dict bằng cách sử dụng như sau:

Trong Python <= 2.6:

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

Trong Python> = 2.7:

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

13
Bạn chỉ có thể làmlist_of_dicts = [dict(row.items()) for row in rows]
Markus Meskanen 7/11/2016

Một nhược điểm là các tên cột mà SQLAlchemy sử dụng trong tập kết quả là table_name_column_name, nếu bạn muốn các tên khác nhau (ví dụ: chỉ column_name), hãy sử dụng .labelphương thức. session.query( MyTable.column_name.label('column_name'), ... )
Aneel

Hi Tôi nhận được vấn đề này xin giúp tôi * datetime.datetime (2018, 11, 24, 18, 52, 50) không phải là JSON serializable *
Saravanan Nandhan

13

Giả sử các hàm sau sẽ được thêm vào class Usersau đây sẽ trả về tất cả các cặp khóa-giá trị của tất cả các cột:

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

Không giống như tất cả các câu trả lời khác, nhưng chỉ các thuộc tính đó của đối tượng được trả về là Columncác thuộc tính ở cấp lớp của đối tượng. Do đó, không có _sa_instance_statehoặc bất kỳ thuộc tính SQLalchemy nào khác hoặc bạn thêm vào đối tượng được bao gồm. Tài liệu tham khảo

EDIT: Quên nói rằng, điều này cũng hoạt động trên các Cột được kế thừa.

hybrid_propery gia hạn

Nếu bạn cũng muốn bao gồm hybrid_propertycác thuộc tính, sau đây sẽ hoạt động:

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_

Tôi giả sử ở đây rằng bạn đánh dấu các Cột bằng một khởi đầu _để cho biết rằng bạn muốn ẩn chúng, bởi vì bạn truy cập thuộc tính bằng một hybrid_propertyhoặc đơn giản là bạn không muốn hiển thị chúng. Tài liệu tham khảo

Tipp all_orm_descriptors cũng trả về hybrid_methodAssociationProxy nếu bạn cũng muốn bao gồm chúng.

Nhận xét cho câu trả lời khác

Mỗi câu trả lời (như 1 , 2 ) dựa trên __dict__thuộc tính chỉ đơn giản trả về tất cả các thuộc tính của đối tượng. Đây có thể là nhiều thuộc tính hơn sau đó bạn muốn. Giống như tôi buồn điều này bao gồm _sa_instance_statehoặc bất kỳ thuộc tính nào khác mà bạn xác định trên đối tượng này.

Mỗi câu trả lời (như 1 , 2 ) dựa trên dict()hàm chỉ hoạt động trên các đối tượng hàng SQLalchemy được trả về bởi session.execute()không phải trên các lớp bạn xác định để làm việc với, giống như class Usertừ câu hỏi.

Các câu trả lời giải quyết mà là dựa trên row.__table__.columnschắc chắn sẽ không làm việc. row.__table__.columnschứa tên cột của Cơ sở dữ liệu SQL. Chúng chỉ có thể bằng tên thuộc tính của đối tượng python. Nếu không bạn có được một AttributeError. Đối với câu trả lời (như 1 , 2 ) dựa trên class_mapper(obj.__class__).mapped_table.cnó là như nhau.


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
Hãy nhận biết sự khác biệt giữa local_table và mapped_table. Ví dụ: nếu bạn áp dụng một số loại kế thừa bảng trong db của bạn (tbl_emp NHÂN> tbl_manager, tbl_emp NHÂN> tbl_staff), các lớp được ánh xạ của bạn sẽ cần phản ánh điều này (Người quản lý (Nhân viên), Nhân viên (Nhân viên)). mapped_table.c sẽ cung cấp cho bạn tên cột của cả bảng cơ sở và bảng kế thừa. local_table chỉ cung cấp cho bạn tên của bảng (kế thừa) của bạn.
Michael Ekoka

Điều này tránh đưa ra trường '_sa_instance_state', ít nhất là trong phiên bản 0.8+.
Evan Siroky

4
Câu trả lời này đưa ra một giả định không hợp lệ: tên cột không nhất thiết phải khớp với tên thuộc tính.
RazerM

11

Theo câu trả lời @balki, vì SQLAlchemy 0.8, bạn có thể sử dụng _asdict (), có sẵn cho các đối tượng KeyedTuple. Điều này làm cho một câu trả lời khá đơn giản cho câu hỏi ban đầu. Chỉ cần thay đổi trong ví dụ của bạn hai dòng cuối cùng (vòng lặp for) cho dòng này:

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

Điều này hoạt động vì trong đoạn mã trên, u là một đối tượng của lớp loại KeyedTuple , vì .all () trả về một danh sách KeyedTuple. Do đó, nó có phương thức _asdict () , trả về u như một từ điển.

WRT câu trả lời của @STB: AFAIK, anithong mà .all () trả về là danh sách KeypedTuple. Do đó, ở trên hoạt động nếu bạn chỉ định một cột hoặc không, miễn là bạn đang xử lý kết quả của .all () như được áp dụng cho một đối tượng Truy vấn.


6
Điều này có thể đúng trong quá khứ, nhưng trên SQLAlchemy v1.0 .all()trả về một danh sách các Usertrường hợp, vì vậy điều này không hoạt động.
RazerM

@RazerM, xin lỗi, nhưng tôi không hiểu ý của bạn. Vòng lặp for phải lặp chính xác qua danh sách các thể hiện của Người dùng, chuyển đổi chúng (u) thành từ điển và sau đó in chúng ...
jgbarah

3
Usertrường hợp không có một _asdictphương pháp. Xem gist.github.com/RazerM/2eff51571b3c70e8aeecd303c2a2bc8d
RazerM

2
Bây giờ tôi đã nhận nó. Cảm ơn. Thay vì KeyedTuple, bây giờ .all () trả về các đối tượng Người dùng. Vì vậy, vấn đề đối với v1.0 (trở lên, tôi giả sử) là làm thế nào để lấy từ điển ra khỏi đối tượng Người dùng. Cảm ơn bạn đã làm rõ.
jgbarah

10

Biểu thức bạn đang lặp qua các đánh giá để liệt kê các đối tượng mô hình , không phải các hàng. Vì vậy, sau đây là cách sử dụng chính xác của chúng:

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

Bạn có thực sự cần phải chuyển đổi chúng thành dicts? Chắc chắn, có rất nhiều cách, nhưng sau đó bạn không cần phần ORM của SQLAlchemy:

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

Cập nhật : Hãy xem sqlalchemy.orm.attributesmô-đun. Nó có một tập hợp các hàm để làm việc với trạng thái đối tượng, đặc biệt có thể hữu ích cho bạn instance_dict().


2
Tôi muốn chuyển đổi chúng thành dict thành, bởi vì một số mã khác cần dữ liệu là dict và tôi muốn một cách chung vì tôi sẽ không biết cột nào mà đối tượng mô hình có
Anurag Uniyal

và khi tôi xử lý chúng, tôi chỉ có quyền truy cập vào các đối tượng mô hình vì vậy tôi không thể sử dụng session.execute, v.v.
Anurag Uniyal

8

Tham khảo Câu trả lời của Alex Brasetvik , bạn có thể sử dụng một dòng mã để giải quyết vấn đề

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

Trong phần bình luận của Câu trả lời của Alex Brasetvik, zzzeek, ​​người tạo ra SQLAlchemy tuyên bố đây là "Phương pháp đúng" cho vấn đề.


1
@Greenonline Chắc chắn, nhận xét phê duyệt nằm dưới câu trả lời của Alex Brasetvik. Đã chỉnh sửa để thêm liên kết vào câu trả lời của anh ấy
NorWay

Là gì resultproxy?
lameei

8

Bạn có thể thử làm theo cách này.

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

Nó sử dụng một phương thức dựng sẵn trong đối tượng truy vấn trả về một đối tượng dictonary của đối tượng truy vấn.

tài liệu tham khảo: https://docs.sqlalchemy.org/en/latest/orm/query.html


1
Thêm một số giải thích có thể?
Tiw

1
Không có gì thực sự nhiều hơn để giải thích. Đó là một phương thức tích hợp trên đối tượng kết quả. Vì vậy, cho dù bạn làm điều này cho tất cả các kết quả hay một hàng đơn lẻ, có một _asdict()phương thức tích hợp về cơ bản sẽ nén các tên trường bằng các giá trị trường và trả về kết quả dưới dạng từ điển.
Matthew

Rất súc tích và tôi ước nó hoạt động nhưng utrong trường hợp của tôi là một chuỗi và tôi gặp lỗi `` Model 'đối tượng không có thuộc tính' _asdict'` @hllau bên dưới hoạt động với tôi
Mote Zart

7

Tôi đã tìm thấy bài đăng này bởi vì tôi đang tìm cách chuyển đổi một hàng SQLAlchemy thành một dict. Tôi đang sử dụng SqlSoup ... nhưng câu trả lời đã được xây dựng bởi chính tôi, vì vậy, nếu nó có thể giúp ai đó ở đây hai xu của tôi:

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

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

1
hoặc, nếu bạn thích ..: [dict (zip (i.keys (), i.values ​​())) cho i trong b]
Mychot buồn

Đây là cú pháp duy nhất tôi thấy thực sự hoạt động! Tôi đã thử đồ trong hơn một giờ.
slashdottir

Đối với các lựa chọn cốt lõi, RowProxy( ctrong câu trả lời này) tuân thủ giao thức ánh xạ, vì vậy bạn chỉ cần gọi dict(c).
RazerM

4

Bạn có thể chuyển đổi đối tượng sqlalchemy sang từ điển như thế này và trả lại dưới dạng json / dictionary.

Chức năng trợ giúp:

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) 

Chức năng điều khiển:

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

3

Hai lối:

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

3

Các tài liệu cung cấp một giải pháp rất đơn giản: ResultRow._asdict()

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

def query():
    data = session.query(Table).all()
    return to_array(data)

2

Đây là cách Elixir làm điều đó. Giá trị của giải pháp này là nó cho phép đệ quy bao gồm cả biểu diễn từ điển của các quan hệ.

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

Liên kết đã chết. Lần sau xin vui lòng sao chép mã có liên quan ở đây là tốt.
Gus E

Sẽ làm lần sau. Nếu tôi nhớ chính xác, chức năng khá dài.
argentpepper

2

Với mã này, bạn cũng có thể thêm vào "bộ lọc" truy vấn hoặc "tham gia" và công việc này!

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("_")])

Cần làm việc.


1
Điều gì xảy ra nếu tên cột bắt đầu bằng "_"?
Anurag Uniyal

4
Tôi sẽ tưởng tượng rằng bạn thực sự không nên đặt tên cột của bạn với một dấu gạch dưới hàng đầu. Nếu bạn làm thế, nó sẽ không hoạt động. Nếu đó chỉ là một số lẻ mà bạn biết, bạn có thể sửa đổi nó để thêm các cột đó.
Singletname

1

Tôi có một biến thể về câu trả lời của Marco Mariani, được thể hiện như một vật trang trí. Sự khác biệt chính là nó sẽ xử lý danh sách các thực thể, cũng như bỏ qua một số loại giá trị trả về khác một cách an toàn (rất hữu ích khi viết bài kiểm tra bằng cách sử dụng giả):

@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

Để hoàn thành câu trả lời của @Anurag Uniyal, đây là một phương pháp sẽ theo dõi đệ quy các mối quan hệ:

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}

trong trường hợp mặc định cho 'with_relationships' được thay đổi thành false, tốt hơn nên chuyển giá trị này qua cuộc gọi đệ quy. tức là: d[relationship.key] = to_dict(val,with_relationships) if val else None
Nicholas Hamilton

Làm thế nào tôi có thể đạt được kết quả, nếu tôi muốn tham gia bảng người dùng và địa chỉ dựa trên cột address_id và tìm nạp tất cả các cột từ bảng người dùng và chỉ cột id từ bảng địa chỉ.
Sudhakar

0

Tôi là một lập trình viên Python mới được tạo ra và gặp vấn đề khi truy cập JSON với các bảng đã tham gia. Sử dụng thông tin từ các câu trả lời ở đây, tôi đã xây dựng một hàm để trả về kết quả hợp lý cho JSON, trong đó các tên bảng được bao gồm để tránh phải bí danh hoặc có các trường xung đột.

Chỉ cần chuyển kết quả của một truy vấn phiên:

test = Phiên (). truy vấn (VMInfo, Khách hàng) .join (Khách hàng) .order_by (VMInfo.vm_name) .limit (50) .offset (10)

json = sqlAl2json (kiểm tra)

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

nếu cột bảng mô hình của bạn không phải là cột mysie.

nhu la :

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

Cần sử dụng:

 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}

nếu bạn sử dụng theo cách này, bạn có thể nhận được redirect_time và created_time cả hai đều là Không

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

Bởi vì tên thuộc tính lớp không bằng với lưu trữ cột trong mysql


0

Trả về nội dung của nó: class: .KeyedTupleas a dictionary

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

Vì lợi ích của mọi người và bản thân tôi, đây là cách tôi sử dụng nó:

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

Chức năng này có thể giúp đỡ. Tôi không thể tìm ra giải pháp tốt hơn để giải quyết vấn đề khi tên thuộc tính khác với tên cột.


0

Bạn sẽ cần nó ở mọi nơi trong dự án của bạn, tôi xin lỗi @anurag đã trả lời rằng nó hoạt động tốt. Cho đến thời điểm này tôi đã sử dụng nó, nhưng nó sẽ làm rối tung tất cả mã của bạn và cũng không hoạt động với thay đổi thực thể.

Thay vì thử điều này, hãy kế thừa lớp truy vấn cơ sở của bạn trong 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)

sau đó bất cứ nơi nào bạn sẽ xác định phương thức "as_dict" của đối tượng của bạn sẽ ở đó.


0

Với python 3.8+, chúng ta có thể thực hiện điều này với dataclass và asdictphương thức đi kèm với nó:

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

Điều quan trọng là sử dụng trình @dataclasstrang trí và chú thích mỗi cột với loại của nó ( : strmột phần của name: str = Column(String)dòng).

Cũng lưu ý rằng vì emailkhông được chú thích, nó không được bao gồm trong query_result_dict.


Trên Python3.7, tôi nhận được "NameError: tên 'asdict' không được xác định"
devnull

Lỗi của tôi! Đây là một chức năng được thêm vào trong python 3.8. Đã sửa câu trả lời của tôi.
toaruScar

-1

Trong hầu hết các kịch bản, tên cột phù hợp với chúng. Nhưng có thể bạn viết mã như sau:

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

cột.name "user_email" trong khi tên trường là "email", cột.name không thể hoạt động tốt như trước.

sqlalchemy_base_model.py

tôi cũng viết câu trả lời ở đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.