In my SQLAlchemy app I have the following model:
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()
Later in code for every new MyModel objects I want to generate label randomly, and just regenerate it if the generated value is already exist in DB.
I’m trying to do the following:
# 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
but get this error in case when first generated label is not unique and save() called on the second (or later) iteration:
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...
When I add DBSession.rollback() in the position (*) I get this:
ResourceClosedError: The transaction is closed
What should I do to handle this situation correctly?
Thanks
If your
sessionobject rolls back essentially you have to create a new session and refresh your models before you can start again. And if you are usezope.sqlalchemyyou should be usingtransaction.commit()andtransaction.abort()to control things. So your loop would look something like this:I’ve pulled the use of the session object out of the object’s
savemethod here. I am not entirely sure how theScopedSessionrefreshes itself when being used at the class level as you have done. Personally, I think embeddingSqlAlchemystuff inside your models doesn’t really work well with SqlAlchemy’sunit of workapproach to things any how.If your label object really is a generated and unique value, then I would agree with
TokenMacGuyand just use auuidvalue.Hope that helps.