Konwertuj obiekt wiersza sqlalchemy na Python Dict


240

Czy istnieje prosty sposób na iterację par nazw kolumn i wartości?

Moja wersja sqlalchemy to 0.5.6

Oto przykładowy kod, w którym próbowałem użyć dict (wiersz), ale zgłasza wyjątek, TypeError: Obiekt „User” nie jest iterowalny

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)

Uruchomienie tego kodu na wyjściach systemu:

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
Tytuł pytania nie pasuje do samego pytania. Zgodnie z dokumentami Wiersze wyników zwracane przez zapytanie, które zawierają wiele jednostek ORM i / lub wyrażeń kolumn, używają tej klasy do zwracania wierszy. gdzie ta klasa jest sqlalchemy.util.KeyedTupleco rząd obiekt z tytułu Pytanie za. Jednak zapytanie w pytaniu używa klasy model (mapowanej), dlatego typem obiektu wiersza jest klasa modelu zamiast sqlalchemy.util.KeyedTuple.
Piotr Dobrogost

2
@PiotrDobrogost Pytanie pochodzi z 2009 roku i wspomina o wersji sqlalchemy 0.5.6
Anurag Uniyal

Odpowiedzi:


232

Możesz uzyskać dostęp do wnętrza __dict__obiektu SQLAlchemy, na przykład:

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

24
Najlepsza odpowiedź w tym wątku, nie wiem, dlaczego wszyscy inni proponują znacznie bardziej skomplikowane rozwiązania.
Dave Rawks,

92
Daje to dodatkowe pole „_sa_instance_state”, przynajmniej w wersji 0.7.9.
elbear

21
więc byłoby lepiej:dictret = dict(row.__dict__); dictret.pop('_sa_instance_state', None)
lyfing

6
nie wydaje się to idealne, ponieważ, jak zauważyli ludzie, __dict__zawiera _sa_instance_statewpis, który należy następnie usunąć. jeśli uaktualnisz do przyszłej wersji i zostaną dodane inne atrybuty, być może będziesz musiał wrócić i ręcznie sobie z nimi poradzić. jeśli chcesz tylko kolumny danych (na przykład, aby pobrać listę wystąpień z zapytania i upuścić je w ramce danych pandy), wówczas {col.name: getattr(self, col.name) for col in self.__table__.columns}odpowiedź Anurag Uniyal (z ważnymi poprawkami z komentarzy do tej odpowiedzi) wydaje się bardziej zwięzła i zawiera błędy dowód.
kilgoretrout

14
Ta odpowiedź jest zła. Pytanie ma nawet dict(u)i poprawnie stwierdza, że ​​rzuca TypeError.
RazerM

146

Nie mogłem uzyskać dobrej odpowiedzi, więc używam tego:

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

    return d

Edycja: jeśli powyższa funkcja jest zbyt długa i nie nadaje się do niektórych smaków, to jest to jedna linijka (python 2.7+)

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

17
Bardziej zwięźle return dict((col, getattr(row, col)) for col in row.__table__.columns.keys()).
argentpepper

4
@argentpepper tak, możesz nawet zrobić, row2dict = lambda row: dict((col, getattr(row, col)) for col in row.__table__.columns.keys())aby był to jeden linijka, ale wolę, aby mój kod był czytelny, poziomo krótki, pionowo długi
Anurag Uniyal

14
Co jeśli moja kolumna nie jest przypisana do atrybutu o tej samej nazwie? IE x = Column('y', Integer, primary_key=True)? Żadne z tych rozwiązań nie działa w tym przypadku.
Przyciski 840

13
drdaeman ma rację, oto poprawny fragment:return {c.name: getattr(self, c.name) for c in self.__table__.columns}
charlax,

5
Ta odpowiedź zawiera nieprawidłowe założenie: nazwy kolumn niekoniecznie pasują do nazw atrybutów.
RazerM,

94

Jak na @zzzeek w komentarzach:

zauważ, że jest to poprawna odpowiedź dla współczesnych wersji SQLAlchemy, zakładając, że „row” jest głównym obiektem wiersza, a nie instancją odwzorowaną na ORM.

for row in resultproxy:
    row_as_dict = dict(row)

13
Mówi: „Obiekt XXX nie jest iterowalny”, używam 0.5.6, otrzymuję przez session.query (Klass) .filter (). All ()
Anurag Uniyal

