如何在没有全局变量的芹菜任务中设置sqlalchemy会话

时间:2015-08-13 22:10:03

标签: python sqlalchemy celery

总结:我想在芹菜任务中使用sqlalchemy会话,而不需要包含该会话的全局变量。

我在一个芹菜任务的项目中使用SQLAlchemy,我有

目前,我有一个全局变量' session'与我的芹菜应用程序设置(celery.py)一起定义,并带有一个工作信号来设置它。

session = scoped_session(sessionmaker())

@celeryd_init.connect
def configure_workers(sender=None, conf=None, **kwargs):
    # load the application configuration
    # db_uri = conf['db_uri']
    engine = create_engine(db_uri)
    session.configure(bind=engine)

在定义任务的模块中,我只需导入会话'并使用它。使用自定义类定义任务,该类在返回后关闭会话:

class DBTask(Task):
    def after_return(self, *args, **kwargs):
        session.remove()

但是效果很好:当使用CELERY_ALWAYS_EAGER = True进行单元测试时,会话无法配置。到目前为止,我发现的唯一解决方案是嘲笑那个会议'在单元测试中运行任务时的变量:

with mock.patch('celerymodule.tasks.session', self.session):
    do_something.delay(...)

虽然它有效但我不想这样做。

有没有办法设置一个不是全局变量的会话,这对于正常的异步行为和没有CELERY_ALWAYS_EAGER = True的工作者都有效?

1 个答案:

答案 0 :(得分:10)

关于custom task classes的官方文档,答案就在我的鼻子底下。

我修改了我用于访问数据库的任务的自定义任务类:

class DBTask(Task):
    _session = None

    def after_return(self, *args, **kwargs):
        if self._session is not None:
            self._session.remove()

    @property
    def session(self):
        if self._session is None:
            _, self._session = _get_engine_session(self.conf['db_uri'],
                                                   verbose=False)

        return self._session

我以这种方式定义我的任务:

@app.task(base=DBTask, bind=True)
def do_stuff_with_db(self, conf, some_arg):
    self.conf = conf
    thing = self.session.query(Thing).filter_by(arg=some_arg).first()

这样,SQLAlchemy会话只会为每个芹菜工作进程创建一次,而且我不需要任何全局变量。

这解决了单元测试的问题,因为SQLAlchemy会话设置现在与芹菜工作者无关。