Flask、SQLAlchemy、Celeryの適切な設定を取得するのに本当に苦労しています。私は広範囲を検索し、さまざまなアプローチを試しましたが、実際には何も機能しないようです。アプリケーションコンテキストを逃したか、ワーカーを実行できないか、その他の問題があります。より大きなアプリケーションを構築できるように、構造は非常に一般的です。
私は使用しています:Flask 0.10.1、SQLAlchemy 1.0、Celery 3.1.13、私の現在の設定は次のとおりです:
app/__ init__.py
#Empty
app/config.py
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config:
@staticmethod
def init_app(app):
pass
class LocalConfig(Config):
DEBUG = True
SQLALCHEMY_DATABASE_URI = r"sqlite:///" + os.path.join(basedir,
"data-dev.sqlite")
CELERY_BROKER_URL = 'amqp://guest:guest@localhost:5672//'
config = {
"local": LocalConfig}
app/exstensions.py
from flask.ext.sqlalchemy import SQLAlchemy
from celery import Celery
db = SQLAlchemy()
celery = Celery()
app/factory.py
from extensions import db, celery
from flask import Flask
from flask import g
from config import config
def create_before_request(app):
def before_request():
g.db = db
return before_request
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
db.init_app(app)
celery.config_from_object(config)
# Register the blueprints
# Add the before request handler
app.before_request(create_before_request(app))
return app
app/manage.py
from factory import create_app
app = create_app("local")
from flask import render_template
from flask import request
@app.route('/test', methods=['POST'])
def task_simple():
import tasks
tasks.do_some_stuff.delay()
return ""
if __name__ == "__main__":
app.run()
app/models.py
from extensions import db
class User(db.Model):
__tablename__ = "user"
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(128), unique=True, nullable=False)
app/tasks.py
from extensions import celery
from celery.signals import task_prerun
from flask import g, current_app
@task_prerun.connect
def close_session(*args, **kwargs):
with current_app.app_context():
# use g.db
print g
@celery.task()
def do_some_stuff():
with current_app.app_context():
# use g.db
print g
フォルダーアプリ:
python.exe manage.py
で開始しますcelery.exe worker -A tasks
意味のないインポートエラーが発生します。アプリケーションの構造を変える必要がありますか?最後に、私は非常に基本的なセットアップが必要だと思います。 Flaskをファクトリパターンで使用して、Flask-SQLAlchmey拡張機能を使用し、データベースにアクセスする必要があるワーカーを用意することができます。
どんな助けでも大歓迎です。
トレースバックは、セロリワーカーの起動時に実行されます。
Traceback (most recent call last):
File "[PATH]\scripts\celery-script.py", line 9, in <module>
load_entry_point('celery==3.1.13', 'console_scripts', 'celery')()
File "[PATH]\lib\site-packages\celery\__main__.py", line 30, in main
main()
File "[PATH]\lib\site-packages\celery\bin\celery.py", line 81, in main
cmd.execute_from_commandline(argv)
File "[PATH]\lib\site-packages\celery\bin\celery.py", line 769, in execute_from_commandline
super(CeleryCommand, self).execute_from_commandline(argv)))
File "[PATH]\lib\site-packages\celery\bin\base.py", line 305, in execute_from_commandline
argv = self.setup_app_from_commandline(argv)
File "[PATH]\lib\site-packages\celery\bin\base.py", line 473, in setup_app_from_commandline
user_preload = Tuple(self.app.user_options['preload'] or ())
AttributeError: 'Flask' object has no attribute 'user_options'
[〜#〜] update [〜#〜]コメントの提案に従ってコードを変更します。ワーカーが起動しますが、http://127.0.0.1:5000/test
へのgetリクエストでテストすると、次のトレースバックが表示されます。
Traceback (most recent call last):
File "[PATH]\lib\site-packages\celery\app\trace.py", line 230, in trace_task
args=args, kwargs=kwargs)
File "[PATH]\lib\site-packages\celery\utils\dispatch\signal.py", line 166, in send
response = receiver(signal=self, sender=sender, \**named)
File "[PATH]\app\stackoverflow\tasks.py", line 7, in close_session
with current_app.app_context():
File "[PATH]\lib\site-packages\werkzeug\local.py", line 338, in __getattr__
return getattr(self._get_current_object(), name)
File "[PATH]\lib\site-packages\werkzeug\local.py", line 297, in _get_current_object
return self.__local()
File "[PATH]\lib\site-packages\flask\globals.py", line 34, in _find_app
raise RuntimeError('working outside of application context')
RuntimeError: working outside of application context exc, exc_info.traceback)))
[〜#〜] update [〜#〜]Marteenからのコメントに基づいて、コードを変更しました。現在の作業バージョンは以下です https://Gist.github.com/anonymous/fa47834db2f4f3b8b257 。さらなる改善や提案は大歓迎です。
私はcurrent_appのアドバイスに不満を感じていました。
セロリオブジェクトは、アプリケーションコンテキストにアクセスする必要があります。ファクトリー関数を使用したCeleryオブジェクトの作成に関する情報をオンラインで見つけました。以下の例は、メッセージブローカーなしでテストされています。
#factory.py
from celery import Celery
from config import config
def create_celery_app(app=None):
app = app or create_app(config)
celery = Celery(__name__, broker=app.config['CELERY_BROKER_URL'])
celery.conf.update(app.config)
TaskBase = celery.Task
class ContextTask(TaskBase):
abstract = True
def __call__(self, *args, **kwargs):
with app.app_context():
return TaskBase.__call__(self, *args, **kwargs)
celery.Task = ContextTask
return celery
そしてtasks.pyで:
#tasks.py
from factory import create_celery_app
from celery.signals import task_prerun
from flask import g
celery = create_celery_app()
@task_prerun.connect
def celery_prerun(*args, **kwargs):
#print g
with celery.app.app_context():
# # use g.db
print g
@celery.task()
def do_some_stuff():
with celery.app.app_context():
# use g.db
g.user = "test"
print g.user
いくつかのリンク:
ファクトリ関数でCeleryインスタンスを作成するためのフラスコパターン
flaskアプリケーションファクトリパターンで動作し、タスクでapp.app_context()
を明示的に使用する必要なく、コンテキストでセロリタスクを作成するソリューションを次に示します。私のアプリでは、循環インポートを回避しながらそのアプリオブジェクトを取得するのは非常に難しいですが、これはそれを解決します。これは、執筆時の最新のセロリバージョン4.2にも適しています。
構造:
_repo_name/
manage.py
base/
base/__init__.py
base/app.py
base/runcelery.py
base/celeryconfig.py
base/utility/celery_util.py
base/tasks/workers.py
_
したがって、この例ではbase
がメインアプリケーションパッケージです。 _base/__init__.py
_では、次のようにセロリインスタンスを作成します。
_from celery import Celery
celery = Celery('base', config_source='base.celeryconfig')
_
_base/app.py
_ファイルには、flask app factory _create_app
_が含まれ、init_celery(app, celery)
が含まれていることに注意してください。
_from base import celery
from base.utility.celery_util import init_celery
def create_app(config_obj):
"""An application factory, as explained here:
http://flask.pocoo.org/docs/patterns/appfactories/.
:param config_object: The configuration object to use.
"""
app = Flask('base')
app.config.from_object(config_obj)
init_celery(app, celery=celery)
register_extensions(app)
register_blueprints(app)
register_errorhandlers(app)
register_app_context_processors(app)
return app
_
_base/runcelery.py
_のコンテンツに移ります:
_from flask.helpers import get_debug_flag
from base.settings import DevConfig, ProdConfig
from base import celery
from base.app import create_app
from base.utility.celery_util import init_celery
CONFIG = DevConfig if get_debug_flag() else ProdConfig
app = create_app(CONFIG)
init_celery(app, celery)
_
次に、_base/celeryconfig.py
_ファイル(例):
_# -*- coding: utf-8 -*-
"""
Configure Celery. See the configuration guide at ->
http://docs.celeryproject.org/en/master/userguide/configuration.html#configuration
"""
## Broker settings.
broker_url = 'pyamqp://guest:guest@localhost:5672//'
broker_heartbeat=0
# List of modules to import when the Celery worker starts.
imports = ('base.tasks.workers',)
## Using the database to store task state and results.
result_backend = 'rpc'
#result_persistent = False
accept_content = ['json', 'application/text']
result_serializer = 'json'
timezone = "UTC"
# define periodic tasks / cron here
# beat_schedule = {
# 'add-every-10-seconds': {
# 'task': 'workers.add_together',
# 'schedule': 10.0,
# 'args': (16, 16)
# },
# }
_
次に、_base/utility/celery_util.py
_ファイルでinit_celeryを定義します。
_# -*- coding: utf-8 -*-
def init_celery(app, celery):
"""Add flask app context to celery.Task"""
TaskBase = celery.Task
class ContextTask(TaskBase):
abstract = True
def __call__(self, *args, **kwargs):
with app.app_context():
return TaskBase.__call__(self, *args, **kwargs)
celery.Task = ContextTask
_
_base/tasks/workers.py
_の労働者向け:
_from base import celery as celery_app
from flask_security.utils import config_value, send_mail
from base.bp.users.models.user_models import User
@celery_app.task
def send_welcome_email(email, user_id, confirmation_link):
"""Background task to send a welcome email with flask-security's mail.
You don't need to use with app.app_context() as Task has app context.
"""
user = User.query.filter_by(id=user_id).first()
print(f'sending user {user} a welcome email')
send_mail(config_value('EMAIL_SUBJECT_REGISTER'),
email,
'welcome', user=user,
confirmation_link=confirmation_link)
@celery_app.task
def do_some_stuff():
print(g)
_
次に、セロリビートとセロリワーカーを_repo_name
_フォルダ内から2つの異なるコマンドプロンプトで開始する必要があります。
1つのコマンドプロンプトで_celery -A base.runcelery:celery beat
_を実行し、もう1つの_celery -A base.runcelery:celery worker
_を実行します。
次に、flaskコンテキストが必要なタスクを実行します。動作するはずです。