在SQLAlchemy事件侦听器中未捕获IntegrityError

时间:2016-03-15 07:36:15

标签: python error-handling flask sqlalchemy flask-sqlalchemy

我正在使用Flask和SQLAlchemy构建一个简单的数据库驱动博客。在博客帖子的模型中,我定义了title和slug属性:

class BlogPost(Model):
    ...
    title = Column(String(80))
    slug = Column(String(80), unique=True)

稍后我使用事件监听器自动创建并插入标题中的slug:

@event.listens_for(BlogPost.title, 'set')
def autoslug(target, value, oldvalue, initiator):
    target.slug = slugify(value)

正如预期的那样,如果我尝试向数据库添加帖子,并且帖子的标题评估为与之前帖子相同的slug,则事务将因IntegrityError而失败。我认为在实践中这无论如何都不会成为问题。但只是为了咯咯笑,我尝试了这样的事情:

from sqlalchemy.exc import IntegrityError

@event.listens_for(BlogPost.title, 'set')
def autoslug(target, value, oldvalue, initiator):
    try:
        target.slug = slugify(value)
    except IntegrityError:
        target.slug = slugify(value) + random_string()

random_string可能是任何东西,真的,重点是我尝试的任何东西都没有被执行,因为IntegrityError没有被抓住,我不知道为什么 - 试图添加&使用相同的标题向数据库提交帖子仍然会引发IntegrityError并在我尝试提交时中止事务。我已经看过其他一些关于它的帖子,但答案主要是金字塔特有的,我没有使用。

有人知道我在这里缺少什么吗?

技术参与:Python3,Flask,Flask-Sqlalchemy,Sqlalchemy

1 个答案:

答案 0 :(得分:1)

SQLAlchemy在设置时不会将flush更改为将对象建模到数据库。为了得到错误,您必须执行类似

的操作
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm.session import object_session

@event.listens_for(BlogPost.title, 'set')
def autoslug(target, value, oldvalue, initiator):
    session = object_session(target)
    try:
        with session.begin_nested():
            target.slug = slugify(value)
            session.flush()
    except IntegrityError:
        target.slug = slugify(value) + random_string()

请注意,您必须在嵌套事务(savepoint)中包装可能的完整性违规,否则即使您捕获IntegrityError,您的整个事务也将失败。如果您的数据库不支持保存点或SQLAlchemy实现该想法,那么您将失去运气。