【问题标题】:Celery - importing models in tasks.pyCelery - 在 tasks.py 中导入模型
【发布时间】:2018-05-07 10:30:55
【问题描述】:

我在访问我的 tasks.py 中的模型时遇到问题

我的目标是在应用程序的各个部分(用户注册、重置密码等)发送电子邮件。为此,我将用户 ID 传递给名为“send_email”的 celery 任务。

@shared_task()
def send_email(sender_id=None, receiver_id=None, type=None, message=None):

    sender = User.objects.get(id=sender_id)
    receiver = User.objects.get(id=receiver_id)

    logger.info("Starting send email")
    Email.send_email(sender, receiver, type, message)
    logger.info("Finished send email")

然后任务需要使用 id 来检索用户并向他们发送电子邮件。尝试将 User 模型导入 tasks.py 文件时会出现这种情况。

我收到一个错误

raise AppRegistryNotReady("Apps aren't loaded yet.") django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.

我尝试过的事情

  1. 在 tasks.py 文件顶部调用 django.setup() - 原因

    raise RuntimeError("populate() isn't reentrant") 
    

    放入 send_email 方法时也会导致相同的错误。这些是对 SO 中其他类似问题的建议

  2. 在'send_email'方法中导入模型,允许worker启动但导致以下错误

    raise AppRegistryNotReady("Apps aren't loaded yet.") 
    

    这是关于 SO 中类似问题的另一个建议

  3. 在调用 'send_email' 函数时删除 .delay 有效(在 tasks.py 文件顶部或在 send_email 方法中导入)但由于任务不再是异步的,它没有任何好处,但可能会缩小解决问题?

注意事项?

  1. 我使用扩展 AbstractBaseUser 的自定义用户模型,我在 celery 中看到了许多与此相关的 github 问题,但我相信这些问题应该在 celery v3.1 中得到修复
  2. 我正在使用 celery v4.1、django 1.11.10、python 2.7,并且正在使用 RabbitMQ 作为代理并在虚拟环境上运行工作程序/服务器。我正在使用

    celery -A api worker -l info
    

    在终端窗口上,然后使用 pycharm 的终端使用

    启动服务器
    python manage.py runserver
    

    那么有效地有 2 个环境?会不会是这个问题?

  3. 这可能相关或不相关,但为了让我的自定义用户模型在我的 app/models.py 中工作,我只有一行导入用户模型,否则我会得到一个

    django.core.exceptions.ImproperlyConfigured: AUTH_USER_MODEL refers to model 'myApi.User' that has not been installed
    
  4. 我尝试将身份验证模型设置为“myApi.user.User”(用户是声明模型的文件夹,但得到一个

    Invalid model reference 'myApi.user.User'. String model references must be of the form 'app_label.ModelName'.
    

    所以我猜这就是为什么需要在 myApi/models.py 中导入以便可以在此处获取它?

项目结构

├── api
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── celerySettings.py # my celery.py
├── db.sqlite3
├── myApi
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── tasks.py
│   ├── urls.py
│   ├── user
│   │   ├── __init__.py
│   │   ├── managers.py
│   │   ├── models.py
│   │   ├── serializers.py
│   │   ├── urls.py
│   │   └── views.py
│   ├── utils
│   │   └── Email.py
│   ├── views.py
├── manage.py
└── static

tasks.py

from __future__ import absolute_import, unicode_literals
from celery.schedules import crontab
from celery.task import periodic_task
from celery.utils.log import get_task_logger
from celery import shared_task

from celery import current_app

from .user.models import User
from .utils import Email

logger = get_task_logger(__name__)

@shared_task()
def send_email(sender_id=None, receiver_id=None, type=None, message=None):

    sender = User.objects.get(id=sender_id)
    receiver = User.objects.get(id=receiver_id)

    logger.info("Starting send email")
    Email.send_email(sender, receiver, type, message)
    logger.info("Finished send email")

settings.py

....
INSTALLED_APPS = [
    'rest_framework',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'corsheaders',
    'myApi',
    'celery',
    'rest_framework.authtoken',
    'rest_framework.renderers',
]

AUTH_USER_MODEL = 'myApi.User'
CELERY_IMPORTS = ('api.myApi.tasks')
....

celerySettings.py

from __future__ import absolute_import, unicode_literals
from django.conf import settings
import os
from celery import Celery


# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api.api.settings')

app = Celery('api', broker='amqp://')

# Using a string here means the worker doesn't have to serialize
# the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys
#   should have a `CELERY_` prefix.
app.config_from_object(settings, namespace='CELERY')

# Load task modules from all registered Django app configs.
app.autodiscover_tasks()

@app.task(bind=True)
def debug_task(self):
    print('Request: {0!r}'.format(self.request))

myApi/models.py

from user.models import User

myApi/admin.py

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.contrib import admin

from user.models import User

admin.site.register(User)

api/wsgi.py

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "api.settings")

application = get_wsgi_application()

任何建议将不胜感激。也很抱歉这么长的帖子,这是我的第一篇,所以不确定需要多少细节。

【问题讨论】:

  • 你试过在 celerySettings.py 顶部附近调用django.setup() 吗?
  • @Leighton 是的,抱歉我忘了提这个。当我将它添加到 tasks.py 时,我得到了相同的可重入错误
  • 你改变了你的 wsgi.py 吗?可重入错误通常意味着您的 settings.py 不正确,或者与加载 apache/WSGI 相关的配置错误。我假设该站点可以正常运行(并且它只是无法正常工作的芹菜)?
  • @Leighton 我不相信我这样做了,我已经编辑了问题以包含它。该 api 运行良好,它只为有问题的 celery 任务导入模型。也感谢您的帮助

标签: python django celery


【解决方案1】:

我发现了我的问题。如果它可以帮助其他人坚持这一点,我需要添加该行

sys.path.append(os.path.abspath('api'))

在我的 celerySettings.py 中获取模型。

现在看起来像这样

from __future__ import absolute_import, unicode_literals
from django.conf import settings
import os, sys
from celery import Celery

sys.path.append(os.path.abspath('api'))

# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api.api.settings')

app = Celery('api', broker='amqp://')

# Using a string here means the worker doesn't have to serialize
# the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys
#   should have a `CELERY_` prefix.
app.config_from_object(settings, namespace='CELERY')

# Load task modules from all registered Django app configs.
app.autodiscover_tasks()

@app.task(bind=True)
def debug_task(self):
    print('Request: {0!r}'.format(self.request))

然后,当我实际尝试在本地查询我的模型的数据库时遇到了另一个问题。 Celery 说我的数据库表不存在,这是因为它在实际本地数据库文件所在的文件夹上方创建了一个新数据库,要修复它我只需要更改数据库名称

"db.sqlite3"

os.path.join(os.path.dirname(__file__), "db.sqlite3")

在 settings.py 中

有效地将其更改为

api/db.sqlite3

芹菜

希望这对其他人有所帮助,因为我花了太多时间来解决这个问题。

【讨论】:

    猜你喜欢
    • 2018-12-27
    • 1970-01-01
    • 2017-05-11
    • 2019-06-28
    • 2012-08-26
    • 2018-02-04
    • 2016-08-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多