LINUX.ORG.RU

как правильно обращаться с Алхимией

 ,


0

1

Как вернуть реальные данные из БД в Академии в виде объектов класса MySubject с заполненными полями?

в этом коде возвращается список словарей с ключем '_sa_instance_state' и значением sqlalchemy.orm.state.InstanceState:

def getMySubject(
        creation_date: datetime.date) -> List[MySubject]:

    subjects= []

    with sessionScope() as session:
        rows = (session 
               .query(MySubject) 
               .filter(MySubject.creation_date ==
                   creation_data)
               # ...
               .all()
        )
        for row in rows:
            subjects.append(row)
    return subjects

Вместо такого громоздкого кода просто сделай:

subjects = list(session.execute(MySubject.select().where(MySubject.creation_date == creation_date)))

UPD: У тебя ещё и опечатка creation_date -> creation_data

eternal_sorrow ★★★★★ ()
Последнее исправление: eternal_sorrow (всего исправлений: 1 )
Ответ на: комментарий от eternal_sorrow

Спасибо! Это сработало. А почему не работает это:

with sessionScope() as session:
    subjects = list(session.query(MySubject).filter(MySubject.creation_date == creation_date). ... .all())
?

внутри менеджера список правильный, с данными, вне - нет

scientistpython ()
Ответ на: комментарий от WitcherGeralt

вроде

Я использовал Core и не помню такого. Не буду утверждать, что их там нет, но это явно не самая очевидная часть api.

eternal_sorrow ★★★★★ ()
Ответ на: комментарий от WitcherGeralt

Хочеться понять, что же все таки тут происходит.

   result = []
   # внутри к.м. выборка заполнена данными
   with sessionScope() as session:
        rows = session.query(Model1).filter(...).all() 
        print(rows)
   # [{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x7fcfd03d1c18>, 'x': 1, 'y': 'a', ...}, ... ]

   
        result = rows
   # вне, при выходе из к.м. там уже нет данных
   print(result)
   # [{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x7fcfd03d1c18>}, ...]
scientistpython ()
Ответ на: комментарий от scientistpython

Начни с этого:

print(rows)
print(type(rows))
print(type(rows[0]))

Ты бы класс таблички дал и пару строчек данных, чтобы это можно было запустить, а не просто гадать. Я-то давно её не юзал, говорю же. Даже не помню действительно ли там инстансы класса таблицы должны быть, а не словари.

Версия алхимии, кстати, какая?

WitcherGeralt ★★ ()
Ответ на: комментарий от WitcherGeralt

спасибо за поддержку

вот, настройки dbSetting нужно подсунуть

from contextlib import contextmanager
from sqlalchemy import create_engine
from sqlalchemy import Column, Integer, Date
from enum import Enum

import pandas as pd
from sqlalchemy.exc import IntegrityError

from datetime import datetime, date
import numpy as np


engine = create_engine(dbSetting, echo=False)

@contextmanager
def sessionScope():
    try:
        session = Session(bind=engine)
        yield session
        session.commit()
    except Exception as e:
        print(e)
        session.rollback()
    finally:
        session.close()



from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()



class MyTable1(Base):

    __tablename__ = 'MyTable1'

    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)
    cdate = Column(Date, nullable=False)

    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

    def __repr__(self):
        return str(self.__dict__)

def getAll():
    result = []
    with sessionScope() as session:
        rows = session.query(MyTable1).all()
        print('- inside the context manager ')
        print(rows)
        result = rows
    return result

if __name__ == '__main__':

    engine = create_engine(psqlSetting)
    # Base.metadata.create_all(engine, tables=[MyTable1.__table__])
    # insert into "MyTable1" ("name", "cdate") values ('a','2021-05-09');
    rres = getAll()
    print('- outside the context manager:')
    print(rres)


- inside the context manager [{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x7f4453d37a20>, 'cdate': datetime.date(2021, 5, 9), 'name': 'a', 'id': 1}] - outside the context manager: [{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x7f4453d37a20>}]

scientistpython ()
Ответ на: комментарий от scientistpython

продублирую печать в консоль (править нельзя коментарий)

- inside the context manager 
[{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x7f4453d37a20>, 'cdate': datetime.date(2021, 5, 9), 'name': 'a', 'id': 1}]
- outside the context manager:
[{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x7f4453d37a20>}]

scientistpython ()
Ответ на: комментарий от WitcherGeralt
print(rows)
print(type(rows))
print(type(rows[0]))
[{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x7fd67ffc7160>, 'id': 1, 'name': 'a', 'cdate': datetime.date(2021, 5, 9)}]
<class 'list'>
<class '__main__.MyTable1'>
scientistpython ()
Ответ на: комментарий от scientistpython

Видишь, это не словарь. Просто представление при форматировании как словарь выглядит. А почему вне контекста форматирование иначе выглядит, хз, сейчас сам поробую.

WitcherGeralt ★★ ()
Последнее исправление: WitcherGeralt (всего исправлений: 1 )
Ответ на: комментарий от scientistpython

А, лол.

Так ты же сам такой вывод сделал:

def __repr__(self):
        return str(self.__dict__)

def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

А это ещё зачем?

WitcherGeralt ★★ ()
Ответ на: комментарий от WitcherGeralt

