title: Celery - importing models in tasks.py
body: <p>I'm having an issue getting access to models in my tasks.py </p>
  
  <p>My goal is to send an email at various parts of the application (user registration, reset password etc..). To do this I pass the user id(s) to a celery task called 'send_email'. </p>
  
  <pre><code>@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")
  </code></pre>
  
  <p>The task then needs to use the id to retrieve the user and send them an email. This breaks down when trying to import the User model into the tasks.py file.</p>
  
  <p>I receive an error</p>
  
  <pre><code>raise AppRegistryNotReady("Apps aren't loaded yet.") django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.
  </code></pre>
  
  <p>Things I've tried</p>
  
  <ol>
  <li><p>calling django.setup() at the top of tasks.py file - causes </p>
  
  <pre><code>raise RuntimeError("populate() isn't reentrant") 
  </code></pre>
  
  <p>causes the same error when put in send_email method as well. These were suggestions on other similar questions in SO</p></li>
  <li><p>Importing model in 'send_email' method, allows the worker to start but  causes the following error</p>
  
  <pre><code>raise AppRegistryNotReady("Apps aren't loaded yet.") 
  </code></pre>
  
  <p>This was another suggestion on a similar question in SO</p></li>
  <li><p>Removing .delay when calling the 'send_email' function which works (with imports at top of tasks.py file or in send_email method) but as the task is no longer async it is not of any benefit but perhaps narrows down the issue?</p></li>
  </ol>
  
  <p>Things of Note?</p>
  
  <ol>
  <li>I use a custom user model that extends AbstractBaseUser, I have seen a number of github issues in celery relating to this but these were meant to be fixed in celery v3.1 or so I believe</li>
  <li><p>I'm on celery v4.1, django 1.11.10, python 2.7 and am using RabbitMQ as Broker and running worker / server on a virtual env. I'm starting my worker using </p>
  
  <pre><code>celery -A api worker -l info
  </code></pre>
  
  <p>on a terminal window and then using pycharm's terminal to start server using </p>
  
  <pre><code>python manage.py runserver
  </code></pre>
  
  <p>so effectively there are 2 envs? Could this be the issue?</p></li>
  <li><p>This may or not be related but in order to get my custom user model to work in my app/models.py I just have a single line that imports the User model otherwise I get a </p>
  
  <pre><code>django.core.exceptions.ImproperlyConfigured: AUTH_USER_MODEL refers to model 'myApi.User' that has not been installed
  </code></pre></li>
  <li><p>I tried to set the auth model to 'myApi.user.User' (user being the folder where model is declared but get a </p>
  
  <pre><code>Invalid model reference 'myApi.user.User'. String model references must be of the form 'app_label.ModelName'.
  </code></pre>
  
  <p>so I'm guessing this is why the import is needed in myApi/models.py so that it can be picked up here?</p></li>
  </ol>
  
  <p><strong>Project Structure</strong></p>
  
  <pre><code>├── 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
  </code></pre>
  
  <p><strong>tasks.py</strong></p>
  
  <pre><code>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")
  </code></pre>
  
  <p><strong>settings.py</strong></p>
  
  <pre><code>....
  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')
  ....
  </code></pre>
  
  <p><strong>celerySettings.py</strong></p>
  
  <pre><code>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))
  </code></pre>
  
  <p><strong>myApi/models.py</strong></p>
  
  <pre><code>from user.models import User
  </code></pre>
  
  <p><strong>myApi/admin.py</strong></p>
  
  <pre><code># -*- coding: utf-8 -*-
  from __future__ import unicode_literals
  
  from django.contrib import admin
  
  from user.models import User
  
  admin.site.register(User)
  </code></pre>
  
  <p><strong>api/wsgi.py</strong></p>
  
  <pre><code>import os
  
  from django.core.wsgi import get_wsgi_application
  
  os.environ.setdefault("DJANGO_SETTINGS_MODULE", "api.settings")
  
  application = get_wsgi_application()
  </code></pre>
  
  <p>Any suggestions would be greatly appreciated. Also sorry for the long post, it is my first one so wasn't sure how much detail was needed.</p>