使用Flask-Admin和Mongoengine为信息系统开发Web管理界面,我需要为所有实体使用Flask-Admin的ModelView
。系统使用几个MongoDB数据库。为了清楚起见,我们假设其中有两个。
通常,人们使用Mongoengine的database aliases管理此类行为。在初始化期间,我们使用Flask-Mongoengine的配置为Flask应用定义了几个别名:
from mongoengine import DEFAULT_CONNECTION_NAME
# Local packages
from config import CurrentConfig
SECOND_DB_ALIAS = "second_db"
app.config['MONGODB_SETTINGS'] = [
{
"ALIAS": DEFAULT_CONNECTION_NAME,
"DB": CurrentConfig.DATABASE_NAME,
},
{
"ALIAS": SECOND_DB_ALIAS,
"DB": CurrentConfig.SECOND_DATABASE_NAME,
},
]
现在,我们可以使用Document
的{{1}}字段,该字段将数据库(由别名表示)绑定到特定实体:
meta
不幸的是,这不符合我的需求,因为两个数据库中都可以存在相同的实体(由相同的 class Entity(Document):
field = StringField()
meta = {'db_alias': SECOND_DB_ALIAS}
类表示)。我想根据应用程序的逻辑设置要查询的数据库。
好吧,随便吧。我们仍然可以使用Mongoengine的context managers动态切换数据库:
Document
(注意:很遗憾,在撰写此问题时是not thread-safe)
这就是我在其余应用程序中所做的。问题是在我的Flask-Admin的 with switch_db(Entity, SECOND_DB_ALIAS):
Entity(field="value").save()
中找不到解决方法。在这种情况下如何设置要查询的数据库别名?
ModelView
答案 0 :(得分:0)
解决了。
花了一些时间检查ModelView
的{{3}},实际上,没有实现任何类似的东西。好吧,必须卷起袖子。
我们必须使用switch_db
上下文管理器将所有查询包装到数据库。 Flask-Admin文档包含source code。因此,如果发生任何数据库查询,就在那里。
通过检查这些方法在ModelView
中的实现,我们可以发现Mongoengine查询只能在get_list
,get_one
,create_model
,{{1}中执行}和update_model
方法。
现在,我们从delete_model
派生并用所需的上下文管理器包装这些方法:
ModelView
然后我们可以像这样在视图中切换数据库:
class SwitchableModelView(ModelView):
database_alias = DEFAULT_CONNECTION_NAME
# Override query methods to add database switchers
def get_list(self, *args, **kwargs):
with switch_db(self.model, self.database_alias):
# It's crucial that the query gets executed immediately,
# while in the switch_db context,
# so we need to override the `execute` argument.
kwargs['execute'] = True
return super().get_list(*args, **kwargs)
def get_one(self, *args, **kwargs):
with switch_db(self.model, self.database_alias):
return super().get_one(*args, **kwargs)
def create_model(self, *args, **kwargs):
with switch_db(self.model, self.database_alias):
return super().create_model(*args, **kwargs)
def update_model(self, *args, **kwargs):
with switch_db(self.model, self.database_alias):
return super().update_model(*args, **kwargs)
def delete_model(self, *args, **kwargs):
with switch_db(self.model, self.database_alias):
return super().delete_model(*args, **kwargs)
如果省略class EntityView(SwitchableModelView):
can_delete = True
can_edit = True
can_view_details = True
can_create = True
can_export = True
# Now it works!
database_alias = SECOND_DB_ALIAS
def __init__(self):
super().__init__(Entity, name="Entities")
,仍将使用默认连接,从而导致原始database_alias
的行为。
我测试了它。有效。
尽管如此,我对这段代码的效率和可靠性有些担忧。正如我提到的,ModelView
目前不是线程安全的。进入和离开上下文时,数据库将在整个switch_db
类上切换。因此,我不确定在多线程Flask应用程序中它在高负载下如何运行,以及是否存在竞争条件问题。
如果有人想出更好的方法来解决此问题或对该代码进行任何改进,我将很高兴听到。