Использование работника Celery для взаимодействия с базой данных SQLAlchemy, включая знание пользователя по запросу - PullRequest
0 голосов
/ 23 февраля 2019

Я провел много исследований по этому вопросу, в том числе пытаясь найти ответы типа this .Похоже, что Celery не имеет доступа к контексту моего приложения Flask.

Я хорошо знаю, что мой объект сельдерея, который будет украшать мои задачи, должен иметь доступ к контексту моего приложения Flask.И я верю, что это следует, поскольку я следовал этому руководству по созданию моего объекта из сельдерея.Я не уверен, что путаница кроется где-то в том, что я использую Flask-HTTPAuth.

Вот кое-что из того, что у меня есть.

def make_celery(app):
    celery = Celery(app.import_name, backend=app.config["CELERY_RESULT_BACKEND"], 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

app = Flask(__name__)
auth = HTTPBasicAuth()
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///flask_app.db"
app.config["CELERY_BROKER_URL"] = "redis://localhost:6379"
app.config["CELERY_RESULT_BACKEND"] = "redis://localhost:6379"
celery = make_celery(app)
db = SQLAlchemy(app)

@celery.task(bind=True, name="flask_app.item_loop")
def loop(self):
    items = g.user.items
    for item in items:
        print(item)

Запуск этой задачи с использованием Flask - нет-то, хотя.Я пытаюсь запустить эту функцию, нажав сервер (пока авторизован!).

@app.route("/item_loop")
@auth.login_required
def item_loop():
    result = loop.delay()
    return "It's running."

Но работник Celery говорит мне задачу raised unexpected: AttributeError("'_AppCtxGlobals' object has no attribute 'user'",), что, как я полагаю, подразумевает, как уже упоминалось, мой объект сельдерея.не имеет контекста приложения, хотя я использовал рекомендованный заводской шаблон.

Ответы [ 3 ]

0 голосов
/ 23 февраля 2019

Как отметил @Dave W. Smith, вместо того, чтобы полагаться на g для извлечения пользователя, передача пользовательской информации в качестве аргумента в задачу Celery могла бы быть лучшим подходом.Согласно документации Flask о контексте приложения , время жизни g является запросом.Поскольку задача Celery выполняется асинхронно, она будет выполняться в другом контексте приложения, чем в запросе, в котором вы определили пользователя.

0 голосов
/ 23 февраля 2019

Несмотря на то, что рекомендации в ответах Дейва и Грега действительны, они упускают из виду то, что у вас есть неправильное понимание использования контекста приложения в задаче Celery.

У вас есть приложение Flask, вкоторый вы используете Flask-HTTPAuth.У вас, вероятно, есть обработчик verify_password, который устанавливает g.user для аутентифицированного пользователя.Это означает, что во время обработки запроса вы можете получить доступ к пользователю как g.user.Это все хорошо.

У вас также есть один или несколько работников Celery, которые представляют собой отдельные процессы, не имеющие прямого подключения к серверу Flask.Единственный обмен данными между сервером Flask и рабочими процессами Celery происходит через используемый вами брокер сообщений (обычно Redis или RabbitMQ).

В зависимости от ваших потребностей работникам Celery может потребоваться доступ к Flask.приложение.Это очень часто встречается при использовании расширений Flask, которые сохраняют свою конфигурацию в словаре app.config.Два распространенных расширения, которые требуют этого, - Flask-SQLAlchemy и Flask-Mail.Без доступа к app.config задача Celery не сможет открыть соединение с базой данных или отправить электронное письмо, поскольку она не будет знать детали базы данных и / или почтового сервера.

КомуПредоставив работникам Celery доступ к конфигурации, принято создавать дубликаты приложений Flask для каждого работника.Это вторичные приложения, которые никак не связаны с реальным объектом приложения, используемым основным сервером Flask.Их единственная цель - хранить копию исходного словаря app.config, к которому может получить доступ ваша задача или любые расширения Flask, используемые вашей задачей.

Таким образом, недопустимо ожидать, что набор g.userна сервере Flask также будет доступен как g.user в задаче Celery, просто потому, что это разные g объекты из разных экземпляров приложения.

Если вам нужно использовать аутентифицированного пользователя в задаче Celeryчто вам нужно сделать, это передать user_id (обычно g.user.id) в качестве аргумента вашей задачи.Затем в своей задаче вы можете загрузить пользователя из базы данных, используя этот id.Надеюсь, это поможет!

0 голосов
/ 23 февраля 2019

Чтобы извлечь пользователя из выполнения задачи, вы можете попробовать передать объект User (если сельдерей может его засолить) или передать достаточно информации, чтобы задача могла получить объект User (например, идентификатор пользователя).В этом последнем случае ваша задача будет выглядеть примерно так:

@celery.task(bind=True, name="flask_app.item_loop")
def loop(self, user_id):
    user = User.query.get(user_id)
    items = user.items
    for item in items:
        print(item)

, и вы начнете ее (если вы используете flask_login) через

result = loop.delay(current_user.id)
...