60
zauważ, że jest to poprawna odpowiedź dla współczesnych wersji SQLAlchemy, zakładając, że „row” jest głównym obiektem wiersza, a nie instancją odwzorowaną na ORM.
zzzeek

48
Zauważ też, że zzzeek jest twórcą sqlalchemy.
Chris

1
Czy ktoś może rozwinąć tę kwestię? Jestem noobem i nie rozumiem tego.
lameei

1
Jaka jest różnica między obiektem wiersza podstawowego a instancją odwzorowaną na ORM? To nie działa dla mnie w wierszach z query(MyModel).all(): Obiekt MyModel nie jest iterowalny.
Jonathan Hartley,

81

W SQLAlchemy v0.8 i nowszych użyj systemu inspekcji .

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)

Zauważ, że .keyjest to nazwa atrybutu, która może różnić się od nazwy kolumny, np. W następującym przypadku:

class_ = Column('class', Text)

Ta metoda działa również w przypadku column_property.


@DukeDougal Myślę, że działa to od wersji 0.8 (kiedy dodano system inspekcji).
RazerM

1
Działa to z Sqlalchemy v2.0. Inne odpowiedzi nie.
Thanh Nguyen,

Nie uwzględnia to odroczonych kolumn
Mark

1
@ Mark Nie jest dla mnie jasne, że należy je domyślnie wykluczyć. Niemniej jednak możesz sprawdzić, czy kluczy nie masqlalchemy.inspect(obj).unloaded
RazerM

5
@NguyenThanh Praca z SQLAlchemy v2.0 jest szczególnie imponująca, biorąc pod uwagę jej nieistnienie! Najnowsza wersja (beta) to 1.3.0b1.
Mark Amery

39

wiersze mają _asdict()funkcję, która daje dyktando

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

Ma być prywatny i nie może zostać usunięty / zmieniony w przyszłych wersjach.
balki

2
@balki Jest dość dobrze udokumentowany i jako taki nie całkiem prywatny. Chociaż wiodący znak podkreślenia ma to znaczenie w Pythonie ogólnie, tutaj jest prawdopodobnie używany, aby nie kolidować z możliwymi kluczami krotkowymi.
Ilja Everilä

5
Działa to tylko z KeyedTuple s, które są zwracane tylko przy wyszukiwaniu określonych pól wiersza. tzn. .query (nazwa_pliku) zwraca KeyedTuple, natomiast .query (nazwa) zwraca temat, który nie ma ._asdict () - Derp. właśnie zobaczyłem odpowiedzi STB poniżej.
Chad Lowe,

20

jak wspomniał @balki:

_asdict()Metoda może być stosowana, jeśli jesteś zapytań pole specyficzny, ponieważ jest zwracany jako KeyedTuple .

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

Natomiast jeśli nie określisz kolumny, możesz użyć jednej z innych proponowanych metod - takich jak ta dostarczona przez @charlax. Pamiętaj, że ta metoda jest ważna tylko dla wersji 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'}

Jeśli atrybuty klasy ORM python mają inne nazwy niż kolumny bazy danych, wypróbuj to rozwiązanie: stackoverflow.com/questions/27947294/…
TaiwanGrapefruitTea

2
w rzeczywistości lepsze rozwiązanie dla wszystkich przypadków zapewnia autor sqlalchemy na stackoverflow.com/a/27948279/1023033
TaiwanGrapefruitTea

Kiedy próbuję tego, otrzymuję wynik ResultProxy nie ma atrybutu „_asdict”
slashdottir

@ slashdottir, czy wykonujesz obiekt zapytania (wywołując .first()metodę)?
Sam Bourne,

1
Ta odpowiedź zawiera nieprawidłowe założenie: nazwy kolumn niekoniecznie pasują do nazw atrybutów - patrz odpowiedź RazerM.
Piotr Dobrogost

18

Stare pytanie, ale ponieważ jest to pierwszy wynik dla „wiersza sqlalchemy do dyktowania” w Google, zasługuje na lepszą odpowiedź.

Obiekt RowProxy, który zwraca SqlAlchemy, ma metodę items (): http://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.RowProxy.items

Zwraca po prostu listę krotek (klucz, wartość). Aby przekonwertować wiersz na dyktando, można wykonać następujące czynności:

W Pythonie <= 2.6:

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

W Pythonie> = 2.7:

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

13
Możesz po prostu zrobićlist_of_dicts = [dict(row.items()) for row in rows]
Markus Meskanen

