Перетворити об’єкт рядка sqlalchemy в діктом python


240

Чи існує простий спосіб перебрати пара імен стовпців та значень?

Моя версія sqlalchemy становить 0.5.6

Ось зразок коду, де я спробував використовувати dict (рядок), але він викидає виняток, TypeError: "User" об'єкт не є доступним

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.
Пьотр Доброгост

2
@PiotrDobrogost Питання від 2009 року та згадується версія sqlalchemy 0.5.6
Anurag Uniyal

Відповіді:


232

Ви можете отримати доступ до внутрішнього __dict__об'єкта SQLAlchemy, як-от наступного:

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

24
Найкраща відповідь у цій темі, не знаю, чому всі інші пропонують набагато складніші рішення.
Дейв Раукс

92
Це дає додаткове поле "_sa_instan_state", принаймні у версії 0.7.9.
elbear

21
так що це буде краще:dictret = dict(row.__dict__); dictret.pop('_sa_instance_state', None)
lyfing

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),? Жодне з цих рішень у цьому випадку не працює.
Кнопки840

13
drdaeman має рацію, ось правильний фрагмент:return {c.name: getattr(self, c.name) for c in self.__table__.columns}
charlax

5
Ця відповідь робить невірним припущення: назви стовпців не обов'язково відповідають іменам атрибутів.
RazerM

94

Відповідно до @zzzeek у коментарях:

зауважте, що це правильна відповідь для сучасних версій SQLAlchemy, припускаючи, що "рядок" є основним об'єктом рядка, а не екземпляром, відображеним ORM.

for row in resultproxy:
    row_as_dict = dict(row)

13
У ній написано: "Об'єкт XXX не є ітерабельним", я використовую 0.5.6, я отримую сеанс.query (Klass) .filter (). All ()
Anurag Uniyal

60
зауважте, що це правильна відповідь для сучасних версій SQLAlchemy, припускаючи, що "рядок" є основним об'єктом рядка, а не екземпляром, відображеним ORM.
zzzeek

48
Також зауважте, що zzzeek є творцем sqlalchemy.
chris

1
Хтось може детальніше розібратися в цьому? Я noob не отримую цього.
lameei

1
Яка різниця між об'єктом основного рядка від екземпляра, нанесеного на ORM? Це не працює для мене в рядках з query(MyModel).all(): Об'єкт MyModel не піддається виконанню.
Джонатан Хартлі

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. Інші відповіді не відповідають.
Тхань Нгуен

Це не враховує відкладені стовпці
Позначте

1
@Mark Мені незрозуміло, що вони повинні бути виключені за замовчуванням. Тим не менш, ви можете перевірити, чи немає ключівsqlalchemy.inspect(obj).unloaded
RazerM

5
@ NguyenThanh Робота з SQLAlchemy v2.0 особливо вражає, враховуючи її відсутність! Останній (бета) випуск - v1.3.0b1.
Марк Амері

39

рядки мають _asdict()функцію, яка дає диктат

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

Він повинен бути приватним і не може бути видалений / змінений у наступних версіях.
балки

2
@balki Це досить добре документовано і як таке не зовсім приватне. Хоча провідна підкреслення має таке значення в Python взагалі, тут вона, ймовірно, використовується для того, щоб не зіткнутися з можливими кортежними клавішами.
Ilja Everilä

5
Це працює лише з KeyedTuple s, які повертаються лише при запиті певних полів рядка. тобто .query (Topic.name) повертає KeyedTuple, тоді як .query (Topic) повертає тему, яка не має ._asdict () - Derp. щойно побачила відповідь НТУ нижче.
Чад Лоу

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

Якщо атрибути класу пітон ORM мають різні імена з стовпців бази даних, спробуйте це рішення: stackoverflow.com/questions/27947294 / ...
TaiwanGrapefruitTea

2
на Насправді, найкраще рішення для всіх випадків забезпечується SQLAlchemy автором в stackoverflow.com/a/27948279/1023033
TaiwanGrapefruitTea

Коли я спробую це, я отримую об'єкт ResultProxy не має атрибута '_asdict'
slashdottir

