标签归档:flask

application not registered on db

前言

我在用Flask开发的时候,打算写个脚本用作cron定时执行。打算直接用flask中的model和sqlalchemy,并且有些代码可能会在应用中用到。代码终于写完了。。然后得到了错误”RuntimeError(‘application not registered on db ‘” and RuntimeError: application not registered on db instance and no application bound to current context。找到下面的文章

原文链接:http://piotr.banaszkiewicz.org/blog/2012/06/29/flask-sqlalchemy-init_app/

下面是翻译。

我最近正在重构我的Flask应用,并且其中一个最重要的改变是我用到了应用工厂模式。你可以发现很多不同(但类似)使用SQLAlchemy的语法结构。这里是其中一个最常见的:

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app():
    app = Flask(__name__)
    db.init_app(app)
    db.create_all()
    return app

然而,你应该偶然发现这么一个异常:

RuntimeError: application not registered on db instance and no application bound to current context

也许在你的测试用例中,使用db.create_all()期间。

文档并没有对此问题有清楚的描述。经过漫长的寻找,我终于发现有三个办法可以解决此问题。

方法一

创建你的db对象,并作为app的一个参数:

def create_app():
    app = Flask(__name__)
    db = SQLAlchemy(app)
    db.create_all()
    return app

缺点是你没有一个全局的db对象,并且导致你的蓝本无法工作。你将无法容易的创建models。

(顺便说:设置global db加上db = SQLAlchemy(app),对我来说没有效果)

方法二

db.app分配创建好的应用:

db = SQLAlchemy()
def create_app():
    app = Flask(__name__)
    db.init_app()
    db.app = app
    db.create_all()
    return app

这个方法的缺点是:你可能会破坏db内部源码。你不想这么做对吧?

(译者注:我是用这个办法解决了在脚本调用db的问题。并且实际上这并不会破坏内部源码。)

方法三

在应用上下文里面调用db.create_all()(这就是我们问题的所在)

db = SQLAlchemy()
def create_app():
    app = Flask(__name__)
    db.init_app(app)
    with app.test_request_context():
        db.create_all()
    return app

缺点是:这是test_request_context(test!)。但是,毕竟,它对我来说很有效果,我的所有测试用例都通过了!

用你想用的方法。

如果你遇到了问题,并且用了不同的方法解决,请评论让我知道。

额外的提示

你需要在db.create_all发生之前导入models本身:

with app.test_request_context():
    from application.models import Post
    db.create_all()

那么保持你的SQLAlchemy对象实例放在不同的文件里,以皮面不会循环导入,是一个很好的主意:

# application/database.py
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()

# application factory
from application.database import db
def create_app():
    ...
    db.init_app(app)
    ...

# application/models.py
from application.database import db
class Post(db.Model):
    ...

博主后话

以上是翻译全文,后来我在查看db.init_app()源码的时候,找到了get_app函数,发现里面抛出的异常和译文描述的是一样的信息。说明异常是在这里抛出。

    def get_app(self, reference_app=None):
        """Helper method that implements the logic to look up an application.
        """
        if reference_app is not None:
            return reference_app
        if self.app is not None:
            return self.app
        ctx = connection_stack.top
        if ctx is not None:
            return ctx.app
        raise RuntimeError('application not registered on db '
                           'instance and no application bound '
                           'to current context')

但是对于我来说,问题还是没有解决,因为我不是为了测试,事实上我的测试代码没有问题,我就是想在开发环境中测试一下。可以用原文中的方法二。但应该有更好的办法。
在stackoverflow中找到下面的答案:

This has to do with Flask’s application context. When initialized with db.init_app(app), Flask-SQLAlchemy doesn’t know which app is the “current” app (remember, Flask allows for multiple apps in the same interpreter). You could have multiple apps using the same SQLAlchemy instance in the same process, and Flask-SQLAlchemy would need to know which is the “current” one (due to Flask’s context local nature of everything).

If you need to do this during runtime, you must explicitly say which app is the “current” app for all calls. You can do this by changing your code to do the following:

def create_app():
    app = flask.Flask("app")
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
    app.register_blueprint(api)
    db.init_app(app)
    with app.app_context():
        # Extensions like Flask-SQLAlchemy now know what the "current" app
        # is while within this block. Therefore, you can now run........
        db.create_all()

    return app

If you are writing a standalone script that needs the app context, you can push the context at the beginning rather than putting everything in a with block.

create_app().app_context().push()

If you write a command for Flask’s cli the command will automatically have access to the context.

他的方法二感觉就是我想要的,但不是很明白。

我加了也没有用。

app_ctx = app.app_context()
app_ctx.push()
stocks.main()#这里有数据库的读写操作。
app_ctx.pop()

实验来实验去都没有解决掉问题。暂时妥协了。也用了原作者的方法:

db.app = app#单独的任务,这么用也没啥问题。
stocks.main()#这里有数据库的读写操作。

Flask部署方案(ubuntu):Virtualenv+Supervisor+Nginx

首先是概念解释

  1. WSGI服务器,负责我们的app与服务器的交互,常用的有Gunicorn。flask自带的不够健壮,不能用在生产服务器上。
  2. Web服务器,是个HTTP服务器,就相当于tomacat于Java,常用的有Nginx,用作反向代理,负载均衡什么的。

Web服务器中,Nginx是一款面向性能设计的HTTP服务器,相较于Apache、lighttpd具有占有内存少,稳定性高等优势。

部署工具:

  1. Virtualenv,用户创建独立的虚拟的Python运行环境,可以解决版本,依赖等问题。
  2. Supervisor,负责管理应用:应用的开启,关闭,以及多应用的管理等

Virtualenv

安装
sudo pip install virtualenv
使用
#创建虚拟环境
virtualenv venv

#启动虚拟环境
source venv/bin/activat

#停止虚拟环境
deactivate

Supervisor

安装
sudo apt-get install supervisor
使用

配置Supervisor的配置文件的路径:/etc/supervisor/conf.d/*conf , centos是 /etc/supervisord.d/*.ini

cd /etc/supervisor/conf.d/*conf

在这里目录下,我们创建我们应用的配置文件,这样Supervisor才能读取到

sudo vim app.conf

里面的内容输入(内容其实就是执行我们的Python文件):

[program:app]
command = python /home/tengfei/api01/test/TestTo/TODO-orm/app.py


//注,这个配置是和后文中Nginx配置好后对象的9000的转发端口执行的操作
[program:todo]
command = /home/tengfei/api01/test/TestTo/TODO-orm/venv/bin/gunicorn -b 127.0.0.1:9000 app:app
directory = /home/tengfei/api01/test/TestTo/TODO-orm

更改配置文件后,要让supervisor的配置文件生效,执行

supervisorctl reload

启动supervisor:

sudo service supervisor start

重启:

sudo service supervisor restart 

查看supervisor程序当前的状态

sudo supervisorctl

这里面可以使用status指令来查看程序的运行状态

app名字:就是刚才配置配置文件中[program:app]的,比如我这里输入 start app就可以了,以后同上:
start app名字 # 启动app
stop app # 停止app

需要注意的是supervisor发生错误的话,需要它的日志中才能看到

supervisor错误日志地址:
/var/log/supervisor/supervisord.log

参考ubuntu安装和使用supervisor

Ubuntu安装Curl的方法

Nginx

安装
    sudo apt-get update
    sudo apt-get install nginx
    
配置

nginx的也是把程序运行需要的配置文件放到指定的目录下即可

cd /etc/nginx

/etc/nginx子目录下,有两个文件要格外关注关注一下:

sites-available  :可用的配置文件
sites-enabled       :生效的配置文件
    

1:先在sites-available 目录下创建配置文件vim api_app

里面写上:

# api_app内容
server {
        listen 81;

        location /static {
                alias /home/tengfei/api01/test/TestTo/TODO-orm/;
        }

        location / {
                proxy_pass http://127.0.0.1:9000;
        }
}

注,listen 81;:意思是nginx监听的是81端口,我开始监听80端口,结果和默认的冲突了;location /static:是把静态文件转发到后文中的目录下;location /:是把动态请求转发到本机(服务器)的9000端口,上面文件的内容的格式可自行百度。我开始

然后创建软连接到sites-enabled

sudo ln -s ../sites-available/api01_app .

然后重新启动nginx

sudo service nginx reload

//这代表nginx重启成功
 * Reloading nginx configuration nginx                                                       [ OK ] 
    
使用

启动

sudo service nginx restart

重启

sudo service nginx reload
    

查看nginx的状态

sudo service nginx status       
    
    

参考:How To Install Nginx on Ubuntu 14.04 LTS

gunicorn

安装

apt-get install gunicorn

使用

gunicorn -b 0.0.0.0:8080 run:app

尝试在虚拟环境中运行一下程序,看看是否可以成功
//注意下面语句前面的run是指运行的python文件的的名字为run.py的话,执行run:app,如果为app.py那就是app:app

参考文档

Ubuntu 安装mysql和简单操作

“error: command ‘x86_64-linux-gnu-gcc’ failed with exit status 1” in virtualenv

ubuntu下mysql-python模块的安装

how to install the SQLALchemy on the ubuntu?

另外可以用fabric来管理构建和部署。