python - tutorial - Actualización eficiente de la base de datos utilizando SQLAlchemy ORM
sqlalchemy tutorial español (6)
Aquí hay un ejemplo de cómo resolver el mismo problema sin tener que asignar los 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()
Entonces, para actualizar una instancia de Media, puede hacer algo como esto:
media = Media(id=123, title="Titular Line", slug="titular-line", type="movie")
media.update()
Estoy comenzando una nueva aplicación y estoy mirando el uso de un ORM, en particular, SQLAlchemy.
Digamos que tengo una columna ''foo'' en mi base de datos y quiero incrementarla. En sqlite directo, esto es fácil:
db = sqlite3.connect(''mydata.sqlitedb'')
cur = db.cursor()
cur.execute(''update table stuff set foo = foo + 1'')
Descubrí el equivalente del SQL-builder de SQLAlchemy:
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)
Esto es un poco más lento, pero no hay mucho en él.
Aquí está mi mejor estimación para un enfoque 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()
Esto hace lo correcto, pero toma menos de cincuenta veces más tiempo que los otros dos enfoques. Supongo que es porque tiene que traer todos los datos a la memoria antes de que pueda funcionar con ellos.
¿Hay alguna forma de generar el SQL eficiente usando el ORM de SQLAlchemy? ¿O usando cualquier otro ORM de python? ¿O debería volver a escribir el SQL a mano?
Demasiadas pruebas, lo intentaría:
for c in session.query(Stuff).all():
c.foo = c.foo+1
session.commit()
(IIRC, commit () funciona sin descarga ()).
He descubierto que a veces hacer una consulta grande y luego iterar en Python puede ser hasta 2 órdenes de magnitud más rápido que muchas consultas. Supongo que iterar sobre el objeto de consulta es menos eficiente que iterar sobre una lista generada por el método all () del objeto de consulta.
[Tenga en cuenta el comentario a continuación - esto no aceleró las cosas].
El ORM de SQLAlchemy está destinado a ser utilizado junto con la capa de SQL, no a ocultarlo. Pero debe tener en cuenta una o dos cosas cuando usa el ORM y el SQL simple en la misma transacción. Básicamente, por un lado, las modificaciones de datos ORM solo llegarán a la base de datos cuando elimine los cambios de su sesión. Por otro lado, las instrucciones de manipulación de datos SQL no afectan a los objetos que están en su sesión.
Entonces, si dices
for c in session.query(Stuff).all():
c.foo = c.foo+1
session.commit()
hará lo que dice, irá a buscar todos los objetos de la base de datos, modificará todos los objetos y luego, cuando sea el momento de enjuagar los cambios en la base de datos, actualice las filas una por una.
En cambio, debes hacer esto:
session.execute(update(stuff_table, values={stuff_table.c.foo: stuff_table.c.foo + 1}))
session.commit()
Esto se ejecutará como una consulta como era de esperar, y como por lo menos la configuración de sesión predeterminada expira todos los datos en la sesión de confirmación, no tiene problemas de datos obsoletos.
En la serie 0.5 casi lanzada también puedes usar este método para actualizar:
session.query(Stuff).update({Stuff.foo: Stuff.foo + 1})
session.commit()
Eso básicamente ejecutará la misma declaración SQL que el fragmento anterior, pero también seleccionará las filas modificadas y expirará cualquier dato obsoleto en la sesión. Si sabe que no está utilizando ningún dato de sesión después de la actualización, también puede agregar synchronize_session = False a la declaración de actualización y deshacerse de esa selección.
Hay varias formas de ACTUALIZAR utilizando 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)
Si es debido a la sobrecarga en términos de creación de objetos, entonces probablemente no pueda acelerarse en absoluto con SA.
Si es porque está cargando objetos relacionados, entonces es posible que pueda hacer algo con la carga diferida. ¿Se crean muchos objetos debido a las referencias? (Es decir, obtener un objeto de compañía también obtiene todos los objetos de personas relacionados).
session.query(Clients).filter(Clients.id == client_id_list).update({''status'': status})
session.commit()
Prueba esto =)