@slashdottir, чи виконуєте ви свій об’єкт запиту (виклик .first()методу)?
Сем Борн

1
Ця відповідь робить неправдивим припущення: назви стовпців не обов'язково відповідають іменам атрибутів - див. Відповідь RazerM.
Пьотр Доброгост

18

Старе запитання, але оскільки це перший результат для "sqlalchemy ряду, щоб диктувати" в Google, він заслуговує на кращу відповідь.

Об'єкт RowProxy, який повертає SqlAlchemy, має метод items (): http://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.RowProxy.items

Він просто повертає список (ключ, значення) кортежів. Таким чином, можна перетворити рядок у діктант, використовуючи наступне:

У 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]
Маркус Месканен

Один з прихильників полягає в тому, що імена стовпців, які використовує SQLAlchemy в наборі результатів table_name_column_name, якщо ви хочете різних імен (наприклад, просто column_name), використовуйте .labelметод. session.query( MyTable.column_name.label('column_name'), ... )
Анеель

Привіт, я отримую це питання, будь ласка, допоможіть мені * datetime.datetime (2018, 11, 24, 18, 52, 50) - це не серіалізація JSON *
Сараванан Нандхан

13

Якщо припустити, що наступні функції будуть додані до 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 або ви не додаєте до об'єкта. Довідково

EDIT: Забудьте сказати, що це також працює на успадкованих стовпцях.

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її основі те саме.


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 дає лише назву вашої (успадкованої) таблиці.
Майкл Екока

Це дозволяє уникнути введення поля "_sa_instan_state", принаймні у версії 0.8+.
Evan Siroky

4
Ця відповідь робить невірним припущення: назви стовпців не обов'язково відповідають іменам атрибутів.
RazerM

11

Після відповіді @balki, оскільки SQLAlchemy 0.8 ви можете використовувати _asdict (), доступний для об'єктів KeyedTuple. Це дає досить просту відповідь на початкове запитання. Просто змініть у своєму прикладі останні два рядки (цикл for) для цього:

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

Це працює, тому що у наведеному вище коді u є об'єктом класу типу KeyedTuple , оскільки .all () повертає список KeyedTuple. Тому він має метод _asdict () , який чудово повертає u як словник.

Напишіть відповідь від @STB: AFAIK, anithong, який повертає .all () - це список KeypedTuple. Отже, вищезазначене працює або в тому випадку, якщо ви вказали стовпець чи ні, поки ви маєте справу з результатом .all (), застосованим до об’єкта Query.


6
Це можливо було і раніше, але в SQLAlchemy v1.0 .all()повертається список Userпримірників, тому це не працює.
RazerM

@RazerM, вибач, але я не розумію, що ти маєш на увазі. Цикл for повинен точно проглядати список екземплярів користувача, перетворюючи їх (u) у словники, а потім друкуючи їх ...
jgbarah

3
Userекземпляри не мають _asdictметоду. Дивіться gist.github.com/RazerM/2eff51571b3c70e8aeecd303c2a2bc8d
RazerM

2
Тепер я це отримав. Дякую. Замість KeyedTuple тепер .all () повертає об'єкти користувача. Отже, проблема v1.0 (і вище, я припускаю) полягає в тому, як вилучити словник із об’єкта User. Дякуємо за роз’яснення.
jgbarah

10

Вираз, який ви повторюєте, оцінює список об'єктів моделі , а не рядків. Отже, правильне їх використання:

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

Вам справді потрібно конвертувати їх у дикти? Звичайно, існує багато способів, але тоді вам не потрібна ORM частина SQLAlchemy:

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

Оновлення : подивіться на sqlalchemy.orm.attributesмодуль. Він має набір функцій для роботи з об'єктним станом, які можуть бути корисні для вас, особливо instance_dict().


2
Я хочу перетворити їх на диктування, тому що якийсь інший код потребує даних як dict, і я хочу загальний спосіб, тому що я не знаю, які стовпці мають об'єкт моделі
Anurag Uniyal

