2011-04-20 12 views
6

Tengo una tabla con la siguiente definición declarativa:¿Cómo funciona sqlalchemy manejar restricción única en la definición de la tabla

class Type(Base): 
    __tablename__ = 'Type' 
    id = Column(Integer, primary_key=True) 
    name = Column(String, unique = True) 
    def __init__(self, name): 
     self.name = name 

la columna "Nombre" tiene una restricción única, pero soy capaz de hacer

type1 = Type('name1') 
session.add(type1) 
type2 = Type(type1.name) 
session.add(type2) 

Por lo tanto, como se puede ver, la restricción única no se comprueba en absoluto, ya que he agregado a la sesión 2 objetos con el mismo nombre.

Cuando hago session.commit(), obtengo un error mysql ya que la restricción también está en la tabla mysql.

¿Es posible que sqlalchemy me avise de antemano que no puedo o no lo identifico y no inserta 2 entradas con la misma columna de "nombre"? Si no, ¿debo mantener en la memoria todos los nombres existentes, para poder verificar si existen o no, antes de crear el objeto?

Respuesta

9

SQLAlechemy no maneja uniquness, porque no es posible hacerlo bien. Incluso si realiza un seguimiento de los objetos creados y/o comprueba si existe un objeto con ese nombre, existe una condición de carrera: cualquiera en otro proceso puede insertar un nuevo objeto con el nombre que acaba de marcar. La única solución es bloquear toda la tabla antes de verificar y liberar el bloqueo después de la inserción (algunas bases de datos admiten dicho bloqueo).

+0

voy a ser el único insertar datos allí, así que va a asegurarse de que esto no suceda. Mi pregunta es específicamente sobre cómo sqlalchemy maneja la unicidad de un campo – duduklein

+4

@duduklein No maneja la exclusividad. Y mi respuesta describe por qué. –

6

AFAIK, sqlalchemy no maneja las restricciones de exclusividad en el comportamiento de Python. Esos "únicas = true" declaraciones sólo se utilizan para imponer restricciones de tabla de nivel de base de datos, y sólo entonces si crea la tabla con un comando de sqlalchemy, es decir

Type.__table__.create(engine) 

o algo así. Si crea un modelo SA contra una tabla existente que en realidad no tiene esta restricción presente, será como si no existiera.

Dependiendo de su caso de uso específico, es probable que tenga que utilizar un patrón como

try: 
    existing = session.query(Type).filter_by(name='name1').one() 
    # do something with existing 
except: 
    newobj = Type('name1') 
    session.add(newobj) 

o una variante, o que sólo tendrá que coger la excepción MySQL y recuperar a partir de ahí.

1

.one() tiros dos tipos de excepciones: sqlalchemy.orm.exc.NoResultFound y sqlalchemy.orm.exc.MultipleResultsFound

debe crear ese objeto cuando se produce la primera excepción, si se produce la segunda estás jodido de todos modos y no debe hacer que es peor.

try: 
    existing = session.query(Type).filter_by(name='name1').one() 
# do something with existing 
except NoResultFound: 
    newobj = Type('name1') 
    session.add(newobj) 
3

From the docs

class MyClass(Base): 
    __tablename__ = 'sometable' 
    __table_args__ = (
      ForeignKeyConstraint(['id'], ['remote_table.id']), 
      UniqueConstraint('foo'), 
      {'autoload':True} 
      ) 
Cuestiones relacionadas