如何为 Flask-Admin 视图绑定不同的 Mongoengine 数据库别名?
How to bind different Mongoengine database aliases for Flask-Admin views?
使用 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
的 meta
字段,它将数据库(由其别名表示)绑定到特定实体:
class Entity(Document):
field = StringField()
meta = {'db_alias': SECOND_DB_ALIAS}
不幸的是,它不适合我的需要,因为 相同的实体(由相同的 Document
class 表示)可以存在于两个数据库中.我想根据应用程序的逻辑设置我查询的数据库。
好吧,随便吧。我们仍然可以使用 Mongoengine 的 context managers:
动态切换数据库
with switch_db(Entity, SECOND_DB_ALIAS):
Entity(field="value").save()
(注意:很遗憾,写这个问题的时候是not thread-safe)
这就是我在应用程序的其余部分所做的。问题是 我无法在我的 Flask-Admin ModelView
s 中找到执行相同操作的方法。在这种情况下如何设置要查询的数据库的别名?
class EntityView(ModelView):
can_delete = True
can_edit = True
can_view_details = True
can_create = True
can_export = True
# No such or similar attribute!
database_alias = SECOND_DB_ALIAS
def __init__(self):
super().__init__(Entity, name="Entities")
admin = Admin(app, name='Admin Panel', template_mode='bootstrap3')
admin.add_view(EntityView())
解决了。
花了一些时间检查 ModelView
的 source code,并且确实没有实现类似的东西。得撸起袖子了
我们必须使用 switch_db
上下文管理器将所有查询包装到数据库中。 Flask-Admin 文档包含 a list of methods needed to implement a model backend。因此,如果发生任何数据库查询,它就在那里。
通过检查这些方法在ModelView
中的实现,我们可以发现Mongoengine查询只能在get_list
、get_one
、create_model
、[=中执行18=] 和 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
目前不是线程安全的。数据库在进入和离开上下文时全开Entity
class。所以,我不确定它在多线程 Flask 应用程序的高负载下会如何表现,以及是否会出现竞争条件问题。
如果有人提出解决问题的更好方法或对此代码进行任何改进,我将很高兴听到。
使用 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
的 meta
字段,它将数据库(由其别名表示)绑定到特定实体:
class Entity(Document):
field = StringField()
meta = {'db_alias': SECOND_DB_ALIAS}
不幸的是,它不适合我的需要,因为 相同的实体(由相同的 Document
class 表示)可以存在于两个数据库中.我想根据应用程序的逻辑设置我查询的数据库。
好吧,随便吧。我们仍然可以使用 Mongoengine 的 context managers:
动态切换数据库 with switch_db(Entity, SECOND_DB_ALIAS):
Entity(field="value").save()
(注意:很遗憾,写这个问题的时候是not thread-safe)
这就是我在应用程序的其余部分所做的。问题是 我无法在我的 Flask-Admin ModelView
s 中找到执行相同操作的方法。在这种情况下如何设置要查询的数据库的别名?
class EntityView(ModelView):
can_delete = True
can_edit = True
can_view_details = True
can_create = True
can_export = True
# No such or similar attribute!
database_alias = SECOND_DB_ALIAS
def __init__(self):
super().__init__(Entity, name="Entities")
admin = Admin(app, name='Admin Panel', template_mode='bootstrap3')
admin.add_view(EntityView())
解决了。
花了一些时间检查 ModelView
的 source code,并且确实没有实现类似的东西。得撸起袖子了
我们必须使用 switch_db
上下文管理器将所有查询包装到数据库中。 Flask-Admin 文档包含 a list of methods needed to implement a model backend。因此,如果发生任何数据库查询,它就在那里。
通过检查这些方法在ModelView
中的实现,我们可以发现Mongoengine查询只能在get_list
、get_one
、create_model
、[=中执行18=] 和 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
目前不是线程安全的。数据库在进入和离开上下文时全开Entity
class。所以,我不确定它在多线程 Flask 应用程序的高负载下会如何表现,以及是否会出现竞争条件问题。
如果有人提出解决问题的更好方法或对此代码进行任何改进,我将很高兴听到。