Converter objeto de linha sqlalchemy em dict python

240

Existe uma maneira simples de iterar sobre o nome da coluna e os pares de valores?

Minha versão do sqlalchemy é 0.5.6

Aqui está o código de exemplo em que tentei usar o dict (linha), mas ele lança a exceção TypeError: o objeto 'User' não é iterável

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)

A execução deste código nas saídas do meu sistema:

sqlalchemy version: 0.5.6
Traceback (most recent call last):
  File "untitled-1.py", line 37, in <module>
    print dict(u)
TypeError: 'User' object is not iterable
Anurag Uniyal
fonte
3
O título da pergunta não corresponde à pergunta em si. De acordo com os documentos As linhas de resultado retornadas pela consulta que contêm várias entidades ORM e / ou expressões de coluna fazem uso dessa classe para retornar linhas. onde esta classe é sqlalchemy.util.KeyedTuplequal é o objeto de linha do título da pergunta. No entanto, a consulta na pergunta usa a classe model (mapeada), portanto, o tipo de objeto de linha é a classe model em vez de sqlalchemy.util.KeyedTuple.
Piotr Dobrogost 02/02/19
2
@PiotrDobrogost pergunta é de 2009 e menciona sqlalchemy versão 0.5.6
Anurag Uniyal

Respostas:

232

Você pode acessar o interno __dict__de um objeto SQLAlchemy, como o seguinte:

for u in session.query(User).all():
    print u.__dict__
hllau
fonte
24
Melhor resposta neste tópico, não sei por que todo mundo está propondo soluções muito mais complicadas.
precisa
92
Isso fornece um campo '_sa_instance_state' extra, pelo menos na versão 0.7.9.
elbear
21
então isso seria melhor:dictret = dict(row.__dict__); dictret.pop('_sa_instance_state', None)
lyfing 5/11/14
6
isso não parece ideal, pois, como as pessoas indicaram, __dict__inclui uma _sa_instance_stateentrada que deve ser removida. se você atualizar para uma versão futura e outros atributos forem adicionados, poderá ser necessário voltar e lidar manualmente com eles. se você deseja apenas dados da coluna (por exemplo, pegar uma lista de instâncias de uma consulta e soltá-las em um quadro de dados do pandas), então, {col.name: getattr(self, col.name) for col in self.__table__.columns}conforme respondido por Anurag Uniyal (com correções importantes dos comentários a essa resposta), parece mais sucinto e com erros prova.
kilgoretrout
14
Esta resposta está errada. A questão ainda tem dict(u)e afirma corretamente que lança a TypeError.
RazerM
146

Como não consegui uma boa resposta, uso o seguinte:

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

    return d

Edit: se a função acima é muito longa e não é adequada para alguns gostos, aqui está um liner (python 2.7+)

row2dict = lambda r: {c.name: str(getattr(r, c.name)) for c in r.__table__.columns}
Anurag Uniyal
fonte
17
Mais sucintamente return dict((col, getattr(row, col)) for col in row.__table__.columns.keys()).
Argentpepper 30/03/12
4
@argentpepper sim você pode até fazer row2dict = lambda row: dict((col, getattr(row, col)) for col in row.__table__.columns.keys())para torná-lo um forro de um real, mas eu prefiro o meu código para ser legível, horizontalmente short, verticalmente longa
Anurag Uniyal
14
E se minha coluna não estiver atribuída a um atributo com o mesmo nome? IE x = Column('y', Integer, primary_key=True)? Nenhuma dessas soluções funciona nesse caso.
Buttons840
13
drdaeman está certo, aqui está o trecho correto:return {c.name: getattr(self, c.name) for c in self.__table__.columns}
charlax
5
Esta resposta faz uma suposição inválida: os nomes das colunas não correspondem necessariamente aos nomes dos atributos.
RazerM 20/05
93

Conforme @zzzeek nos comentários:

observe que esta é a resposta correta para versões modernas do SQLAlchemy, supondo que "linha" seja um objeto de linha principal, não uma instância mapeada em ORM.

for row in resultproxy:
    row_as_dict = dict(row)
