以可测试的方式连接到mongodb

时间:2011-12-17 09:09:03

标签: python mongodb flask pymongo ming

我打算使用Flask和MongoDB(可能还有Ming作为ODM)在python中编写一个webapp。问题是我想保持我的模型和控制器真正分离,其中一个原因是能够在单独的组件上运行简单的单元测试。

现在这是我的问题,在我需要连接到MongoDB的请求生命周期中的某个时刻。每个请求都有一个单独的连接。 Flask提供了一个线程本地对象,它可以包含任何 global 到请求的变量,这似乎是放置mongo连接的好地方。但是,这会在数据层和Flask之间产生一种硬依赖关系,这将使得单独测试或运行它们非常困难。

所以我的问题是,是否有一个优雅的解决方案。我自己想出了几个选项,但它们远非优雅。

首先,我可以给数据模块一个函数,告诉它从哪里获取连接对象。或者类似地将它交给一个可以用来获取新连接的函数。

第二个选项是创建一个类,模块可以使用它来获取与MongoDB的连接,然后创建该类的两个版本,一个使用Flask的全局对象,另一个只是明显连接到MongoDB。

这些对我来说似乎都不健壮或优雅,有没有办法更好地做到这一点?

1 个答案:

答案 0 :(得分:5)

一种方法可以是使用Python模块级单例模式。 创建一个具有'conn'对象的模块,(仅使用普通的PyMongo)

try:
    conn = Connection(max_pool_size=20)
except ConnectionFailure:
    app.logger.critical("Unable to connect to MongoDB")

然后只为PyMongo集合创建一个包装器

class Model(object):
    def __init__(self, table, db = app.config['DB_NAME']):
        self._table = table
        self._db = db

    def find_one(self, spec_or_id=None, *args, **kwargs):
        result = None
        try:
            result = conn[self._db][self._table].find_one(spec_or_id, *args, **kwargs)
        except InvalidName:
            app.logger.critical('invalid DB or Table name')
        finally:
            conn.end_request()

        return result

此处conn.end_request()将导致连接返回到池,并且每个find_one()将从池中获取连接。别担心,它们是线程安全的。

现在您可以使用类似

的模型
result = Model(COLLECTION).find_one({user:'joe'})