Pergunta

Estou começando uma nova aplicação e olhando para usar um ORM -., Em particular, SQLAlchemy

Say Eu tenho uma coluna de 'foo' no meu banco de dados e eu quero incrementá-lo. Em sqlite reta, isso é fácil:

db = sqlite3.connect('mydata.sqlitedb')
cur = db.cursor()
cur.execute('update table stuff set foo = foo + 1')

Eu descobri o SQLAlchemy SQL-construtor equivalentes:

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)

Este é um pouco mais lento, mas não há muito nele.

Aqui está o meu melhor palpite para uma abordagem 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()

Este faz a coisa certa, mas é preciso um pouco menos de cinqüenta vezes, enquanto as outras duas abordagens. Eu presumo que isso é porque ele tem que trazer todos os dados na memória antes que ele possa trabalhar com ele.

Existe alguma maneira para gerar o SQL eficiente usando ORM SQLAlchemy? Ou usando qualquer outro ORM python? Ou devo apenas voltar a escrever o SQL à mão?

Foi útil?

Solução

ORM SQLAlchemy se destina a ser utilizado em conjunto com a camada de SQL, não escondê-lo. Mas você tem que manter uma ou duas coisas em mente quando se utiliza o ORM e SQL simples na mesma transação. Basicamente, de um lado, as modificações de dados ORM só vai bater o banco de dados quando você liberar as mudanças de sua sessão. Do outro lado, SQL instruções de manipulação de dados não afetam os objetos que estão na sua sessão.

Então, se você dizer

for c in session.query(Stuff).all():
    c.foo = c.foo+1
session.commit()

ele vai fazer o que ele diz, ir buscar todos os objetos do banco de dados, modificar todos os objetos e, em seguida, quando é hora de liberar as alterações para o banco de dados, atualizar as linhas, um por um.

Em vez disso você deve fazer isso:

session.execute(update(stuff_table, values={stuff_table.c.foo: stuff_table.c.foo + 1}))
session.commit()

Isto irá executar como uma consulta como seria de esperar, e porque pelo menos a configuração de sessão padrão expira todos os dados na sessão de cometer você não tem quaisquer problemas de dados obsoleto.

Nos quase-lançado 0,5 série, você também pode usar este método para atualizar:

session.query(Stuff).update({Stuff.foo: Stuff.foo + 1})
session.commit()

Isso basicamente executar a mesma instrução SQL como o trecho anterior, mas também selecionar as linhas alteradas e expiram quaisquer dados antigos na sessão. Se você sabe que você não está usando quaisquer dados da sessão após a atualização Você também pode adicionar synchronize_session = False para a instrução de atualização e se livrar desse seleto.

Outras dicas

session.query(Clients).filter(Clients.id == client_id_list).update({'status': status})
session.commit()

Tente esta =)

Existem várias maneiras de atualizar usando sqlalchemy

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)

Aqui está um exemplo de como resolver o mesmo problema sem ter que mapear os campos manualmente:

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

Assim, para atualizar uma instância de mídia, você pode fazer algo como isto:

media = Media(id=123, title="Titular Line", slug="titular-line", type="movie")
media.update()

testes withough, eu tentaria:

for c in session.query(Stuff).all():
     c.foo = c.foo+1
session.commit()

(IIRC, commit () funciona sem flush ()).

Descobri que às vezes fazer uma consulta grande e, em seguida, repetindo em python pode ser de até 2 ordens de grandeza mais rápido do que muitas consultas. Presumo que a iteração sobre o objeto de consulta é menos eficiente do que a iteração através de uma lista gerada pelo método all () do objeto de consulta.

[Por favor comentário em nota abaixo - este não fez acelerar as coisas em tudo]

.

Se é por causa da sobrecarga em termos de criação de objetos, em seguida, ele provavelmente não pode ser acelerado em tudo com SA.

Se é porque ele está carregando-se objetos relacionados, então você pode ser capaz de fazer algo com o carregamento lento. Existem lotes de objetos a ser criado devido a referências? (IE, obtendo um objeto Companhia também recebe todas as pessoas relacionadas objetos).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top