2012-02-02 19 views
5

Supongamos que tenemos un objeto con un nombre único. Ahora desea cambiar el nombre de los dos objetos:¿Cómo puedo cambiar dos campos de una fila única dentro de una confirmación usando SQLAlchemy?

Este es el diagrama:

import sqlalchemy as sa 
import sqlalchemy.orm as orm 
from sqlalchemy.ext.declarative import declarative_base 

Base = declarative_base() 

class MyObject(Base): 
    __tablename__ = 'my_objects' 
    id = sa.Column(sa.Integer, primary_key=True) 
    name = sa.Column(sa.Text, unique=True) 

if __name__ == "__main__": 
    engine = sa.create_engine('sqlite:///:memory:', echo=True) 
    Session = orm.sessionmaker(bind=engine) 
    Base.metadata.create_all(engine) 
    session = Session() 

y me gustaría hacer esto:

a = MyObject(name="Max") 
b = MyObject(name="Moritz") 
session.add_all([a, b]) 
session.commit() 

# Now: switch names! 
tmp = a.name 
a.name = b.name 
b.name = tmp 
session.commit() 

Esto arroja una IntegrityError. ¿Hay alguna manera de cambiar estos campos dentro de una confirmación sin este error?

Respuesta

4

Dio unique=True en el campo de nombre, por lo que cuando intenta realizar la ejecución se ejecutará la consulta de actualización, se generará el error.

La situación es cuando cambia el nombre que establecerá en la memoria. Pero cuando intente ejecutar la consulta de actualización, el registro anterior ya existe con el mismo nombre, por lo que dará el IntegrityError.

La forma de cambiar el nombre es

a = MyObject(name="Max") 
b = MyObject(name="Moritz") 
session.add_all([a, b]) 
session.commit() 

# Now: switch names! 
atmp = a.name 
btemp = b.name 

a.name = a.name+btemp # Temp set the any random name 
session.commit() 

b.name = atemp 
a.name = btemp 
session.commit() # Run the update query for update the record. 
+1

Gracias. De hecho, parece ser una propiedad SQL básica que se pasa directamente a través de SQLAlchemy. Aquí hay un enlace a una pregunta relacionada: http://stackoverflow.com/questions/644/swap-unique-indexed-column-values-in-database –

+0

Por alguna razón, también tuve que establecer 'b.name' en valor aleatorio (además de 'a.name') porque, en algunos casos, el segundo' session.commit() 'generaba un IntegrityError. – wil93

+0

El primer commit probablemente debería ser un flush(), porque de lo contrario violaría la semántica de ACID (su cambio no es atómico). –

1

Python permite esta sintaxis (utilizando tuplas):

a.name, b.name = b.name, a.name 

Es absolutamente aceptable cambiar de dos argumentos normales de esta manera, pero no han sido evaluados en su situación, tal vez usted puede darle una oportunidad?

+0

Mientras no lo hago comprometerse con la base de datos que funciona bien. El problema parece ser la base de datos. –

+0

No parece encontrar un SQL de declaración única equivalente para hacer esto, también. Quizás esto es algo que SQLAlchemy no tuvo en cuenta. –

3

Una opción más pura sería la de eliminar una, cambiar el nombre de B, a continuación, volver a agregar un renombrado:

session.delete(a) 
sqlalchemy.orm.session.make_transient(a) 
a.name, b.name = b.name, a.name 
session.flush() 
session.add(a) 
session.commit() 
Cuestiones relacionadas