хотел заюзать MyTable1 как data transfer object, т.е. создавать записи

x = MyTable1(name='b', cdate=date(2021,1,1))
и затем их вставлять в БД

+

нужно читать записи и что-то с ними делать.

scientistpython ()
Ответ на: комментарий от WitcherGeralt

+, инит убрал, работает.

Но, основной момент такой. Можно ли не перекладывая поля из одного объекта в другой, сразу вытащить список с объектами типа MyTable1?

т.е. как просто объекты класса с полями (MyTable1) получить, ведь как просто структура MyTable1 работает:

x = MyTable1(name='b', cdate=date(2021,1,1))

x.name
Out[29]: 'b'

from datetime import timedelta

x.cdate + timedelta(days=1)
Out[31]: datetime.date(2021, 1, 2)

scientistpython ()
Ответ на: комментарий от scientistpython

Твой же код:

from contextlib import contextmanager
from sqlalchemy import create_engine
from sqlalchemy import Column, Integer, String, Date
from sqlalchemy.orm import Session
from enum import Enum

# import pandas as pd
from sqlalchemy.exc import IntegrityError

from datetime import datetime, date
# import numpy as np

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

@contextmanager
def sessionScope():
    try:
        session = Session(bind=engine)
        yield session
        session.commit()
    except Exception as e:
        print(e)
        session.rollback()
    finally:
        session.close()


from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()


class MyTable1(Base):

    __tablename__ = 'MyTable1'

    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)
    cdate = Column(Date, nullable=False)


def getAll():
    result = []
    with sessionScope() as session:
        rows = session.query(MyTable1).all()
        print('- inside the context manager ')
        print(rows)
        result = rows
    return result

if __name__ == '__main__':
    Base.metadata.create_all(engine, tables=[MyTable1.__table__])
    with Session(bind=engine) as session:
        row = MyTable1(id=1, name='foo', cdate=date.today())
        print(type(row.id), row.id, row.name, row.cdate)
        session.add(row)
        session.commit()
    rres = getAll()
    print('- outside the context manager:')
    print(rres)

Без проблем можешь юзать.

Вывод:

<class 'int'> 1 foo 2021-05-11
- inside the context manager 
[<__main__.MyTable1 object at 0x7f8299909780>]
- outside the context manager:
[<__main__.MyTable1 object at 0x7f8299909780>]
WitcherGeralt ★★ ()
Последнее исправление: WitcherGeralt (всего исправлений: 2 )
Ответ на: комментарий от WitcherGeralt

к сожалению, данные вне контекстного менеджера не доступны:

    print(rres)
    for rr in rres:
        print(rr)
        print(rr.name)



DetachedInstanceError: Instance <MyTable1 at 0x7f51a992b128> is not bound to a Session; attribute refresh operation cannot proceed (Background on this error at: http://sqlalche.me/e/13/bhk3)
scientistpython ()
Ответ на: комментарий от scientistpython

для этого и init прикрутил

class MyTable1(Base):

    __tablename__ = 'MyTable1'

    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)
    cdate = Column(Date, nullable=False)

    def __init__(self, **kwargs):
          self.__dict__.update(kwargs)

def getAll():
    result = []
    with sessionScope() as session:
        rows = session.query(MyTable1).all()
        for row in rows:
            result.append(MyTable1(id=row.id, name=row.name, cdate=row.cdate))

    return result

хотел найти способ передачи вне к.м. результата выборки без поэлементного перекладываниея как сейчас в getAll()

ну и в целом может кто-то раскажет о бестпрактиках )))

scientistpython ()
Ответ на: комментарий от scientistpython

и даже так, в итоге я завел дополнительный класс-дубликат полей ))

@dataclass
class MyData1:
    id: int
    name: str
    cdate: datetime.date

и его юзаю по проекту

def getAll():
    result = []
    with sessionScope() as session:
        rows = session.query(MyTable1).all()
        for row in rows:
            result.append(MyData1(id=row.id, name=row.name, cdate=row.cdate))

мне так спокойнее, но подумываю о том, что не с душком ли код …

scientistpython ()
Ответ на: комментарий от scientistpython

Ну так это объект строки бд, вне контекста он смысла и не имеет. Меняя значение поля, ты же меняешь его и в бд (при вызове коммита), так же у тебя зависымые поля меняются. Всё логично.

А дейтаклассы и есть хорошая практика, а ещё лучше Pydantic, он абы как, но ещё и валидацией / приведением типов занимается.

Чтобы код душком не пах, пиши методы-хелперы from-to, будет удобно хотя бы.

WitcherGeralt ★★ ()
Последнее исправление: WitcherGeralt (всего исправлений: 2 )
Ответ на: комментарий от WitcherGeralt

Чтобы код душком не пах, пиши методы-хелперы from-to, будет удобно хотя бы.

А вот to_B я правильно понимаю? смущает, что в нем cls не нужен

@dataclass
class A:
    x:int

    @classmethod
    def from_B(cls, elem):
        return cls(x=elem.x)

    @classmethod
    def to_B(cls, elem):
        return B(x=elem.x)

@dataclass
class B:
    x: int

b = B(x=1)
a = A.from_B(b)
bb = A.to_B(a)
scientistpython ()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.