Jednym z problemów jest to, że nazwy kolumn używane przez SQLAlchemy w zestawie wyników to table_name_column_name, jeśli chcesz inne nazwy (np. Po prostu column_name), użyj .labelmetody. session.query( MyTable.column_name.label('column_name'), ... )
Aneel

Cześć, otrzymuję ten problem, proszę pomóżcie mi * datetime.datetime (2018, 11, 24, 18, 52, 50) nie można serializować JSON *
Saravanan Nandhan

13

Zakładając, że następujące funkcje zostaną dodane do class Usernastępujących, zwrócą wszystkie pary klucz-wartość wszystkich kolumn:

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

w przeciwieństwie do innych odpowiedzi zwracane są tylko te atrybuty obiektu, które są Columnatrybutami na poziomie klasy obiektu. Dlatego nie są uwzględniane _sa_instance_stateżadne inne atrybuty SQLalchemy ani dodane do obiektu. Odniesienie

EDYCJA: Zapomnij powiedzieć, że działa to również na odziedziczone kolumny.

hybrid_propery rozszerzenie

Jeśli chcesz również dołączyć hybrid_propertyatrybuty, następujące działania będą działać:

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_

Zakładam tutaj, że zaznaczasz Kolumny początkiem, _aby wskazać, że chcesz je ukryć, albo dlatego, że uzyskujesz dostęp do atrybutu przez, hybrid_propertyalbo po prostu nie chcesz ich pokazywać. Odniesienie

Tipp all_orm_descriptors zwraca także hybrid_method i AssociationProxy jeśli chcesz je również dołączyć.

Uwagi do innych odpowiedzi

Każda odpowiedź (jak 1 , 2 ), która na podstawie __dict__atrybutu zwraca po prostu wszystkie atrybuty obiektu. Może to być znacznie więcej atrybutów, niż chcesz. To smutne, że to obejmuje_sa_instance_state lub dowolny inny atrybut zdefiniowany w tym obiekcie.

Każda odpowiedź (np. 1 , 2 ), która jest oparta na dict()funkcji, działa tylko na obiektach wierszy SQLalchemy zwracanych przez session.execute()nie na klasach, z którymi zdefiniujesz pracę, takich jakclass User z pytania.

Odpowiedź rozwiązywanie który oparty jest na row.__table__.columnspewno nie praca. row.__table__.columnszawiera nazwy kolumn bazy danych SQL. Mogą być one równe tylko nazwie atrybutu obiektu python. Jeśli nie, dostaniesz AttributeError. Odpowiedzi (jak 1 , 2 ) oparte na class_mapper(obj.__class__).mapped_table.cnim są takie same.


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
Pamiętaj o różnicy między tabelą lokalną a tabelą mapowaną. Na przykład, jeśli zastosujesz jakieś dziedziczenie tabeli w swoim pliku db (tbl_employees> tbl_managers, tbl_employees> tbl_staff), twoje odwzorowane klasy będą musiały to odzwierciedlać (kierownik (pracownik), personel (pracownik)). mapped_table.c daje nazwy kolumn tabeli podstawowej i tabeli dziedziczenia. local_table podaje tylko nazwę twojej (dziedziczącej) tabeli.
Michael Ekoka,

Pozwala to uniknąć podawania pola „_sa_instance_state”, przynajmniej w wersji 0.8+.
Evan Siroky,

4
Ta odpowiedź zawiera nieprawidłowe założenie: nazwy kolumn niekoniecznie pasują do nazw atrybutów.
RazerM,

11

Po odpowiedzi @balki, od SQLAlchemy 0.8 możesz użyć _asdict (), dostępnej dla obiektów KeyedTuple. To daje dość prostą odpowiedź na pierwotne pytanie. Po prostu zmień w swoim przykładzie dwie ostatnie linie (pętlę for) dla tego:

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

Działa to, ponieważ w powyższym kodzie u jest obiektem klasy typu KeyedTuple , ponieważ .all () zwraca listę KeyedTuple. Dlatego ma metodę _asdict () , która ładnie zwraca u jako słownik.

WRT odpowiedź @STB: AFAIK, po czym zwraca .all () jest listą KeypedTuple. Dlatego powyższe działa albo, jeśli podasz kolumnę, albo nie, o ile masz do czynienia z wynikiem .all () zastosowanym do obiektu zapytania.


6
To może być prawda w przeszłości, ale w SQLAlchemy v1.0 .all()zwraca listę Userinstancji, więc to nie działa.
RazerM,

