2011-04-20 296 views
6

我有以下声明定义的表:如何处理SQLAlchemy的表定义唯一约束

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

列“名”具有独特的约束,但我能做到

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

因此,可以看到,唯一的约束根本不被检查,因为我已经向会话2添加了具有相同名称的对象。

当我做session.commit(),我得到一个mysql错误,因为约束也在mysql表中。

sqlalchemy是否有可能提前告诉我,我无法做出或识别它,并且没有用相同的“名称”柱插入2个条目? 如果不是,我应该在内存中保存所有现有的名称,以便在创建对象之前检查它们是否存在?

回答

9

SQLAlechemy不处理独特性,因为它不可能做到很好。即使您跟踪创建的对象和/或检查具有该名称的对象是否存在竞争条件:其他进程中的任何人都可以使用刚刚检查的名称插入新对象。唯一的解决方案是在检查之前锁定整个表并在插入后释放锁(一些数据库支持这种锁定)。

+0

我是唯一一个将数据插入那里,所以我会确保这种情况不会发生。我的问题具体是关于sqlalchemy如何处理字段的唯一性 – duduklein 2011-04-20 16:53:39

+4

@duduklein它不处理唯一性。我的回答描述了为什么。 – 2011-04-21 05:00:18

6

AFAIK,sqlalchemy不处理python行为中的唯一性约束。那些“独特=真”的声明仅用于将强加数据库级别表约束,才把如果创建使用SQLAlchemy的命令表,即

Type.__table__.create(engine) 

或一些这样的。如果您针对实际上没有此约束的现有表创建SA模型,那么它就好像不存在一样。

根据您的具体使用情况下,你可能不得不使用一种模式像

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

或变体,或者你只是必须抓住MySQL的异常,并从那里恢复。

1

.one()抛出两种异常: sqlalchemy.orm.exc.NoResultFoundsqlalchemy.orm.exc.MultipleResultsFound

,当第一异常发生时,您应创建一个对象,如果你就有点犯糊涂,不应该做出更糟糕的是发生在第二位。

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