1

I was having a lot of issues getting websockets working for a chat app I'm building in Django on Heroku. Most of the stackoverflow posts use daphne instead of gunicorn in their Procfile, so I recently switched to using daphne. First question: Does anyone know if I really do need to be using daphne? Or is there a way to get websockets to work on Heroku using gunicorn?

Since switching to daphne, my app is working on my local web server, but when i push to heroku, my app crashes and I get this error:

django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.

I've tried many solutions proposed on similar stackoverflow questions, but none of them work. It seems that it likely has to do with my Settings.py, Procfile or asgi.py files.

asgi.py

# mysite/asgi.py
import os
import django
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter, get_default_application
from django.core.asgi import get_asgi_application
import chat.routing

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_project.settings")
django.setup()

# startapp = get_default_application() #todo: confirm why app doesn't crash if this is commented out... this is called from Procfile

application = ProtocolTypeRouter({ #
  "http": get_asgi_application(), # TODO: CONFIRM http vs https
  "websocket": AuthMiddlewareStack(
        URLRouter(
            chat.routing.websocket_urlpatterns
        )
    ),
})

Procfile

release: python manage.py migrate
web: daphne django_project.asgi:application --port $PORT --bind 0.0.0.0 -v2
worker: python3 manage.py runworker channel_layer -v2

settings.py

import django
from django.core.wsgi import get_wsgi_application
import os
import django_heroku

# setup(needed for daphne)
SECRET_KEY = 'seckey...' #todo: test removing this in own deployment
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_project.settings")
# application = get_wsgi_application()


# SECURITY WARNING: keep the secret key used in production secret!
#SECRET_KEY = os.environ.get('SECRET_KEY')

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = 'True'

ALLOWED_HOSTS = ['*', 'localhost', '127.0.0.1']


# Application definition
# Allows Django to look for models (for Databases)
INSTALLED_APPS = [
    'blog.apps.BlogConfig', #allows Django to correctly search your templates for the 'blog' app
    'users.apps.UsersConfig',
    'crispy_forms',
    'chat',
    'channels',
    'dal',
    'dal_select2',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    # 'django_messages',
    'django.contrib.staticfiles',
    'storages'
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'django_project.urls'

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, '')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
            # 'libraries': {
            #     'staticfiles':'chat.templatetags.__init__.py'
            # }
        },
    },
]

SETTINGS_PATH = os.path.join(os.path.dirname(__file__) ,'../templates').replace('\\','/')
TEMPLATE_DIRS = ( 
    os.path.join(SETTINGS_PATH, 'blog/templates'), # Django will look at the templates from templates/ directory under your project
)

# ~~~MESSAGES CONFIG~~~
WSGI_APPLICATION = 'django_project.wsgi.application'
ASGI_APPLICATION = 'django_project.asgi.application' # older version of django: 'django_project.routing.application'

# Channels redis config:
CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            #"hosts": [('127.0.0.1', 6379)], or 'redis' #l ocal
            "hosts": ['rediss://:p628bf20dab326cedb30d4df129e9691dbb6e7e1f4486954eadbfdf77db854369@ec2-34-235-242-69.compute-1.amazonaws.com:25180'], # REDIS_TLS_URL #todo: confirm. Changed from "127.0.0.1" to 'redis'... found promising answer, changing this
            # 'redis://:p628bf20dab326cedb30d4df129e9691dbb6e7e1f4486954eadbfdf77db854369@ec2-34-235-242-69.compute-1.amazonaws.com:25179' REDIS_URL
        },
        "ROUTING": "chat.routing.websocket_urlpatterns", #todo: add "ROUTING": "chat.routing.websocket_urlpatterns",
    },
}

CACHES = {
    "default": {
         "BACKEND": "redis_cache.RedisCache",
         "LOCATION": os.environ.get('REDIS_TLS_URL'),
         "OPTIONS": {
            "CONNECTION_POOL_KWARGS": {
                "ssl_cert_reqs": False
            }
        }
    }
}



# Use Postgrest Database in Shrouded-Inlet
DB_URL = os.environ['DATABASE_URL']
DATABASE_URL = DB_URL


# Password validation
# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/2.1/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'America/Los_Angeles'

USE_I18N = True

USE_L10N = True

USE_TZ = True


STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATIC_URL = '/static/'

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

CRISPY_TEMPLATE_PACK = 'bootstrap4'


django_heroku.settings(locals())

DATA_UPLOAD_MAX_NUMBER_FIELDS = 4000
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'staticfilescustom') #todo: may have to add own staticFileDir folder
]