Alex Brasetvik
fonte
13
Ele diz 'XXX objeto não é iterable', eu estou usando 0.5.6, eu recebo por session.query (Klass) .filter () todos ().
Anurag Uniyal
60
observe que esta é a resposta correta para versões modernas do SQLAlchemy, supondo que "linha" seja um objeto de linha principal, não uma instância mapeada em ORM.
Zzzeek
48
Observe também que o zzzeek é o criador do sqlalchemy.
chris
1
Alguém pode elaborar sobre este? Eu sou um noob e não entendo isso.
lameei 30/05/19
1
Qual é a diferença entre um objeto de linha principal e uma instância mapeada em ORM? Isso não funciona para mim nas linhas de query(MyModel).all(): O objeto MyModel não é iterável.
Jonathan Hartley
81

No SQLAlchemy v0.8 e mais recente, use o sistema de inspeção .

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)

Observe que esse .keyé o nome do atributo, que pode ser diferente do nome da coluna, por exemplo, no seguinte caso:

class_ = Column('class', Text)

Este método também funciona para column_property.

RazerM
fonte
@DukeDougal Acho que isso funciona a partir da v0.8 (quando o sistema de inspeção foi adicionado).
RazerM 11/09/16
1
Isso funciona com o Sqlalchemy v2.0. Outras respostas não.
Thanh Nguyen
Isso não leva em consideração as colunas adiadas
Marque
1
@ Mark Não está claro para mim que eles devem ser excluídos por padrão. No entanto, você pode verificar se as chaves não estão dentrosqlalchemy.inspect(obj).unloaded
RazerM 4/17/17
5
@NguyenThanh Trabalhar com o SQLAlchemy v2.0 é particularmente impressionante, dada a sua inexistência! A versão (beta) mais recente é a v1.3.0b1.
Mark Amery
39

linhas têm uma _asdict()função que fornece um ditado

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
fonte
É suposto ser privado e não pode ser removido / alterado em versões futuras.
balki
2
@balki É muito bem documentado e, como tal, não é muito particular. Embora um sublinhado principal tenha esse significado no Python em geral, aqui é provavelmente usado para não colidir com possíveis chaves de tupla.
Ilja Everilä 17/08/19
5
Isso funciona apenas com KeyedTuple s, que são retornados apenas ao consultar campos específicos de uma linha. ie .query (Topic.name) retorna um KeyedTuple, enquanto .query (Topic) retorna um Tópico que não possui ._asdict () - Derp. só vi os STBs responderem abaixo.
Chad Lowe
20

como @balki mencionado:

O _asdict()método pode ser usado se você estiver consultando um campo específico porque ele é retornado como um KeyedTuple .

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

Considerando que, se você não especificar uma coluna, poderá usar um dos outros métodos propostos - como o fornecido por @charlax. Observe que esse método é válido apenas para 2.7+.

In [1]: foo = db.session.query(Topic).first()
In [2]: {x.name: getattr(foo, x.name) for x in foo.__table__.columns}
Out[2]: {'name': u'blah'}
Sam Bourne
fonte
Se os atributos de classe python ORM têm nomes diferentes das colunas de banco de dados, tente esta solução: stackoverflow.com/questions/27947294/...
TaiwanGrapefruitTea
2
na verdade, a melhor solução para todos os casos é fornecido pelo autor sqlalchemy em stackoverflow.com/a/27948279/1023033
TaiwanGrapefruitTea
Quando eu tento isso eu recebo objeto ResultProxy tem nenhum atributo '_asdict'
slashdottir
@slashdottir, você está executando seu objeto de consulta (chamando o .first()método)?
Sam Bourne
1
Esta resposta faz uma suposição inválida: os nomes das colunas não necessariamente correspondem aos nomes dos atributos - consulte a resposta do RazerM.
Piotr Dobrogost
18

Pergunta antiga, mas como esse é o primeiro resultado para "ditar sqlalchemy row" no Google, ele merece uma resposta melhor.

O objeto RowProxy que SqlAlchemy retorna possui o método items (): http://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.RowProxy.items

Ele simplesmente retorna uma lista de tuplas (chave, valor). Portanto, é possível converter uma linha em ditado usando o seguinte:

No Python <= 2.6:

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

Em Python> = 2.7:

rows = conn.execute(query)
list_of_dicts = [{key: value for (key, value) in row.items()} for row in rows]
fgasparini
fonte
13
Você pode simplesmente fazerlist_of_dicts = [dict(row.items()) for row in rows]
Markus Meskanen 7/16/16
Um problema é que os nomes de coluna que o SQLAlchemy usa em um conjunto de resultados são table_name_column_name, se você quiser nomes diferentes (por exemplo, apenas column_name), use o .labelmétodo session.query( MyTable.column_name.label('column_name'), ... )
Aneel
Oi eu estou recebendo este problema pls help me * datetime.datetime (2018, 11, 24, 18, 52, 50) não é JSON serializado *
Saravanan Nandhan
13