і коли я до них звертаюся, у мене є доступ лише до модельних об'єктів, тому я не можу використовувати session.execute тощо
Anurag Uniyal

8

Зверніться до відповіді Алекса Брасетвіка , ви можете використовувати один рядок коду для вирішення проблеми

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

У розділі коментарів відповіді Алекса Брасетвіка, zzzeek, ​​автор SQLAlchemy заявив, що це "правильний метод" для проблеми.


1
@Greenonline Звичайно, коментар схвалення відповідає відповіді Алекса Брасетвіка. Відредаговано, щоб додати відповідь на свою відповідь
NorWay

Що таке resultproxy?
lameei

8

Ви можете спробувати зробити це таким чином.

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

Він використовує вбудований метод в об'єкт запиту, який повертає об'єкт дикторії об'єкта запиту.

посилання: https://docs.sqlalchemy.org/en/latest/orm/query.html


1
Додайте ще трохи пояснень, можливо?
Тів

1
Нічого насправді більше пояснювати. Це вбудований метод на об'єкті результату. Отже, чи робите ви це для всіх результатів, або для одного рядка, є вбудований _asdict()метод, який по суті замикає назви полів зі значеннями полів і повертає результат у вигляді словника.
Метью

Дуже лаконічно, і я хочу, щоб це спрацювало, але uв моєму випадку це рядок, і я отримую помилку, що об'єкт `` Model 'не має атрибутів' _asdict'` @hllau нижче працював для мене
Mote Zart

7

Я знайшов цю посаду, тому що шукав спосіб перетворити рядок SQLAlchemy в дикт. Я використовую 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 (i.keys (), i.values ​​())) для i in b]
Mychot sad

Це єдиний синтаксис, який я виявив, що насправді працює! Я пробував речі більше години.
slashdottir

Для вибору ядра RowProxy( cу цій відповіді) дотримується протокол відображення, тому ви можете просто зателефонувати dict(c).
RazerM

4

Ви можете перетворити об'єкт sqlalchemy в такий словник і повернути його як json / словник.

Функції помічника:

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

Ось як це робить Еліксир. Цінність цього рішення полягає в тому, що воно дозволяє рекурсивно включати словникове подання відносин.

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

Я маю різні варіанти відповіді Марко Мар'яні, вираженої як декоратор. Основна відмінність полягає в тому, що він буде обробляти списки сутностей, а також безпечно ігнорувати деякі інші типи повернених значень (що дуже корисно при написанні тестів за допомогою макетів):

@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' змінено значення false, краще передайте це значення рекурсивному виклику. а саме: d[relationship.key] = to_dict(val,with_relationships) if val else None
Ніколас Гамільтон

як я можу досягти результату, якщо я хочу приєднатись до таблиці користувачів та адрес на основі стовпця address_id та отримати весь стовпець з таблиці користувачів та лише стовпець id з адресної таблиці.
Судхакар

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)рядка).

Також зауважте, що оскільки emailце не зазначається , воно не включається query_result_dict.


На Python3.7 я отримую "NameError: name" asdict "не визначено"
devnull

Моє ліжко! Це функція, додана в python 3.8. Виправлена ​​моя відповідь.
toaruScar

Так пітонічно. 3,8 дивним. Але вам насправді метод init не потрібен ? декларативний та клас даних забезпечують загальні методи init.
Джефф Лафлін

0

Я нещодавно відрізаний програміст Python і зіткнувся з проблемами потрапляння в JSON з таблицями Joined. Використовуючи інформацію з відповідей тут, я побудував функцію для повернення розумних результатів JSON, де включені імена таблиць, уникаючи виникнення псевдоніму чи стикання полів.

Просто передайте результат запиту сеансу:

test = сесія (). запит (VMInfo, клієнт). приєднатися (клієнт) .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}

якщо ви використовуєте цей спосіб, ви можете отримати modify_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)

ім'я стовпця "user_email", а ім'я поля - "електронна пошта", ім'я стовпця не може працювати добре, як раніше.

sqlalchemy_base_model.py

також я пишу відповідь тут

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.