将数据流传输到数据库以供Flask应用使用

时间:2019-07-10 15:29:52

标签: python sqlite flask sqlalchemy

我对此任务还很陌生,所以请帮助我确定我缺少的概念。

我正在尝试将数据从API传输到SQLite数据库,并让Flask应用程序使用这些数据。我这样在models.py中定义了模型

# models.py
import os
from flask_sqlalchemy import SQLAlchemy

# Create sqlite db
db = SQLAlchemy()


class MyDataModel(db.Model):
    # Manual table name choice
    __tablename__ = 'table1'

    id = db.Column(db.Integer, primary_key=True)
    created_at = db.Column(db.Text)
    text = db.Column(db.Text)

    def __init__(self, created_at, text):
        self.created_at = created_at
        self.text = text

    def __repr__(self):
        return f"Data: {self.text} ... created at {self.created_at}"

app.py中,我有一个简单的view函数,该函数可以对行进行计数并将服务器发送事件返回到前端以进行实时跟踪。

# app.py
import os
import time
from flask import Flask, render_template, url_for, redirect, Response
from flask_migrate import Migrate
from models import db, MyDataModel
from settings import *


app = Flask(__name__)
logging.basicConfig(level=logging.DEBUG)

basedir = os.path.abspath(os.path.dirname(__file__))

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app)
Migrate(app, db)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/getcount')
def getcount():
    def count_stream():
        count = db.session.query(MyDataModel).count()
        while True:
            yield f"data:{str(count)}\n\n"
            time.sleep(0.5)
    return Response(count_stream(), mimetype='text/event-stream')


if __name__ == "__main__":
    app.run(debug=True, port=PORT)

现在我还有另一个python脚本stream_to_db.py,它从API作为流获取数据,大致像这样

# stream_to_db.py
import logging
import os
from models import db, MyDataModel
from settings import *
from SomeExternalAPI import SomeAPI


def stream_to_db():
    api = SomeAPI(
        API_KEY, API_SECRET_KEY, ACCESS_TOKEN, ACCESS_TOKEN_SECRET
    )

    r = api.request()

    for item in r:
        created_at, text = item['created_at'], item['text']
        logging.info(text)
        datum = MyDataModel(created_at, text)
        db.session.add(datum)
        db.session.commit()

# Stream data to sqlite
stream_to_db()

当我尝试运行此python stream_to_db.py时出现错误

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

我查看了应用程序上下文的文档,但仍然感到困惑。假设我没有使用SQLAlchemy,而是直接使用Python和SQL进行数据插入,那么此stream_to_db.py脚本应独立于Flask应用程序。但是,如果我仍然想将SQLAlchemy用于models.py的语法和模型定义,该怎么办?

从概念上讲,我感觉到db的流传输与Flask应用程序无关,并且应该是一个本质上是while True循环并且永远存在的脚本。 Flask应用程序仅读取数据库,将数据发送到前端,而没有执行其他任何操作。我尝试将stream_to_db()函数放入__main__中的app.py中,但这没有意义,app.run()stream_to_db()本质上都是while True循环并且不能放在一起。

我迷失了方向,错过了关键概念。请帮助并提出正确的方法/最佳做法。我觉得这是一项非常基本的任务,应该已经有最佳实践和一套专用工具。预先感谢!


编辑

为了进行进一步的实验,我将app导入到stream_to_db.py中并添加了

with app.app_context():
    stream_to_db()

现在我可以毫无问题地运行python stream_to_db.py了,但是如果我同时启动Flask应用程序,我会得到很多

Debugging middleware caught exception in streamed response at a point where response headers were already sent.

还有

Traceback (most recent call last):
  File "/Users/<username>/webapps/<appname>/venv/lib/python3.6/site-packages/werkzeug/wsgi.py", line 507, in __next__
    return self._next()
  File "/Users/<username>/webapps/<appname>/venv/lib/python3.6/site-packages/werkzeug/wrappers/base_response.py", line 45, in _iter_encoded
    for item in iterable:
  File "/Users/<username>/webapps/<appname>/app.py", line 33, in count_stream
    count = db.session.query(CardanoTweet).count()
  File "/Users/<username>/webapps/<appname>/venv/lib/python3.6/site-packages/sqlalchemy/orm/scoping.py", line 162, in do
    return getattr(self.registry(), name)(*args, **kwargs)
  File "/Users/<username>/webapps/<appname>/venv/lib/python3.6/site-packages/sqlalchemy/util/_collections.py", line 1012, in __call__
    return self.registry.setdefault(key, self.createfunc())
  File "/Users/<username>/webapps/<appname>/venv/lib/python3.6/site-packages/sqlalchemy/orm/session.py", line 3214, in __call__
    return self.class_(**local_kw)
  File "/Users/<username>/webapps/<appname>/venv/lib/python3.6/site-packages/flask_sqlalchemy/__init__.py", line 136, in __init__
    self.app = app = db.get_app()
  File "/Users/<username>/webapps/<appname>/venv/lib/python3.6/site-packages/flask_sqlalchemy/__init__.py", line 982, in get_app
    'No application found. Either work inside a view function or push'
RuntimeError: No application found. Either work inside a view function or push an application context. See http://flask-sqlalchemy.pocoo.org/contexts/.

看起来像SQLAlchemy找不到该应用程序。不确定app.py中的db.init_app(app)和models.py中的db = SQLAlchemy()是否存在问题。我避免在models.py中使用db = SQLAlchemy(app),因为出于循环依赖的原因,我无法将应用导入模型。


EDIT2

这次,我将所有代码从models.py移至app.py,并使用db = SQLAlchemy(app),删除了db.init_app(app),将导入应用程序保留在stream_to_db.py中,并将应用程序上下文保留在其中。工作了!

我的问题

  • 如何在没有循环依赖性的情况下将模型定义正确地移动到models.py
  • stream_to_db.py中包含应用上下文意味着什么?如果我让大佬们经营着许多工人,那么基本上会有多个Flask应用程序实例。这会引起问题吗?

EDIT3

感谢所有答复。我认为这不是flask make_response with large files的副本。

问题不在于将数据从Flask流传输到客户端,而是将数据从外部API流传输到DB,并让Flask消耗DB中数据的一些统计信息。因此,我不明白为什么从概念上讲流作业应该与Flask完全相关,因为它们是独立的。问题是我在流作业中使用SQLAlchemy进行数据模型和数据库事务,并且SQLAlchemy需要定义Flask应用。这部分让我感到困惑。

使用SQLAlchemy定义的数据模型编写此不断运行的后台作业以将数据流传输到db的正确方法是什么?我是否应该从流代码中删除SQLAlchemy代码,而仅使用SQL,并手动确保架构在将来是否需要进一步迁移的情况下达成共识?

1 个答案:

答案 0 :(得分:-2)

请尝试在创建数据库的model.py中进行以下更改。 db = SQLAlchemy(app)