【问题标题】:How to bind different Mongoengine database aliases for Flask-Admin views?如何为 Flask-Admin 视图绑定不同的 Mongoengine 数据库别名?
【发布时间】:2019-02-05 14:29:56
【问题描述】:

使用 Flask-Admin 和 Mongoengine 为信息系统开发 Web 管理界面,我的所有实体都需要 Flask-Admin 的 ModelViews。该系统使用多个 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,
        },
    ]

现在我们可以使用Documentmeta 字段,它将数据库(由其别名表示)绑定到特定实体:

    class Entity(Document):
        field = StringField()

        meta = {'db_alias': SECOND_DB_ALIAS}

不幸的是,它不适合我的需求,因为两个数据库中可能存在相同的实体(由相同的Document 类表示)。我想根据应用程序的逻辑设置我查询的数据库。

好吧,随便。我们仍然可以使用 Mongoengine 的context managers 动态切换数据库:

    with switch_db(Entity, SECOND_DB_ALIAS):
         Entity(field="value").save()

(注意:不幸的是,在写这个问题的时候是not thread-safe

这就是我在应用程序的其余部分所做的。问题是 我在 Flask-Admin 的 ModelViews 中找不到相同的方法。这种情况下如何设置查询的数据库别名?

    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())

【问题讨论】:

    标签: python flask mongoengine flask-admin flask-mongoengine


    【解决方案1】:

    解决了。

    花了一些时间检查ModelViewsource code,事实上,没有实现任何类似的东西。好吧,只好撸起袖子了。

    我们必须使用switch_db 上下文管理器将所有查询包装到数据库中。 Flask-Admin 文档包含a list of methods needed to implement a model backend。因此,如果发生任何数据库查询,它就在那里。

    通过检查ModelView中这些方法的实现,我们可以发现Mongoengine查询只能在get_listget_onecreate_modelupdate_modeldelete_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 类上打开。所以,我不确定它在多线程 Flask 应用程序的高负载下会如何表现,以及是否会出现竞争条件问题。

    如果有人提出解决问题的更好方法或对此代码进行任何改进,我将很高兴听到。

    【讨论】:

      猜你喜欢
      • 2021-08-21
      • 1970-01-01
      • 1970-01-01
      • 2023-03-15
      • 1970-01-01
      • 2019-09-17
      • 1970-01-01
      • 2015-05-17
      • 1970-01-01
      相关资源
      最近更新 更多