Heroku Logs:

"/app/.heroku/python/lib/python3.6/site-packages/django/contrib/auth/base_user.py", line 48, in <module>
    2021-03-24T00:09:27.191111+00:00 app[web.1]: class AbstractBaseUser(models.Model):
    2021-03-24T00:09:27.191120+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/db/models/base.py", line 108, in __new__
    2021-03-24T00:09:27.191258+00:00 app[web.1]: app_config = apps.get_containing_app_config(module)
    2021-03-24T00:09:27.191262+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/apps/registry.py", line 253, in get_containing_app_config
    2021-03-24T00:09:27.191417+00:00 app[web.1]: self.check_apps_ready()
    2021-03-24T00:09:27.191418+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/apps/registry.py", line 135, in check_apps_ready
    2021-03-24T00:09:27.191540+00:00 app[web.1]: settings.INSTALLED_APPS
    2021-03-24T00:09:27.191540+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/conf/__init__.py", line 82, in __getattr__
    2021-03-24T00:09:27.191668+00:00 app[web.1]: self._setup(name)
    2021-03-24T00:09:27.191669+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/conf/__init__.py", line 69, in _setup
    2021-03-24T00:09:27.191796+00:00 app[web.1]: self._wrapped = Settings(settings_module)
    2021-03-24T00:09:27.191797+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/conf/__init__.py", line 170, in __init__
    2021-03-24T00:09:27.191927+00:00 app[web.1]: mod = importlib.import_module(self.SETTINGS_MODULE)
    2021-03-24T00:09:27.191928+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/importlib/__init__.py", line 126, in import_module
    2021-03-24T00:09:27.192056+00:00 app[web.1]: return _bootstrap._gcd_import(name[level:], package, level)
    2021-03-24T00:09:27.192057+00:00 app[web.1]: File "./django_project/settings.py", line 204, in <module>
    2021-03-24T00:09:27.192203+00:00 app[web.1]: django.setup()
    2021-03-24T00:09:27.192203+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/__init__.py", line 24, in setup
    2021-03-24T00:09:27.192317+00:00 app[web.1]: apps.populate(settings.INSTALLED_APPS)
    2021-03-24T00:09:27.192318+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/apps/registry.py", line 114, in populate
    2021-03-24T00:09:27.192433+00:00 app[web.1]: app_config.import_models()
    2021-03-24T00:09:27.192434+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/apps/config.py", line 211, in import_models
    2021-03-24T00:09:27.192592+00:00 app[web.1]: self.models_module = import_module(models_module_name)
    2021-03-24T00:09:27.192593+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/importlib/__init__.py", line 126, in import_module
    2021-03-24T00:09:27.192717+00:00 app[web.1]: return _bootstrap._gcd_import(name[level:], package, level)
    2021-03-24T00:09:27.192718+00:00 app[web.1]: File "./blog/models.py", line 3, in <module>
    2021-03-24T00:09:27.192815+00:00 app[web.1]: from django.contrib.auth.models import User
    2021-03-24T00:09:27.192819+00:00 app[web.1]: ImportError: cannot import name 'User'
    2021-03-24T00:09:27.937262+00:00 heroku[worker.1]: State changed from starting to up
    2021-03-24T00:09:32.877569+00:00 app[worker.1]: Running worker for channels ['channel_layer']
    2021-03-24T00:09:32.950161+00:00 app[worker.1]: Traceback (most recent call last):
    2021-03-24T00:09:32.951479+00:00 app[worker.1]: File "manage.py", line 15, in <module>
    2021-03-24T00:09:32.952190+00:00 app[worker.1]: execute_from_command_line(sys.argv)
    2021-03-24T00:09:32.952266+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
    2021-03-24T00:09:32.952874+00:00 app[worker.1]: utility.execute()
    2021-03-24T00:09:32.952963+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/core/management/__init__.py", line 395, in execute
    2021-03-24T00:09:32.953514+00:00 app[worker.1]: self.fetch_command(subcommand).run_from_argv(self.argv)
    2021-03-24T00:09:32.953607+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/core/management/base.py", line 330, in run_from_argv
    2021-03-24T00:09:32.954188+00:00 app[worker.1]: self.execute(*args, **cmd_options)
    2021-03-24T00:09:32.954413+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/core/management/base.py", line 371, in execute
    2021-03-24T00:09:32.954896+00:00 app[worker.1]: output = self.handle(*args, **options)
    2021-03-24T00:09:32.955486+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/site-packages/channels/management/commands/runworker.py", line 46, in handle
    2021-03-24T00:09:32.955801+00:00 app[worker.1]: worker.run()
    2021-03-24T00:09:32.956071+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/site-packages/asgiref/server.py", line 62, in run
    2021-03-24T00:09:32.956383+00:00 app[worker.1]: event_loop.run_until_complete(self.handle())
    2021-03-24T00:09:32.956455+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/asyncio/base_events.py", line 488, in run_until_complete
    2021-03-24T00:09:32.957118+00:00 app[worker.1]: return future.result()
    2021-03-24T00:09:32.957514+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/site-packages/channels/worker.py", line 30, in handle
    2021-03-24T00:09:32.957810+00:00 app[worker.1]: [listener.result() for listener in listeners]
    2021-03-24T00:09:32.958233+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/site-packages/channels/worker.py", line 30, in <listcomp>
    2021-03-24T00:09:32.959151+00:00 app[worker.1]: [listener.result() for listener in listeners]
    2021-03-24T00:09:32.959234+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/site-packages/channels/worker.py", line 37, in listener
    2021-03-24T00:09:32.959589+00:00 app[worker.1]: message = await self.channel_layer.receive(channel)
    2021-03-24T00:09:32.959659+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/site-packages/channels_redis/core.py", line 501, in receive
    2021-03-24T00:09:32.960387+00:00 app[worker.1]: return (await self.receive_single(channel))[1]
    2021-03-24T00:09:32.960664+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/site-packages/channels_redis/core.py", line 524, in receive_single
    2021-03-24T00:09:32.961154+00:00 app[worker.1]: index, channel_key, timeout=self.brpop_timeout
    2021-03-24T00:09:32.961341+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/site-packages/channels_redis/core.py", line 356, in _brpop_with_clean
    2021-03-24T00:09:32.961891+00:00 app[worker.1]: async with self.connection(index) as connection:
    2021-03-24T00:09:32.962103+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/site-packages/channels_redis/core.py", line 884, in __aenter__
    2021-03-24T00:09:32.963869+00:00 app[worker.1]: self.conn = await self.pool.pop()
    2021-03-24T00:09:32.963943+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/site-packages/channels_redis/core.py", line 80, in pop
    2021-03-24T00:09:32.964279+00:00 app[worker.1]: conn = await aioredis.create_redis(**self.host, loop=loop)
    2021-03-24T00:09:32.964342+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/site-packages/aioredis/commands/__init__.py", line 175, in create_redis
    2021-03-24T00:09:32.964727+00:00 app[worker.1]: loop=loop)
    2021-03-24T00:09:32.964806+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/site-packages/aioredis/connection.py", line 113, in create_connection
    2021-03-24T00:09:32.965216+00:00 app[worker.1]: timeout)
    2021-03-24T00:09:32.965286+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/asyncio/tasks.py", line 339, in wait_for
    2021-03-24T00:09:32.965790+00:00 app[worker.1]: return (yield from fut)
    2021-03-24T00:09:32.965850+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/site-packages/aioredis/stream.py", line 24, in open_connection
    2021-03-24T00:09:32.966201+00:00 app[worker.1]: lambda: protocol, host, port, **kwds)
    2021-03-24T00:09:32.966271+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/asyncio/base_events.py", line 824, in create_connection
    2021-03-24T00:09:32.967044+00:00 app[worker.1]: sock, protocol_factory, ssl, server_hostname)
    2021-03-24T00:09:32.967368+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/asyncio/base_events.py", line 850, in _create_connection_transport
    2021-03-24T00:09:32.968101+00:00 app[worker.1]: yield from waiter
    2021-03-24T00:09:32.968459+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/asyncio/sslproto.py", line 505, in data_received
    2021-03-24T00:09:32.969699+00:00 app[worker.1]: ssldata, appdata = self._sslpipe.feed_ssldata(data)
    2021-03-24T00:09:32.969776+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/asyncio/sslproto.py", line 201, in feed_ssldata
    2021-03-24T00:09:32.970144+00:00 app[worker.1]: self._sslobj.do_handshake()
    2021-03-24T00:09:32.970216+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.6/ssl.py", line 689, in do_handshake
    2021-03-24T00:09:32.971002+00:00 app[worker.1]: self._sslobj.do_handshake()
    2021-03-24T00:09:32.971118+00:00 app[worker.1]: ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:852)
    2021-03-24T00:09:33.408193+00:00 heroku[worker.1]: Process exited with status 1
Tom
  • 364
  • 2
  • 19

0 Answers0