Assumindo que as seguintes funções serão adicionadas, class Usero seguinte retornará todos os pares de valores-chave de todas as colunas:

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

ao contrário das outras respostas, todos, exceto apenas os atributos do objeto, são retornados, que são Columnatributos no nível de classe do objeto. Portanto, nenhum _sa_instance_stateou qualquer outro atributo SQLalchemy ou você adiciona ao objeto estão incluídos. Referência

Edição: esqueça de dizer que isso também funciona em colunas herdadas.

hybrid_propery extensão

Se você também deseja incluir hybrid_propertyatributos, o seguinte funcionará:

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_

Suponho aqui que você marque as colunas com um começo _para indicar que deseja ocultá-las, porque você acessa o atributo por um hybrid_propertyou simplesmente não deseja mostrá-las. Referência

Tipp all_orm_descriptors também retorna hybrid_method e AssociationProxy, se você também quiser incluí-los.

Comentários a outras respostas

Toda resposta (como 1 , 2 ) que, com base no __dict__atributo, simplesmente retorna todos os atributos do objeto. Isso pode ser muito mais atributos do que você deseja. Como eu triste isso inclui_sa_instance_state ou qualquer outro atributo que você definir sobre esse objeto.

Todas as respostas (como 1 , 2 ) baseadas na dict()função funcionam apenas nos objetos de linha SQLalchemy retornados, session.execute()não nas classes com as quais você define trabalhar, como as class Userda pergunta.

A resposta de solução que se baseia row.__table__.columnsdefinitivamente não funcionará. row.__table__.columnscontém os nomes das colunas do banco de dados SQL. Eles podem ser iguais apenas ao nome dos atributos do objeto python. Se não, você recebe um AttributeError. Para respostas (como 1 , 2 ), com base class_mapper(obj.__class__).mapped_table.cnela, é a mesma.

F.Raab
fonte
12
from sqlalchemy.orm import class_mapper

def asdict(obj):
    return dict((col.name, getattr(obj, col.name))
                for col in class_mapper(obj.__class__).mapped_table.c)
Marco Mariani
fonte
4
Esteja ciente da diferença entre local_table e mapped_table. Por exemplo, se você aplicar algum tipo de herança de tabela em seu banco de dados (tbl_employees> tbl_managers, tbl_employees> tbl_staff), suas classes mapeadas precisarão refletir isso (Gerente (Funcionário), Equipe (Funcionário)). mapped_table.c fornecerá os nomes das colunas da tabela base e da tabela herdada. local_table apenas fornece o nome da sua tabela (herdada).
22612 Michael Ekoka
Isso evita fornecer o campo '_sa_instance_state', pelo menos na versão 0.8+.
precisa saber é o seguinte
4
Esta resposta faz uma suposição inválida: os nomes das colunas não correspondem necessariamente aos nomes dos atributos.
RazerM
11

Após a resposta @balki, desde SQLAlchemy 0.8, você pode usar _asdict (), disponível para objetos KeyedTuple. Isso torna uma resposta bastante direta à pergunta original. Apenas mude no seu exemplo as duas últimas linhas (o loop for) desta:

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

Isso funciona porque no código acima u é um objeto da classe tipo KeyedTuple , pois .all () retorna uma lista de KeyedTuple. Portanto, ele tem o método _asdict () , que retorna u como um dicionário.

Escreva a resposta por @STB: AFAIK, ao lado de .all () retorna uma lista de KeypedTuple. Portanto, o acima funcionará se você especificar uma coluna ou não, desde que esteja lidando com o resultado de .all () aplicado a um objeto de Consulta.

jgbarah
fonte
6
Isso pode ter sido verdade no passado, mas no SQLAlchemy v1.0 .all()retorna uma lista de Userinstâncias, portanto, isso não funciona.
RazerM 20/05
@RazerM, desculpe, mas eu não entendo o que você quer dizer. O laço for deve precisamente percorrer a lista de instâncias do usuário, convertendo-os (u) a dicionários, e, em seguida, imprimi-los ...
jgbarah
3
Userinstâncias não têm um _asdictmétodo. Veja gist.github.com/RazerM/2eff51571b3c70e8aeecd303c2a2bc8d
RazerM
2
Agora entendi. Obrigado. Em vez de KeyedTuple, agora .all () retorna objetos de usuário. Portanto, o problema da v1.0 (e acima, eu assumo) é como obter um dicionário de um objeto Usuário. Obrigado pelo esclarecimento.
Jgbarah # 28/16
10