@RazerM, przepraszam, ale nie rozumiem, co masz na myśli. Pętla for powinna dokładnie przechodzić przez listę instancji użytkownika, przekształcając je (u) w słowniki, a następnie drukując je ...
jgbarah

3
Userinstancje nie mają _asdictmetody. Zobacz gist.github.com/RazerM/2eff51571b3c70e8aeecd303c2a2bc8d
RazerM

2
Teraz rozumiem. Dzięki. Zamiast KeyedTuple, teraz .all () zwraca obiekty użytkownika. Problemem w wersji 1.0 (i wyższych, jak przypuszczam) jest to, jak pobrać słownik z obiektu użytkownika. Dziękuję za wyjaśnienie.
jgbarah

10

Wyrażenie, przez które się iteruje, przekształca się w listę obiektów modelu , a nie wiersze. Więc następujące jest ich prawidłowe użycie:

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

Czy naprawdę musisz przekonwertować je na dyktanda? Jasne, istnieje wiele sposobów, ale wtedy nie potrzebujesz części ORM SQLAlchemy:

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

Aktualizacja : spójrz na sqlalchemy.orm.attributesmoduł. Posiada zestaw funkcji do pracy ze stanem obiektu, który może być szczególnie przydatny instance_dict().


2
Chcę przekonwertować je na dyktowanie, ponieważ jakiś inny kod potrzebuje danych jako dykt, i chcę ogólny sposób, ponieważ nie będę wiedział, jakie kolumny ma obiekt modelu
Anurag Uniyal

a kiedy się z nimi
uporam,

8

Zapoznaj się z odpowiedzią Alexa Brasetvika , aby rozwiązać problem, możesz użyć jednego wiersza kodu

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

W sekcji komentarza w odpowiedzi Alexa Brasetvika zzzeek, ​​twórca SQLAlchemy, stwierdził, że jest to „poprawna metoda” problemu.


1
@Greenonline Pewnie, komentarz do zatwierdzenia znajduje się pod odpowiedzią Alexa Brasetvika. Edytowano, aby dodać link do jego odpowiedzi
NorWay

Co to jest resultproxy?
lameei

8

Możesz spróbować to zrobić w ten sposób.

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

Używa wbudowanej metody w obiekcie zapytania, która zwraca obiekt dictonary obiektu zapytania.

referencje: https://docs.sqlalchemy.org/en/latest/orm/query.html


1
Dodaj więcej wyjaśnień?
Tiw

1
Nic więcej do wyjaśnienia. Jest to wbudowana metoda obiektu wynikowego. Niezależnie od tego, czy robisz to dla wszystkich wyników, czy dla pojedynczego wiersza, istnieje wbudowana _asdict()metoda, która zasadniczo zamyka nazwy pól wartościami pól i zwraca wynik jako słownik.
Matthew

Bardzo zwięzłe i chciałbym, żeby to zadziałało, ale uw moim przypadku jest to ciąg znaków i pojawia się błąd `` Model '' obiekt nie ma atrybutu '_asdict' '@hllau poniżej pracował dla mnie
Mote Zart

7

Znalazłem ten post, ponieważ szukałem sposobu na konwersję wiersza SQLAlchemy na dyktando. Używam SqlSoup ... ale odpowiedź została zbudowana przeze mnie, więc jeśli to mogłoby pomóc komuś, oto moje dwa centy:

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

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

1
lub, jeśli wolisz ...: [dykta (zip (i.keys (), i.values ​​())) dla i in b]
Mychot sad

To jedyna składnia, którą znalazłem, która faktycznie działa! Próbuję rzeczy od ponad godziny.
slashdottir,

W przypadku podstawowych wyborów RowProxy( cw tej odpowiedzi) przestrzega protokołu mapowania, więc możesz po prostu zadzwonić dict(c).
RazerM,

4

Możesz przekonwertować obiekt sqlalchemy na słownik w ten sposób i zwrócić go jako json / dictionary.

Funkcje pomocnicze:

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) 

Funkcja kierowcy:

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

3

Dwie drogi:

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

Oto, jak robi to Elixir. Wartość tego rozwiązania polega na tym, że pozwala on rekurencyjnie uwzględniać słownikową reprezentację relacji.

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

Link nie działa. Następnym razem skopiuj również odpowiedni kod tutaj.
Gus E

Zrobię następnym razem. Jeśli dobrze pamiętam, funkcja była dość długa.
argentpepper

