4.5 阅读源码解决db.create_all的问题

对于Flask来说,文档更适合中高级的开发者,而对于新手不是特别友好。所以以不变应万变。我们可以遇到问题的时候,可以通过阅读源码的时候来解决。

下面我们来看下在第三章的时候,为什么我们的flask_sqlalchemy已经注册了app对象,但是create_all方法还是需要传入app参数,不传就会报错

首先看一下init_app方法的源码

    def init_app(self, app):
        """This callback can be used to initialize an application for the
        use with this database setup.  Never use a database in the context
        of an application not initialized that way or connections will
        leak.
        """
        # 首先是尝试获取app中的配置,如果没有找到则发出警告
        if (
            'SQLALCHEMY_DATABASE_URI' not in app.config and
            # 如果有多个数据库,需要配置这个选项
            'SQLALCHEMY_BINDS' not in app.config
        ):
            warnings.warn(
                'Neither SQLALCHEMY_DATABASE_URI nor SQLALCHEMY_BINDS is set. '
                'Defaulting SQLALCHEMY_DATABASE_URI to "sqlite:///:memory:".'
            )

        # 防御性编程,给dict设置一些默认值
        # setdefault是dict的默认值
        app.config.setdefault('SQLALCHEMY_DATABASE_URI', 'sqlite:///:memory:')
        app.config.setdefault('SQLALCHEMY_BINDS', None)
        app.config.setdefault('SQLALCHEMY_NATIVE_UNICODE', None)
        app.config.setdefault('SQLALCHEMY_ECHO', False)
        app.config.setdefault('SQLALCHEMY_RECORD_QUERIES', None)
        app.config.setdefault('SQLALCHEMY_POOL_SIZE', None)
        app.config.setdefault('SQLALCHEMY_POOL_TIMEOUT', None)
        app.config.setdefault('SQLALCHEMY_POOL_RECYCLE', None)
        app.config.setdefault('SQLALCHEMY_MAX_OVERFLOW', None)
        app.config.setdefault('SQLALCHEMY_COMMIT_ON_TEARDOWN', False)
        track_modifications = app.config.setdefault(
            'SQLALCHEMY_TRACK_MODIFICATIONS', None
        )

        if track_modifications is None:
            warnings.warn(FSADeprecationWarning(
                'SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and '
                'will be disabled by default in the future.  Set it to True '
                'or False to suppress this warning.'
            ))

        app.extensions['sqlalchemy'] = _SQLAlchemyState(self)

        @app.teardown_appcontext
        def shutdown_session(response_or_exc):
            if app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN']:
                if response_or_exc is None:
                    self.session.commit()

            self.session.remove()
            return response_or_exc

create_app 方法的源码

可以看到create_all方法调用了_execute_for_all_tables私有方法,_execute_for_all_tables里面第一行的get_app方法用来获取一个app核心对象

所以通过三个判断,我们可以总结出三个不同的方法来解决我们遇到的问题。 1.在create_all 中传入关键字参数app。也就是我们之前用过的。 2.向堆栈中推入一条app_context,使得current_app不为空。

3.在初始化flask_sqlalchemy对象的时候,传入app参数。

具体选取哪种方式,是根据情况而定的,比如我们当前的情况,就不合适使用第三种方法,因为我们的flask_sqlalchemy对象是在models中的book.py中的,如果用第三种方式,还需要在这里导入app对象。

Last updated

Was this helpful?