【问题标题】:Flask, concurrent.futures and SQLAlchemy - No application found: work inside a view function or push an application contextFlask、concurrent.futures 和 SQLAlchemy - 未找到应用程序:在视图函数内工作或推送应用程序上下文
【发布时间】:2019-02-15 14:19:33
【问题描述】:

我正在构建一个 Flask 应用程序,它需要一个后台进程,导致上传到 SQLAlchemy 数据库。

相关sn-ps:

from flask_sqlalchemy import SQLAlchemy
import concurrent.futures
import queue
from models import Upload_Tracks

app = Flask(__name__)
db.init_app(app)

app.config.update(
SQLALCHEMY_DATABASE_URI= "sqlite:///%s" % os.path.join(app.root_path, 'path/to/player.sqlite3'))

q = queue.Queue()

database.py:

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

models.py:

def Upload_Tracks(item):
        uri = None
        title = unidecode(item['title'])
        artist = unidecode(item['artist'])
        preview = item['preview']
        energy = item['energy']
        popularity = item['popularity']
        tempo = item['tempo']
        brightness = item['brightness']
        key = item['key']
        image = item['artist_image']
        duration = item['duration']
        loudness = item['loudness']
        valence = item['valence']
        genre = item['genre']

        track = Track(title=title,
                      artist=artist,
                      uri=uri,
                      track_id=None)

        db.session.add(track)

        track.preview = preview
        track.energy = energy
        track.popularity = popularity
        track.tempo = tempo
        track.genre = genre
        track.brightness = brightness
        track.key = key
        track.image = image
        track.duration = duration
        track.loudness = loudness
        track.valence = valence

        db.session.commit()

第一个函数:

# 1st background process
def build_cache():
    """
    Build general cache 
    from user's streaming
    """
    tracks_and_features = spotify.query_tracks()

    for item in tracks_and_features:
        q.put(item)

    return "CACHE BUILT"               

秒:

def upload_cache(track):
    # save to database
    Upload_Tracks(filtered_dataset=track) 

    return "CACHE UPLOADED"               

Flask查看:

#background cache
@app.route('/cache')
def cache():
    # We can use a with statement to ensure threads are cleaned up promptly
    with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
        #executor.submit(build_cache)

        # start a future for a thread which sends work in through the queue
        future_to_track = {
            executor.submit(build_cache): 'TRACKER DONE'}

        while future_to_track:
            # check for status of the futures which are currently working
            done, not_done = concurrent.futures.wait(
                                                future_to_track, 
                                                timeout=0.25,
                                                return_when=concurrent.futures.FIRST_COMPLETED) 

            # if there is incoming work, start a new future
            while not q.empty():

                # fetch a track from the queue
                track = q.get()

                # Start the load operation and mark the future with its TRACK
                future_to_track[executor.submit(upload_cache, track)] = track
            # process any completed futures
            for future in done:
                track = future_to_track[future]
                try:
                    data = future.result()
                except Exception as exc:
                    print('%r generated an exception: %s' % (track, exc))
                else:
                    if track == 'TRACKER DONE':
                        print(data)
                    else:
                        print('%r track is nont uploaded to database') % (track)

                # remove the now completed future
                del future_to_track[future]


    return 'Cacheing playlist in the background...'

数据库可以在没有线程的情况下正常工作。但是,当我全部运行时,会捕获以下异常:

第一个缓存构建 {'brightness': 0.4293608513678877, 'energy': 0.757, 'tempo': 116.494, 'duration': 201013, 'key': 5, 'loudness': -7.607, 'genre': [u'art rock', u 'dance rock', u'folk rock', u'mellow gold', u'new wave', u'new wave pop', u'power pop', u'pub rock', u'rock', u'roots rock'], 'valence': 0.435, 'artist': u'Elvis Costello & The Attractions', 'popularity': 14, 'artist_image': u'https://i.scdn.co/image/c14ffeb7855625383c266c9c04faa75516a25503', 'title': u'Poor Napoleon', 'preview': u'https://p.scdn.co/mp3-preview/c0d57fed887ea2dbd7f69c7209adab71671b9e6e?cid=d3b2f7a12362468daa393cf457185973'} 产生异常:未找到应用程序。在视图函数中工作或推送应用程序上下文。见http://flask-sqlalchemy.pocoo.org/contexts/

但据我所知,该过程在@app.route 内运行。这是怎么断章取义的?我该如何解决这个问题?

【问题讨论】:

  • Flask 将每个请求的上下文存储在线程局部变量中,因此当您尝试访问 Flask 全局对象时会出现这些错误(例如 current_apprequest 或 @987654338 @) 来自不同的线程。 @app.route 与上下文无关,它只是在您的应用程序中注册视图。
  • 你到底在哪里得到错误?你能发布回溯吗?
  • 在终端中。我得到了上面测试的所有 3 首曲目的回溯。所以它被executor 捕获。

标签: python flask sqlalchemy concurrent.futures


【解决方案1】:

万一遇到同样问题的人

from flask import current_app
def context_wrap(fn):
    app_context = current_app.app_context()
    def wrapper(*args, **kwargs):
        with app_context:
            return fn(*args, **kwargs)
    return wrapper

#Then In this case:
future_to_track = {
        executor.submit(context_wrap(build_cache)): 'TRACKER DONE'}
#OR In your case:
context_wrap(any_func_need_context)

【讨论】:

  • 我试图通过 add_default_done_callback 使用 flask-executor 更新我的 SQLALchemy 数据库,而这个解决方案是唯一有效的方法!
【解决方案2】:

以下工作:

def build_cache():
    with app.app_context():
        (...)

def upload_cache(track):
    with app.app_context():
        (...)

@app.route('/cache')
def cache():
    with app.app_context():
        with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
            (...)

【讨论】:

  • 对于它的工作原理,我创建了一个库来解决烧瓶中的这个确切问题,称为 Flask-Executor。您可以使用常见的 Flask 扩展配置模式对其进行初始化,或者像普通执行器一样使用它 - 不用担心应用程序上下文或请求上下文,所有这些都是透明地为您管理的,因此一切都应该“正常工作”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-09
  • 2018-08-11
  • 2021-09-05
  • 1970-01-01
  • 2018-03-14
  • 1970-01-01
相关资源
最近更新 更多