Skip to content

Instantly share code, notes, and snippets.

@codeb2cc
Created August 9, 2012 09:48
Show Gist options
  • Save codeb2cc/3302754 to your computer and use it in GitHub Desktop.
Save codeb2cc/3302754 to your computer and use it in GitHub Desktop.
Sqlalchemy get_or_create implement
# -*- 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
@joshblum
Copy link

line 7 should read:

if defaults is None:
    defaults = {}

This prevents a TypeError: 'NoneType' object is not iterable on line 18 when defaults=None

@mastier
Copy link

mastier commented Nov 5, 2014

or basically:

def _get_or_create(session, model, defaults={}, **kwargs):

@joelbarker2011
Copy link

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