SQLAlchemy有相当于Django的get_or_create吗?

我想从数据库中获取一个对象,如果它已经存在(基于提供的参数),或者创build它,如果它不存在。

Django的get_or_create (或源 )这样做。 在SQLAlchemy中有没有等价的快捷方式?

我现在正在写这个明确的:

 def get_or_create_instrument(session, serial_number): instrument = session.query(Instrument).filter_by(serial_number=serial_number).first() if instrument: return instrument else: instrument = Instrument(serial_number) session.add(instrument) return instrument 

这基本上是做到这一点,没有捷径现成的AFAIK。

你可以概括它的过程:

 def get_or_create(session, model, defaults=None, **kwargs): instance = session.query(model).filter_by(**kwargs).first() if instance: return instance, False else: params = dict((k, v) for k, v in kwargs.iteritems() if not isinstance(v, ClauseElement)) params.update(defaults or {}) instance = model(**params) session.add(instance) return instance, True 

在@WoLpH的解决scheme之后,这是我工作的代码(简单版本):

 def get_or_create(session, model, **kwargs): instance = session.query(model).filter_by(**kwargs).first() if instance: return instance else: instance = model(**kwargs) session.add(instance) session.commit() return instance 

有了这个,我可以get_or_create我的模型的任何对象。

假设我的模型对象是:

 class Country(Base): __tablename__ = 'countries' id = Column(Integer, primary_key=True) name = Column(String, unique=True) 

要获得或创build我的对象,我写:

 myCountry = get_or_create(session, Country, name=countryName) 

我一直在玩这个问题,并以相当强大的解决scheme结束:

 def get_one_or_create(session, model, create_method='', create_method_kwargs=None, **kwargs): try: return session.query(model).filter_by(**kwargs).one(), False except NoResultFound: kwargs.update(create_method_kwargs or {}) created = getattr(model, create_method, model)(**kwargs) try: session.add(created) session.flush() return created, True except IntegrityError: session.rollback() return session.query(model).filter_by(**kwargs).one(), True 

我只是写了一个相当广泛的博客文章的所有细节,但为什么我使用这个几个相当的想法。

  1. 它解包成一个元组,告诉你该对象是否存在。 这在您的工作stream程中通常很有用。

  2. 该函数能够使用@classmethod装饰的创build者函数(以及特定于它们的属性)。

  3. 当您有多个进程连接到数据存储时,该解决scheme可以防止竞争条件。

编辑:我已经将session.commit()更改为session.flush()如本博文所述 。 请注意,这些决定是特定于所使用的数据存储(Postgres在这种情况下)。

编辑2:我已经更新使用{}作为默认值的函数,因为这是典型的Python陷阱。 感谢您的评论 ,奈杰尔! 如果你对这个问题感到好奇,看看这个StackOverflow问题和这篇博文 。

我想我只是在寻找同样的事情。 这个SQLALchemy配方的工作很好,优雅。

埃里克的优秀答案的修改版本

 def get_one_or_create(session, model, create_method='', create_method_kwargs=None, **kwargs): try: return session.query(model).filter_by(**kwargs).one(), True except NoResultFound: kwargs.update(create_method_kwargs or {}) try: with session.begin_nested(): created = getattr(model, create_method, model)(**kwargs) session.add(created) return created, False except IntegrityError: return session.query(model).filter_by(**kwargs).one(), True 
  • 使用嵌套事务只回滚添加新项目而不是回滚所有内容(请参阅此答案以使用SQLite嵌套事务)
  • 移动create_method 。 如果创build的对象具有关系,并且通过这些关系分配了成员,则会自动添加到会话中。 例如,创build一book ,其中有user_iduser作为对应关系,然后在book.user=<user object>里面的book.user=<user object>book添加到会话中。 这意味着create_method必须在里面with从最终的回滚中受益。 请注意, begin_nested自动触发一次刷新。

请注意,如果使用MySQL,则必须将事务隔离级别设置为READ COMMITTED而不是REPEATABLE READ才能工作。 Django的get_or_create (和here )使用相同的策略,另请参阅Django 文档 。

最接近的语义可能是:

 def get_or_create(model, **kwargs): """SqlAlchemy implementation of Django's get_or_create. """ session = Session() instance = session.query(model).filter_by(**kwargs).first() if instance: return instance, False else: instance = model(**kwargs) session.add(instance) session.commit() return instance, True 

不确定在sqlalchemy中依赖于全局定义的Session有多洁净,但是Django版本没有连接,所以…

返回的元组包含实例和一个指示实例是否被创build的布尔值(即,如果我们从数据库中读取实例,则返回False)。

Django的get_or_create经常用于确保全局数据可用,所以我尽可能早地提交。

根据您所采用的隔离级别,以上解决scheme都不可行。 我find的最佳解决scheme是以下forms的RAW SQL:

 INSERT INTO table(f1, f2, unique_f3) SELECT 'v1', 'v2', 'v3' WHERE NOT EXISTS (SELECT 1 FROM table WHERE f3 = 'v3') 

无论隔离级别和并行度如何,这都是事务安全的。

注意:为了使其更有效率,对于独特的列有一个INDEX是明智的。

我稍微简化@Kevin。 解决scheme来避免将整个函数包装在if / else语句中。 这样,只有一个return ,我觉得更清洁:

 def get_or_create(session, model, **kwargs): instance = session.query(model).filter_by(**kwargs).first() if not instance: instance = model(**kwargs) session.add(instance) return instance