芹菜 - 在tasks.py

时间:2018-05-07 10:30:55

标签: python django celery

我在tasks.py

中访问模型时遇到问题

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

@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. 中其他类似问题的建议
  3. 在' send_email'中导入模型方法,允许工作人员启动但导致以下错误

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

    这是对SO

  4. 中类似问题的另一个建议
  5. 在调用' send_email'时删除.delay有效的函数(在tasks.py文件或send_email方法的顶部导入),但由于任务不再是异步,它没有任何好处,但可能会缩小问题范围?

  6. 注意事项?

    1. 我使用扩展AbstractBaseUser的自定义用户模型,我在芹菜中看到了许多与此相关的github问题,但这些问题是在celery v3.1中修复的,所以我相信
    2. 我在celery v4.1,django 1.11.10,python 2.7上使用RabbitMQ作为Broker并在虚拟环境中运行worker / server。我正在使用

      启动我的工作人员
      celery -A api worker -l info
      
      在终端窗口上

      然后使用pycharm的终端使用

      启动服务器
      python manage.py runserver
      

      如此有效地有2个环境?这可能是问题吗?

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

      django.core.exceptions.ImproperlyConfigured: AUTH_USER_MODEL refers to model 'myApi.User' that has not been installed
      
    4. 我尝试将auth模型设置为' myApi.user.User' (用户是声明模型但得到

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

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

    5. 项目结构

      ├── 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()
      

      任何建议都将不胜感激。对于长篇文章也很抱歉,这是我的第一个,所以不确定需要多少细节。

1 个答案:

答案 0 :(得分:2)

我发现了我的问题。如果它帮助其他人坚持这个我需要添加行

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

for Celery

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