2

Za pomocą tego kodu możesz również dodać do zapytania „filtr” lub „dołącz” i to działa!

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

To powinno działać.


1
co się stanie, jeśli nazwa kolumny zaczyna się od „_”?
Anurag Uniyal

4
Wyobrażam sobie, że tak naprawdę nie powinieneś nazywać swoich kolumn wiodącym podkreśleniem. Jeśli to zrobisz, to nie zadziała. Jeśli to tylko dziwny, o którym wiesz, możesz go zmodyfikować, aby dodać te kolumny.
Singletoned

1

Mam odmianę odpowiedzi Marco Mariani, wyrażoną jako dekorator. Główną różnicą jest to, że obsługuje listy jednostek, a także bezpiecznie ignoruje niektóre inne typy zwracanych wartości (co jest bardzo przydatne podczas pisania testów przy użyciu próbnych):

@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

Aby wypełnić odpowiedź @Anurag Uniyal, oto metoda, która będzie rekurencyjnie śledzić relacje:

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}

w przypadku zmiany domyślnej wartości parametru „with_relationships” na false, lepiej przekazać tę wartość do wywołania rekurencyjnego. to znaczy: d[relationship.key] = to_dict(val,with_relationships) if val else None
Nicholas Hamilton

jak mogę osiągnąć wynik, jeśli chcę dołączyć do tabeli użytkowników i adresów na podstawie kolumny adres_id i pobrać całą kolumnę z tabeli użytkowników i tylko kolumnę identyfikatora z tabeli adresów.
Sudhakar

1

W Pythonie 3.8+ możemy to zrobić za pomocą klasy danych i asdictdołączonej do niej metody:

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

Kluczem jest użycie @dataclassdekoratora i opatrzenie adnotacjami każdej kolumny swoim typem ( : strczęśćname: str = Column(String) linii).

Pamiętaj też, że ponieważ emailnie jest to adnotacja, nie jest uwzględnione w query_result_dict.


W Pythonie 3.7 otrzymuję „NameError: nazwa„ asdict ”nie jest zdefiniowana”
devnull

Mój błąd! Jest to funkcja dodana w Pythonie 3.8. Naprawiłem moją odpowiedź.
toaruScar

Tak pytoniczny. 3.8 jest niesamowity. Ale tak naprawdę nie potrzebujesz metody init , prawda? zarówno deklaratywna, jak i klasa danych zapewniają ogólne metody inicjowania.
Jeff Laughlin

0

Jestem świeżo upieczonym programistą Python i miałem problemy z dostępem do JSON z dołączonymi tabelami. Korzystając z informacji zawartych w odpowiedziach tutaj, zbudowałem funkcję zwracającą rozsądne wyniki do JSON-a, gdzie zawarte są nazwy tabel, unikając konieczności aliasu lub kolizji pól.

Po prostu przekaż wynik zapytania o sesję:

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

json = sqlAl2json (test)

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

jeśli kolumna tabeli modeli nie jest równa kolumnie mysql.

Jak na przykład :

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

Potrzebuję użyć:

 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}

jeśli użyjesz tej metody, możesz uzyskać czas modyfikacji i czas utworzenia, oba są Brak

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

Ponieważ nazwa atrybutów klasy nie jest równa nazwie magazynu kolumn w mysql


0

Zwróć zawartość tego: class: .KeyedTuplejako słownika

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

Ze względu na wszystkich i siebie, oto jak go używam:

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

Ta funkcja może pomóc. Nie mogę znaleźć lepszego rozwiązania problemu, gdy nazwa atrybutu jest inna niż nazwa kolumny.


0

Będziesz go potrzebować wszędzie w swoim projekcie, apriciate @anurag odpowiedział, że działa dobrze. do tego momentu używałem go, ale to zepsuje cały twój kod, a także nie będzie działać ze zmianą encji.

Zamiast tego spróbuj odziedziczyć podstawową klasę zapytania w 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)

po tym, gdziekolwiek zdefiniujesz swój obiekt, pojawi się metoda „as_dict”.


-1

W większości scenariuszy nazwa kolumny jest dla nich odpowiednia. Ale może piszesz kod w następujący sposób:

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

nazwa.kolumny „email_użytkownika”, podczas gdy nazwa pola to „e-mail”, nazwa.kolumny nie mogła działać tak dobrze jak wcześniej.

sqlalchemy_base_model.py

też piszę odpowiedź tutaj

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.