# 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对象。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://flask-yushu.gitbook.io/yushu/4.flask-he-xin-ji-zhi/4.6-yue-du-yuan-ma-jie-jue-db.createall-de-wen-ti.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
