SQLAlchemy:尝试保存非唯一值后重新保存模型的唯一字段

Dmi*_*kin 7 python database sqlalchemy

在我的SQLAlchemy应用程序中,我有以下模型:

from sqlalchemy import Column, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker
from zope.sqlalchemy import ZopeTransactionExtension

DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))

class MyModel(declarative_base()):
    # ...
    label = Column(String(20), unique=True)

    def save(self, force=False):
        DBSession.add(self)
        if force:
            DBSession.flush()
Run Code Online (Sandbox Code Playgroud)

稍后MyModel我想要label随机生成的每个新对象的代码,如果生成的值已经存在于DB中,则只重新生成它.
我正在尝试执行以下操作:

# my_model is an object of MyModel
while True:
    my_model.label = generate_label()
    try:
        my_model.save(force=True)
    except IntegrityError:
        # label is not unique - will do one more iteration
        # (*)
        pass
    else:
        # my_model saved successfully - exit the loop
        break
Run Code Online (Sandbox Code Playgroud)

但是,如果第一次生成label的不是唯一的并且save()在第二次(或稍后)迭代时调用,则会收到此错误:

 InvalidRequestError: This Session's transaction has been rolled back due to a previous exception during flush. To begin a new transaction with this Session, first issue Session.rollback(). Original exception was: (IntegrityError) column url_label is not unique... 
Run Code Online (Sandbox Code Playgroud)

当我添加DBSession.rollback()位置(*)时,我得到这个:

 ResourceClosedError: The transaction is closed
Run Code Online (Sandbox Code Playgroud)

我该怎么做才能正确处理这种情况?
谢谢

Mar*_*ill 5

如果您的session对象基本上回滚,则必须先创建一个新会话并刷新模型,然后才能重新开始.如果你使用,zope.sqlalchemy你应该使用transaction.commit()transaction.abort()控制事物.所以你的循环看起来像这样:

# you'll also need this import after your zope.sqlalchemy import statement
import transaction

while True:
    my_model.label = generate_label()
    try:
        transaction.commit()
    except IntegrityError:
        # need to use zope.sqlalchemy to clean things up
        transaction.abort()
        # recreate the session and re-add your object
        session = DBSession()
        session.add(my_model)
    else:
        break
Run Code Online (Sandbox Code Playgroud)

我在save这里从对象的方法中删除了session对象的使用.我不能完全确定如何ScopedSession在课堂上使用时如何刷新自己.就个人而言,我认为SqlAlchemy在你的模型中嵌入东西并不能很好地与SqlAlchemy的unit of work方法很好地协调.

如果你的标签对象确实是一个生成的和唯一的值,那么我会同意TokenMacGuy并只使用一个uuid值.

希望有所帮助.