# 4.5 阅读源码解决db.create\_all的问题

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

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

## 首先看一下init\_app方法的源码

```python
    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 方法的源码

```python
    def _execute_for_all_tables(self, app, bind, operation, skip_tables=False):
        app = self.get_app(app)

        if bind == '__all__':
            binds = [None] + list(app.config.get('SQLALCHEMY_BINDS') or ())
        elif isinstance(bind, string_types) or bind is None:
            binds = [bind]
        else:
            binds = bind

        for bind in binds:
            extra = {}
            if not skip_tables:
                tables = self.get_tables_for_bind(bind)
                extra['tables'] = tables
            op = getattr(self.Model.metadata, operation)
            op(bind=self.get_engine(app, bind), **extra)

    def create_all(self, bind='__all__', app=None):
        """Creates all tables.

        .. versionchanged:: 0.12
           Parameters were added
        """
        self._execute_for_all_tables(app, bind, 'create_all')
```

可以看到create\_all方法调用了\_execute\_for\_all\_tables私有方法，\_execute\_for\_all\_tables里面第一行的get\_app方法用来获取一个app核心对象

```python
    def get_app(self, reference_app=None):
        """Helper method that implements the logic to look up an
        application."""

        # 如果关键字参数的app不为空，就返回参数的app
        if reference_app is not None:
            return reference_app

        # 如果current_app不为空，则返回current_app
        if current_app:
            return current_app._get_current_object()

        # 如果对象的app属性不为空，则返回对象的app属性
        if self.app is not None:
            return self.app

        raise RuntimeError(
            'No application found. Either work inside a view function or push'
            ' an application context. See'
            ' http://flask-sqlalchemy.pocoo.org/contexts/.'
        )
```

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

```python
 with app.app_context():
        db.create_all()
```

3.在初始化flask\_sqlalchemy对象的时候，传入app参数。

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