A expressão pela qual você está iterando é avaliada na lista de objetos de modelo , não em linhas. Portanto, o seguinte é o uso correto deles:

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

Você realmente precisa convertê-los em ditados? Claro, existem várias maneiras, mas você não precisa da parte ORM do SQLAlchemy:

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

Atualização : Dê uma olhada no sqlalchemy.orm.attributesmódulo. Ele possui um conjunto de funções para trabalhar com o estado do objeto, que podem ser úteis para você, especialmente instance_dict().

Denis Otkidach
fonte
2
Eu quero convertê-los para ditar, porque algum outro código precisa de dados como ditado, e eu quero uma maneira genérica porque não saberei quais colunas um objeto de modelo possui
Anurag Uniyal
e quando eu recebo identificador para eles, eu tenho acesso apenas aos objetos de modelo, então não posso usar o session.execute etc #
Anurag Uniyal
8

Consulte a resposta de Alex Brasetvik , você pode usar uma linha de código para resolver o problema

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

Sob a seção de comentários da resposta de Alex Brasetvik, zzzeek, ​​o criador do SQLAlchemy, afirmou que este é o "método correto" para o problema.

Noruega
fonte
1
@Greenonline Claro, o comentário de aprovação está sob a resposta de Alex Brasetvik. Editado para adicionar o link à sua resposta
NorWay 4/17/17
Qual é o resultproxy?
lameei 30/05/19
8

Você pode tentar fazer dessa maneira.

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

Ele usa um método interno no objeto de consulta que retorna um objeto dictonário do objeto de consulta.

referências: https://docs.sqlalchemy.org/en/latest/orm/query.html

Enmanuel Medina
fonte
1
Adicione mais algumas explicações, talvez?
Tiw
1
Nada realmente mais a explicar. É um método interno no objeto de resultado. Portanto, se você fizer isso para todos os resultados ou para uma única linha, existe um _asdict()método interno que fecha os nomes dos campos com valores de campo e retorna o resultado como um dicionário.
18719 Matthew
Muito conciso e gostaria que isso funcionou, mas uno meu caso é uma string, e eu recebo erro `` Model 'objeto não tem nenhum atributo' _asdict'` @hllau abaixo funcionou para mim
Mote Zart
7

Encontrei este post porque estava procurando uma maneira de converter uma linha SQLAlchemy em um ditado. Estou usando o SqlSoup ... mas a resposta foi criada por mim, então, se isso puder ajudar alguém, aqui estão meus dois centavos:

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

# and now, finally...
dict(zip(c.keys(), c.values()))
Mychot triste
fonte
1
ou, se você preferir ..: [dict (zip (i.keys (), i.values ​​())) para i in b]
Mychot sad
Esta é a única sintaxe que encontrei que realmente funciona! Eu venho tentando coisas há mais de uma hora.
precisa
Para seleções principais, o RowProxy( cnesta resposta) adere ao protocolo de mapeamento, para que você possa simplesmente ligar dict(c).
RazerM 20/05
4

Você pode converter o objeto sqlalchemy em dicionário como este e retorná-lo como json / dictionary.

Funções auxiliares:

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) 

Função do driver:

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

Dois caminhos:

1

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

2)

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

Os documentos oferecem uma solução muito simples: ResultRow._asdict()

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

def query():
    data = session.query(Table).all()
    return to_array(data)
santo-amarelo
fonte
2

Aqui está como o Elixir faz isso. O valor dessa solução é que ela permite incluir recursivamente a representação das relações no dicionário.

def to_dict(self, deep={}, exclude=[]):
    """Generate a JSON-style nested dict/list structure from an object."""
    col_prop_names = [p.key for p in self.mapper.iterate_properties \
                                  if isinstance(p, ColumnProperty)]
    data = dict([(name, getattr(self, name))
                 for name in col_prop_names if name not in exclude])
    for rname, rdeep in deep.iteritems():
        dbdata = getattr(self, rname)
        #FIXME: use attribute names (ie coltoprop) instead of column names
        fks = self.mapper.get_property(rname).remote_side
        exclude = [c.name for c in fks]
        if dbdata is None:
            data[rname] = None
        elif isinstance(dbdata, list):
            data[rname] = [o.to_dict(rdeep, exclude) for o in dbdata]
        else:
            data[rname] = dbdata.to_dict(rdeep, exclude)
    return data
