새 응용 프로그램을 시작하고 ORM, 특히 SQLAlchemy를 사용하는 방법을 살펴 봅니다.
내 데이터베이스에 ‘foo’열이 있고이를 증가시키고 싶다고 가정 해 보겠습니다. 직선 sqlite에서는 쉽습니다.
db = sqlite3.connect('mydata.sqlitedb')
cur = db.cursor()
cur.execute('update table stuff set foo = foo + 1')
SQLAlchemy SQL 빌더에 해당하는 것을 알아 냈습니다.
engine = sqlalchemy.create_engine('sqlite:///mydata.sqlitedb')
md = sqlalchemy.MetaData(engine)
table = sqlalchemy.Table('stuff', md, autoload=True)
upd = table.update(values={table.c.foo:table.c.foo+1})
engine.execute(upd)
이것은 약간 느리지 만 그다지 많지 않습니다.
다음은 SQLAlchemy ORM 접근 방식에 대한 최선의 추측입니다.
# snip definition of Stuff class made using declarative_base
# snip creation of session object
for c in session.query(Stuff):
c.foo = c.foo + 1
session.flush()
session.commit()
이것은 옳은 일을하지만 다른 두 가지 접근 방식보다 50 배도 채 걸리지 않습니다. 나는 그것이 작동하기 전에 모든 데이터를 메모리로 가져와야하기 때문이라고 생각합니다.
SQLAlchemy의 ORM을 사용하여 효율적인 SQL을 생성하는 방법이 있습니까? 아니면 다른 파이썬 ORM을 사용하고 계십니까? 아니면 손으로 SQL 작성으로 돌아 가야합니까?
답변
SQLAlchemy의 ORM은 SQL 계층을 숨기지 않고 함께 사용하기위한 것입니다. 그러나 동일한 트랜잭션에서 ORM과 일반 SQL을 사용할 때는 한두 가지를 염두에 두어야합니다. 기본적으로 ORM 데이터 수정은 세션에서 변경 사항을 플러시 할 때만 데이터베이스에 적용됩니다. 반면에 SQL 데이터 조작 문은 세션에있는 개체에 영향을주지 않습니다.
그래서 만약 당신이
for c in session.query(Stuff).all():
c.foo = c.foo+1
session.commit()
그것은 그것이 말하는 것을 수행하고, 데이터베이스에서 모든 개체를 가져오고, 모든 개체를 수정 한 다음 데이터베이스에 대한 변경 사항을 플러시 할 때가되면 행을 하나씩 업데이트합니다.
대신 다음을 수행해야합니다.
session.execute(update(stuff_table, values={stuff_table.c.foo: stuff_table.c.foo + 1}))
session.commit()
이것은 예상대로 하나의 쿼리로 실행되며 적어도 기본 세션 구성은 커밋시 세션의 모든 데이터를 만료하므로 오래된 데이터 문제가 없습니다.
거의 출시 된 0.5 시리즈에서는이 방법을 사용하여 업데이트 할 수도 있습니다.
session.query(Stuff).update({Stuff.foo: Stuff.foo + 1})
session.commit()
기본적으로 이전 스 니펫과 동일한 SQL 문을 실행하지만 변경된 행을 선택하고 세션의 오래된 데이터를 만료합니다. 업데이트 후 세션 데이터를 사용하지 않는다는 것을 알고 있다면 synchronize_session=False
업데이트 문에 추가 하고 해당 선택을 제거 할 수도 있습니다 .
답변
session.query(Clients).filter(Clients.id == client_id_list).update({'status': status})
session.commit()
이것을 시도하십시오 =)
답변
sqlalchemy를 사용하여 UPDATE하는 방법에는 여러 가지가 있습니다.
1) for c in session.query(Stuff).all():
c.foo += 1
session.commit()
2) session.query().\
update({"foo": (Stuff.foo + 1)})
session.commit()
3) conn = engine.connect()
stmt = Stuff.update().\
values(Stuff.foo = (Stuff.foo + 1))
conn.execute(stmt)
답변
다음은 필드를 수동으로 매핑하지 않고도 동일한 문제를 해결하는 방법의 예입니다.
from sqlalchemy import Column, ForeignKey, Integer, String, Date, DateTime, text, create_engine
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.attributes import InstrumentedAttribute
engine = create_engine('postgres://postgres@localhost:5432/database')
session = sessionmaker()
session.configure(bind=engine)
Base = declarative_base()
class Media(Base):
__tablename__ = 'media'
id = Column(Integer, primary_key=True)
title = Column(String, nullable=False)
slug = Column(String, nullable=False)
type = Column(String, nullable=False)
def update(self):
s = session()
mapped_values = {}
for item in Media.__dict__.iteritems():
field_name = item[0]
field_type = item[1]
is_column = isinstance(field_type, InstrumentedAttribute)
if is_column:
mapped_values[field_name] = getattr(self, field_name)
s.query(Media).filter(Media.id == self.id).update(mapped_values)
s.commit()
따라서 Media 인스턴스를 업데이트하려면 다음과 같이 할 수 있습니다.
media = Media(id=123, title="Titular Line", slug="titular-line", type="movie")
media.update()
답변
테스트를 거치지 않고 시도해 보겠습니다.
for c in session.query(Stuff).all():
c.foo = c.foo+1
session.commit()
(IIRC, commit ()은 flush ()없이 작동합니다).
나는 때때로 큰 쿼리를 수행 한 다음 파이썬에서 반복하는 것이 많은 쿼리보다 최대 2 배 더 빠를 수 있음을 발견했습니다. 쿼리 객체를 반복하는 것이 쿼리 객체의 all () 메서드에 의해 생성 된 목록을 반복하는 것보다 덜 효율적이라고 가정합니다.
[아래에 주석을 달아주세요. 속도가 전혀 빨라지지는 않았습니다].
답변
객체 생성의 오버 헤드 때문이라면 아마도 SA로 속도를 높일 수 없을 것입니다.
관련 객체를로드하기 때문이라면 지연로드로 작업을 수행 할 수 있습니다. 참조로 인해 생성되는 개체가 많이 있습니까? (IE, Company 개체를 가져 오면 관련된 모든 People 개체도 가져옵니다).