代码之家  ›  专栏  ›  技术社区  ›  Henry Kim

如何管理应用程序工厂模式中的额外模块?

  •  1
  • Henry Kim  · 技术社区  · 7 年前

    def create_app(config):
        app.config.from_object(config)
        sentry.init(app)
        ...
    
        return app
    

    但是如何管理需要该配置但无法在应用程序创建时初始化的额外模块?

    所以我想做一些

    def create_app(config):
        some_module_obj = Module(host=config.host, port=config.port)
    
        app.config.from_object(config)
        sentry.init(app)
    
        return some_module_obj, app
    

    而不是

    # I don't want to use `config` outside of the `create_app` function!
    some_module_obj = Module(host=config.host, port=config.port)
    
    def create_app(config):
        app.config.from_object(config)
        sentry.init(app)
    
        return app
    
    2 回复  |  直到 7 年前
        1
  •  5
  •   Danila Ganchar    7 年前

    inject + Flask 在评论中。据我所知,主要问题与 Flask + configuration + initialization . 这只是一个例子。

    from flask import Flask
    
    from api import bp
    from configurator import configure
    
    
    def create_app():
        app = Flask(__name__)
        # configure Flask app config as you wish... (app.config.from_object(config))
        # just some settings for demonstration
        app.config.update(dict(
            MODULE1_TIMER=1,
            MODULE2_LIMIT=2,
        ))
        # configure inject using app context and Flask config
        with app.app_context():
            configure()
        # demo blueprint
        app.register_blueprint(bp)
    
        return app
    
    
    if __name__ == '__main__':
        create_app().run(debug=True)
    

    假设我们有一些模块:

    # mod1.py
    class Module1:
        def __init__(self, timer: int) -> None:
            self._timer = timer
    
    # mod2.py
    class Module2:
        def __init__(self, limit: int) -> None:
            self._limit = limit
    
        def get_limit(self):
            return self._limit
    
    # mod3.py - works with mod1 and mod2
    class Module3:
        def __init__(self, module1, module2) -> None:
            self._module1 = module1
            self._module2 = module2
    
        def get_limit(self):
            return self._module2.get_limit()
    

    配置器.py

    import inject
    from flask import current_app
    
    from mod1 import Module1
    from mod2 import Module2
    from mod3 import Module3
    
    
    @inject.params(
        module1=Module1,
        module2=Module2,
    )
    def _init_module3(module1, module2):
        # module1 and module2 are injected instances
        return Module3(module1, module2)
    
    
    def _injector_config(binder):
        # initialization of Module1 and Module2 using Flask config
        binder.bind(Module1, Module1(current_app.config['MODULE1_TIMER']))
        binder.bind(Module2, Module2(current_app.config['MODULE2_LIMIT']))
        # initialization of Module3 using injected Module1 + Module2
        # you can use bind_to_constructor + any function
        binder.bind_to_constructor(Module3, _init_module3)
    
    
    def configure():
        def config(binder):
            binder.install(_injector_config)
            # one more binder.install... etc...
    
        inject.clear_and_configure(config)
    

    api.py公司

    import inject
    from flask import Blueprint, jsonify
    
    from mod1 import Module1
    from mod2 import Module2
    from mod3 import Module3
    
    bp = Blueprint('api', __name__)
    
    
    @bp.route('/test')
    def test():
        # get instances which was created using inject
        return jsonify(dict(
            module1=str(type(inject.instance(Module1))),
            module2=str(type(inject.instance(Module2))),
            module3=str(type(inject.instance(Module3))),
        ))
    
    
    # you can inject something as arg
    @bp.route('/test2')
    @inject.params(module3=Module3)
    def test2(module3: Module3):
        return jsonify(dict(module3=str(type(module3))))
    
    
    @bp.route('/test3')
    def test3():
        # you can inject something into anything
        class Example:
            module3 = inject.attr(Module3)
    
            @inject.params(module2=Module2)
            def __init__(self, module2: Module2) -> None:
                self.module2 = module2
    
        return jsonify({
            'MODULE2_LIMIT': Example.module3.get_limit(),
            'example': dir(Example()),
        })
    

    运行服务器,打开 /test , test2 /test3 .

    关于福利的几句话:

    • 对当前应用程序、配置/上下文等的依赖性较低。
    • 减少递归导入的问题

    希望这有帮助。

        2
  •  0
  •   Henry Kim    7 年前

    决定为初始化对象创建自定义类作为工厂模式。

    例如:

    class CustomFactory(metaclass=ABCMeta):
        @abstractmethod
        def init_factory(self, config):
            pass
    
        @property
        @abstractmethod
        def app(self):
            pass
    
        def __getattr__(self, item):
            return getattr(self.app, item)
    
    
    class RQSchedulerFactory(CustomFactory):
        def __init__(self):
            self._app = None
    
        def init_factory(self, config):
            self._app = Scheduler(connection=Redis(host=config.REDIS_HOST, port=config.REDIS_PORT))
    
        @property
        def app(self):
            return self._app
    
    
    class FireDBFactory(CustomFactory):
        @property
        def app(self):
            return self._app
    
        def __init__(self):
            self._app = None
    
        def init_factory(self, config):
            cred = credentials.Certificate(config.FIREBASE_KEY_FILE)
            firebase_admin.initialize_app(cred)
    
            self._app = firestore.client()
    

    __init__.py (其中 create_app (功能)

    scheduler = RQSchedulerFactory()
    fire_db = FireDBFactory()
    

    而且在 创建应用程序 函数,初始化如下:

    def create_app(config):
        app.config.from_object(config)
    
        # Scheduler initialization
        scheduler.init_factory(config)
    
        # Fire store initialization
        fire_db.init_factory(config)
    
    推荐文章