Created
August 9, 2012 09:48
-
-
Save codeb2cc/3302754 to your computer and use it in GitHub Desktop.
Sqlalchemy get_or_create implement
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
from sqlalchemy.exc import IntegrityError | |
from sqlalchemy.sql.expression import ClauseElement | |
def _get_or_create(session, model, defaults=None, **kwargs): | |
try: | |
query = session.query(model).filter_by(**kwargs) | |
instance = query.first() | |
if instance: | |
return instance, False | |
else: | |
session.begin(nested=True) | |
try: | |
params = dict((k, v) for k, v in kwargs.iteritems() if not isinstance(v, ClauseElement)) | |
params.update(defaults) | |
instance = model(**params) | |
session.add(instance) | |
session.commit() | |
return instance, True | |
except IntegrityError as e: | |
session.rollback() | |
instance = query.one() | |
return instance, False | |
except Exception as e: | |
raise e |
Note that this avoids one race condition (a create that happens between the query on line 10 and the create on line 22 will be caught on line 25), but introduces another: if the newly-created record that raises the IntegrityError is deleted between line 26 and line 27, then line 27 will raise NoResultFound.
I do not have a helpful solution for this problem; race conditions are tricky beasts.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
or basically: