Tengo un registro que quiero que exista en la base de datos si no está allí, y si ya está allí (la clave principal existe) quiero que los campos se actualicen al estado actual. Esto a menudo se llama upsert.¿Cómo hacer un upsert con SqlAlchemy?
El siguiente fragmento de código incompleto demuestra lo que funcionará, pero parece excesivamente torpe (especialmente si había muchas más columnas). ¿Cuál es la mejor/mejor manera?
Base = declarative_base()
class Template(Base):
__tablename__ = 'templates'
id = Column(Integer, primary_key = True)
name = Column(String(80), unique = True, index = True)
template = Column(String(80), unique = True)
description = Column(String(200))
def __init__(self, Name, Template, Desc):
self.name = Name
self.template = Template
self.description = Desc
def UpsertDefaultTemplate():
sess = Session()
desired_default = Template("default", "AABBCC", "This is the default template")
try:
q = sess.query(Template).filter_by(name = desiredDefault.name)
existing_default = q.one()
except sqlalchemy.orm.exc.NoResultFound:
#default does not exist yet, so add it...
sess.add(desired_default)
else:
#default already exists. Make sure the values are what we want...
assert isinstance(existing_default, Template)
existing_default.name = desired_default.name
existing_default.template = desired_default.template
existing_default.description = desired_default.description
sess.flush()
¿Hay una manera mejor o menos detallada de hacer esto? Algo como esto sería grande:
sess.upsert_this(desired_default, unique_key = "name")
aunque el unique_key
kwarg obviamente innecesaria (ORM debe ser capaz de entender fácilmente esto) he añadido sólo porque SQLAlchemy tiende a trabajar únicamente con la clave principal. Por ejemplo: he estado observando si Session.merge sería aplicable, pero esto solo funciona en la clave principal, que en este caso es una identificación autoincrementing que no es terriblemente útil para este propósito.
Un caso de uso de ejemplo para esto es simplemente al iniciar una aplicación de servidor que puede haber actualizado sus datos esperados por defecto. es decir: no hay problemas de concurrencia para este upsert.
por qué no puedes hacer que el campo 'name' una clave principal si está único (y fusión funcionaría en este caso). ¿Por qué necesitas una clave principal separada? – abbot
@abbot: No quiero entrar en un debate de campo de id, pero ... la respuesta corta es "claves foráneas". Más largo es que, aunque el nombre es de hecho la única clave única requerida, hay dos problemas. 1) cuando se hace referencia a un registro de plantilla por 50 millones de registros en otra tabla que tiene ese FK como un campo de cadena es loco. Un número entero indexado es mejor, de ahí la columna de ID aparentemente sin sentido. y 2) extendiéndose sobre eso, si la cadena * fue * usada como FK, ahora hay dos ubicaciones para actualizar el nombre si/cuando cambia, lo cual es molesto y plagado de problemas de relación muerta. La identificación * nunca * cambia. – Russ
puede probar una nueva biblioteca (beta) [upsert para python] (https://github.com/seamusabshere/py-upsert) ... es compatible con psycopg2, sqlite3, MySQLdb –