Преобразование объекта строки sqlalchemy в python dict
Есть ли простой способ перебора пар имен столбцов и значений?
Моя версия sqlalchemy - 0.5.6
Вот пример кода, в котором я пытался использовать dict (row), но он вызывает исключение, 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
Ответы
Ответ 1
Я не смог получить хороший ответ, поэтому я использую это:
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}
Ответ 2
Вы можете получить доступ к внутреннему __dict__
объекта SQLAlchemy, например, следующему::
for u in session.query(User).all():
print u.__dict__
Ответ 3
В 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
.
Ответ 4
Согласно @zzzeek в комментариях:
обратите внимание, что это правильный ответ для современных версий SQLAlchemy, предполагая, что "строка" является базовым объектом строки, а не сопоставленным с ORM Экземпляр.
for row in resultproxy:
row_as_dict = dict(row)
Ответ 5
Строки имеют функцию _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'}
Ответ 6
как упоминал @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'}
Ответ 7
Старый вопрос, но поскольку это первый результат для "sqlalchemy row to dict" в Google, он заслуживает лучшего ответа.
Объект RowProxy, возвращаемый SqlAlchemy, имеет метод items():
http://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.RowProxy.items
Он просто возвращает список кортежей (ключ, значение). Таким образом, можно преобразовать строку в dict, используя следующее:
В 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]
Ответ 8
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)
Ответ 9
Предполагая, что следующие функции будут добавлены в class User
следующие будут возвращать все пары ключ-значение всех столбцов:
def columns_to_dict(self):
dict_ = {}
for key in self.__mapper__.c.keys():
dict_[key] = getattr(self, key)
return dict_
в отличие от других ответов возвращаются все, кроме только тех атрибутов объекта, которые являются атрибутами Column
на уровне класса объекта. Поэтому никакие _sa_instance_state
или любой другой атрибут SQLalchemy, или вы добавляете к объекту, не включены. Ссылка
РЕДАКТИРОВАТЬ: забудьте, что это также работает на унаследованных столбцах.
расширение 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
также возвращает hybrid_method и AssociationProxy, если вы также хотите включить их.
Замечания к другим ответам
Каждый ответ (например, 1, 2), основанный на __dict__
просто возвращает все атрибуты объекта. Это может быть гораздо больше атрибутов, чем вы хотите. Как мне грустно, это включает в себя _sa_instance_state
или любой другой атрибут, который вы определяете для этого объекта.
Каждый ответ (например, 1, 2), основанный на функции dict()
работает только с объектами строк SQLalchemy, возвращаемыми session.execute()
не с классами, с которыми вы работаете, например с class User
из вопроса.
Решающий ответ, основанный на row.__table__.columns
, Определенно не будет работать. row.__table__.columns
содержит имена столбцов базы данных SQL. Они могут быть равны только имени атрибута объекта python. Если нет, вы получите AttributeError
. Для ответов (например, 1, 2) на основе class_mapper(obj.__class__).mapped_table.c
это то же самое.
Ответ 10
После ответа @balki с SQLAlchemy 0.8 вы можете использовать _asdict(), доступный для объектов KeyedTuple. Это дает довольно простой ответ на исходный вопрос. Просто измените в своем примере две последние строки (цикл for) для этого:
for u in session.query(User).all():
print u._asdict()
Это работает, потому что в приведенном выше коде u является объектом класса типа KeyedTuple, так как .all() возвращает список KeyedTuple. Поэтому он имеет метод _asdict(), который прекрасно возвращает u в качестве словаря.
WRT ответ by @STB: AFAIK, anikong, что .all() возвращает список KeypedTuple. Следовательно, вышеизложенное работает, если вы укажете столбец или нет, если вы имеете дело с результатом .all() применительно к объекту Query.
Ответ 11
Выражение, которое вы повторяете, вычисляет список объектов модели, а не строк. Поэтому правильное их использование:
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()
.
Ответ 12
Я нашел этот пост, потому что искал способ преобразования строки 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()))
Ответ 13
Обратитесь к Alex Brasetvik Answer, вы можете использовать одну строку кода для решения проблемы
row_as_dict = [dict(row) for row in resultproxy]
В разделе комментариев Alex Brasetvik Ответ, zzzeek, создатель SQLAlchemy заявил, что это "правильный метод" для проблемы.
Ответ 14
Вы можете попытаться сделать это таким образом.
for u in session.query(User).all():
print(u._asdict())
Он использует встроенный метод в объекте запроса, который возвращает диктонарный объект объекта запроса.
ссылки: https://docs.sqlalchemy.org/en/latest/orm/query.html
Ответ 15
Вы можете преобразовать объект 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)
Ответ 16
Два пути:
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())
Ответ 17
Документы предлагают очень простое решение: ResultRow._asdict()
def to_array(rows):
return [r._asdict() for r in rows]
def query():
data = session.query(Table).all()
return to_array(data)
Ответ 18
Вот как это делает Эликсир. Значение этого решения заключается в том, что оно позволяет рекурсивно включать в себя словарное представление отношений.
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
Ответ 19
С помощью этого кода вы также можете добавить к вашему запросу "фильтр" или "присоединиться" и эту работу!
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]
Ответ 20
class User(object):
def to_dict(self):
return dict([(k, getattr(self, k)) for k in self.__dict__.keys() if not k.startswith("_")])
Это должно работать.
Ответ 21
У меня есть вариант ответа Марко Мариани, выраженный в качестве декоратора. Основное отличие состоит в том, что он будет обрабатывать списки объектов, а также безопасно игнорировать некоторые другие типы возвращаемых значений (что очень полезно при написании тестов с использованием 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
Ответ 22
Чтобы завершить ответ @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}
Ответ 23
Самый простой способ, который я нашел (используя sqla 0.7.8):
dictlist = [dict(row) for row in somequery.execute().fetchall()]
Ответ 24
Я новый программируемый программист на 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)
Ответ 25
если столбец вашей модели не является эквивалентным столбцом 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}
если вы используете этот способ, вы можете получить change_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
Ответ 26
Вернуть содержимое этого: 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'}
Ответ 27
Ради всех и меня, вот как я его использую:
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]
Ответ 28
__dict__
без внутренних (подчеркнутых) атрибутов может быть использовано в качестве быстрого решения.
for u in session.query(User).all():
print({k: v for k, v in u.__dict__.items() if not k.startswith('_')})
Ответ 29
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))
Эта функция может помочь. Я не могу найти лучшего решения для решения проблемы, когда имя атрибута отличается от имени столбца.
Ответ 30
Он понадобится вам везде в вашем проекте, я ответил, что @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" будет там.