argentpepper
fonte
Link está morto. Da próxima vez, copie o código relevante aqui também.
Gus E
Vai fazer na próxima vez. Se bem me lembro, a função foi bastante longa.
Argentpepper
2

Com este código, você também pode adicionar à sua consulta "filtro" ou "associação" e este trabalho!

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

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

Isso deve funcionar.

Singletoned
fonte
1
o que acontece se o nome da coluna começar com "_"?
Anurag Uniyal
4
Eu imagino que você realmente não deve nomear suas colunas com um sublinhado principal. Se você fizer isso, não vai funcionar. Se você conhece apenas o estranho, você pode modificá-lo para adicionar essas colunas.
Singletoned
1

Eu tenho uma variação na resposta de Marco Mariani, expressa como decoradora. A principal diferença é que ele manipulará listas de entidades, além de ignorar com segurança outros tipos de valores de retorno (o que é muito útil ao escrever testes usando zombarias):

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

  return asdict(result)

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

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

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

Para completar a resposta de @Anurag Uniyal, aqui está um método que seguirá recursivamente os relacionamentos:

from sqlalchemy.inspection import inspect

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

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

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

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


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

to_dict(user)
# {'id': 1, 'first_name': 'Nathan', 'address': {'city': 'Lyon'}}
to_dict(user, with_relationship=False)
# {'id': 1, 'first_name': 'Nathan', 'address_id': 1}
nbarraille
fonte
caso o padrão para 'with_relationships' seja alterado para false, passe melhor esse valor para a chamada recursiva. ou seja: d[relationship.key] = to_dict(val,with_relationships) if val else None
Nicholas Hamilton
como posso obter o resultado, se quiser ingressar na tabela de usuários e endereços com base na coluna address_id e buscar toda a coluna da tabela de usuários e apenas a coluna de identificação da tabela de endereços.
Sudhakar
0

Sou um programador Python recém-formado e tive problemas para acessar o JSON com tabelas Associadas. Usando informações das respostas aqui, criei uma função para retornar resultados razoáveis ​​ao JSON, onde os nomes das tabelas estão incluídos, evitando a necessidade de um alias ou a colisão de campos.

Basta passar o resultado de uma consulta de sessão:

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

json = sqlAl2json (teste)

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

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

se a coluna da tabela de modelos não for igual à coluna mysql.

tal como :

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

Precisa usar:

 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}

se você usar dessa maneira, poderá obter modify_time e create_time, ambos são 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}

Porque o nome dos atributos da classe não é igual ao armazenamento da coluna no mysql

张小诚
fonte
0

Retorne o conteúdo deste: 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'}
Eds_k
fonte
0

Para o bem de todos e de mim, aqui está como eu o uso:

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]
humano
fonte
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))

Esta função pode ajudar. Não consigo encontrar uma solução melhor para resolver o problema quando o nome do atributo é diferente dos nomes das colunas.

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

Você precisará dele em qualquer lugar do seu projeto, eu apriciate @anurag respondeu que funciona bem. até o momento, eu estava usando, mas ele bagunçará todo o seu código e também não funcionará com a alteração de entidade.

Em vez disso, tente herdar sua classe de consulta base em 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)

depois disso, onde quer que você defina seu método "as_dict", o objeto estará lá.

Yash Pokar
fonte
0

Com o python 3.8+, podemos fazer isso com a classe de dados e o asdictmétodo que acompanha:

from dataclasses import dataclass, asdict

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

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


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

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

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


Base.metadata.create_all(engine)

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

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

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

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

A chave é usar o @dataclassdecorador e anotar cada coluna com seu tipo (a : strparte doname: str = Column(String) linha).

Observe também que, como o emailnão é anotado, ele não é incluído no query_result_dict.

toaruScar
fonte
No Python3.7, recebo "NameError: name 'asdict' não está definido"
devnull 13/03
Foi mal! É uma função adicionada no python 3.8. Corrigida minha resposta.
toaruScar 14/03
-1

Na maioria dos cenários, o nome da coluna é adequado para eles. Mas talvez você escreva o código da seguinte maneira:

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

o column.name "user_email" enquanto o nome do campo for "email", o column.name não funcionou bem como antes.

sqlalchemy_base_model.py

Também escrevo a resposta aqui

kaka_ace
fonte