AUTH_USER_MODEL error in production only: AUTH_USER_MODEL refers to model 'auth.User' that has not been installed
I have my app working on localhost but when i deploy to production (Heroku), I get this error:
File "./blog/models.py", line 6, in <module>
2021-03-24T23:31:05.696194+00:00 app[web.1]: User = get_user_model()
2021-03-24T23:31:05.696219+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/contrib/auth/__init__.py", line 162, in get_user_model
2021-03-24T23:31:05.696372+00:00 app[web.1]: "AUTH_USER_MODEL refers to model '%s' that has not been installed" % settings.AUTH_USER_MODEL
2021-03-24T23:31:05.696397+00:00 app[web.1]: django.core.exceptions.ImproperlyConfigured: AUTH_USER_MODEL refers to model 'auth.User' that has not been installed
I have tried countless configurations, such as setting AUTH_USER_MODEL = 'auth.User'
in settings after doing from django.contrib.auth.models import User
and:
from django.contrib.auth import get_user_model
User = get_user_model()
but nothing works.
settings.py
"""~~~NOTES~~~
- procfile could use channel_layer instead of channels
- AUTH_USER_MODEL could be 'django_project.User'. Worked locally but not on heroku
- django.setup() seems ok at bottom of settings.py
- If have to
"""
import django
from django.core.wsgi import get_wsgi_application
from django.core.asgi import get_asgi_application
# from django.contrib.auth.models import User #todo: this causes ImproperlyConfigured: SECRET_KEY MUST NOT BE EMPTY
import os
import django_heroku
DJANGO_SETTINGS_MODULE = 'django_project.settings'
SECRET_KEY = 'asdaf123$9pv98=e6p^gl(-eoj' #todo: test removing this in own deployment
# AUTH_USER_MODEL=User # todo: this causes RuntimeError: populate() isnt reentrant
#SECRET_KEY = os.environ.get('SECRET_KEY')
DEBUG = 'True'
ALLOWED_HOSTS = ['*', 'localhost', '127.0.0.1']
INSTALLED_APPS = [
'django.contrib.auth',
'blog.apps.BlogConfig', #allows Django to correctly search your templates for the 'blog' app
'users.apps.UsersConfig',
# 'chat.apps.ChatConfig',
'chat',
'crispy_forms',
'channels',
'dal',
'dal_select2',
'django.contrib.admin',
'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 = ( # deprecated
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
}
}
}
}
DATABASES = { # Use this to use local test DB
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
DB_URL = os.environ['DATABASE_URL']
DATABASE_URL = DB_URL
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',
},
]
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'
LOGIN_REDIRECT_URL = 'blog-home'
LOGIN_URL = 'login'
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
]
django.setup()
blog/models (which is where the error is complaining about):
from django.db import models
from django.utils import timezone
from django.contrib.auth import get_user_model
User = get_user_model()
from django.conf import settings
from django.urls import reverse
from datetime import datetime, timedelta
# we're inheriting from the models.Model
class Post(models.Model):
title = models.CharField(max_length=100) # character field
content = models.TextField() # Unrestricted text
date_posted = models.DateTimeField(default=timezone.now)
last_modified = models.DateTimeField(auto_now=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
class Game(models.Model):
name = models.TextField() # Unrestricted text
platform = models.CharField(max_length=100) # character field
created_date = models.DateTimeField(default=timezone.now)
name_and_platform = models.TextField(default='N/A') #todo: find a good max char limit
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.name # return game name when game.objects.all() is called
def get_name_and_platform(self):
return ''.join([self.name, '(', self.platform, ')'])
def save(self, *args, **kwargs):
self.name_and_platform = self.get_name_and_platform()
super(Game, self).save(*args, **kwargs)
class Trade(models.Model):
name = models.TextField() # Unrestricted text
created_date = models.DateTimeField(default=timezone.now)
is_trade_proposed = models.BooleanField(default=False)
user_who_posted = models.ForeignKey(User, on_delete=models.CASCADE)
owned_game = models.ForeignKey(Game, on_delete=models.CASCADE, related_name='owned_game', db_column='owned_game')
desired_game = models.ForeignKey(Game, on_delete=models.CASCADE, related_name='desired_game', db_column='desired_game')
def get_trade_name(self):
return ''.join([self.user_who_posted.username, '(', timezone.now().strftime("%b %d, %Y %H:%M:%S UTC"), ')'])
def save(self, *args, **kwargs):
self.name = self.get_trade_name()
super(Trade, self).save(*args, **kwargs)
def __str__(self):
return self.name # return game name when game.objects.all() is called
class Transaction(models.Model):
name = models.TextField() # Unrestricted text
created_date = models.DateTimeField(default=timezone.now)
trade_one = models.ForeignKey(Trade, on_delete=models.CASCADE, related_name='trade_one', db_column='trade_one')
trade_two = models.ForeignKey(Trade, on_delete=models.CASCADE, related_name='trade_two', db_column='trade_two')
status = models.TextField() # Unrestricted text. Validated in form.
expiry_date = models.DateTimeField(default=datetime.today() + timedelta(days=3))
open_expiry_date = models.DateTimeField(default=datetime.today() + timedelta(days=9))
user_cancelled_date = models.DateTimeField(null=True, blank=True)
def get_transaction_name(self):
return ''.join([str(self.trade_one_id), ' and ', str(self.trade_two_id), ' on ', timezone.now().strftime("%b %d, %Y %H:%M:%S UTC"), ''])
def get_status_on_insert(self):
return 'Waiting for 2nd confirmation from ' + str(self.trade_two.user_who_posted)
def save(self, *args, **kwargs):
if self.name == '':
self.name = self.get_transaction_name()
if self.status == '':
self.status = self.get_status_on_insert()
super(Transaction, self).save(*args, **kwargs)
def __str__(self):
return self.name # return name when game.objects.all() is called
def get_absolute_url(self): #todo: remove?
return reverse('confirmed-trade', kwargs={'pk': self.pk})
chat/models.py
from django.db import models
from django.contrib.auth import get_user_model
User = get_user_model()
# from django.conf import settings
from django.utils import timezone
class Message(models.Model):
author = models.ForeignKey(User, related_name='author_messages', on_delete=models.CASCADE)
content = models.TextField()
timestamp = models.DateTimeField(default=timezone.now) #todo: timezone fix?
def __str__(self):
return self.author.username
def last_10_messages(self):
return Message.objects.order_by('-timestamp').all()[:10] # only load last x msgs from DB
users/models.py
from django.db import models
from PIL import Image
from django.contrib.auth import get_user_model
User = get_user_model()
from django.conf import settings
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
def __str__(self):
return f'{self.user.username} Profile'
chat/consumers.py
# chat/consumers.py
import json
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
from .models import Message
from django.contrib.auth import get_user_model
User = get_user_model()
from django.conf import settings
class ChatConsumer(WebsocketConsumer):
def fetch_messages(self, data):
print('~~~fetch_messages~~~')
messages = Message.last_10_messages(self)
content = {
'command': 'messages',
'messages': self.messages_to_json(messages)
}
print('content: ' + str(content))
self.send_message(content)
def new_message(self, data):
print('~~~in new_message: 2~~~')
author = data['from']
print('author: ' + author)
author_user = User.objects.filter(username=author)[0]
message = Message.objects.create(author=author_user, content=data['message'])
content = {
'command': 'new_message',
'message': self.message_to_json(message)
}
return self.send_chat_message(content)
def messages_to_json(self, messages):
print('~~~messages_to_json~~~')
result = []
for message in messages:
result.append(self.message_to_json(message))
return result
def message_to_json(self, message):
print('~~~message_to_json~~~')
return {
'author': message.author.username,
'content': message.content,
'timestamp': str(message.timestamp)
}
commands = {
'fetch_messages': fetch_messages,
'new_message': new_message
}
def connect(self):
print('~~~connect~~~')
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_name
# Join room group
async_to_sync(self.channel_layer.group_add)(
self.room_group_name,
self.channel_name
)
self.accept()
def disconnect(self, close_code):
print('~~~disconnect~~~')
# Leave room group
async_to_sync(self.channel_layer.group_discard)(
self.room_group_name,
self.channel_name
)
# Receive message from WebSocket
def receive(self, text_data):
print('~~~receive: 1~~~')
data = json.loads(text_data)
self.commands[data['command']](self, data) # either fetch_messages or new_message
def send_chat_message(self, message):
print('in send_chat_message: 3')
async_to_sync(self.channel_layer.group_send)( # Send message to room group
self.room_group_name,
{
'type': 'chat_message',
'message': message
}
)
def send_message(self, message):
self.send(text_data=json.dumps(message))
# Receive message from room group
def chat_message(self, event):
message = event['message']
print('message: ' + str(message))
content1 = message['message']
print('content1: ' + str(content1))
content2 = content1['content']
print('content2: ' + content2)
self.send(text_data=json.dumps(message)) # Send message to WebSocket
~~~~~~~~~UPDATE:~~~~~~~~~ When i switched my procfile from
release: python manage.py migrate
web: daphne django_project.asgi:application --port $PORST --bind 0.0.0.0 v2
worker: python manage.py runworker channel_layer --settings=django_project.settigns -v2
to
web: gunicorn django_project.wsgi
my application successfully built! Why would my procfile be resulting in that error?
Answer
It seems like the problem is related to the way your Procfile
was configured to run the web server. Specifically, you were using daphne
to run your ASGI application, which caused issues with your app's initialization, leading to the AUTH_USER_MODEL
error. By switching to gunicorn
with wsgi
, your application built successfully.
Here's why it happened:
-
ASGI and WSGI Mismatch:
- Django supports both ASGI and WSGI, but they are used for different types of applications. ASGI (Asynchronous Server Gateway Interface) is designed for handling real-time features like WebSockets, long-lived connections, etc., while WSGI (Web Server Gateway Interface) is for more traditional HTTP applications.
daphne
is an ASGI server used to handle ASGI applications. However, in your case, the error message suggests that the Django app could not fully initialize due to some issue with your application setup when usingdaphne
.- When you switched to
gunicorn
withwsgi
, it was able to work becausegunicorn
is a WSGI server, and it loads the Django app in a traditional way, without the complexities of handling WebSockets or ASGI routing. It appears your application relies on standard HTTP requests and doesn't need real-time features provided by ASGI.
-
AUTH_USER_MODEL Issue:
- The
AUTH_USER_MODEL
error happens when theAUTH_USER_MODEL
setting in Django'ssettings.py
points to a user model that has not been installed or loaded correctly. - It's possible that
daphne
(which is more complex due to its ASGI nature) did not properly initialize the Django settings or database connections, preventing theAUTH_USER_MODEL
from being set correctly. gunicorn
withwsgi
would have avoided this issue because it initializes the application in a simpler, more predictable manner.
- The
Why daphne
failed:
- The issue could be that you may not have configured everything needed for
daphne
properly in your project orProcfile
.daphne
is often used with channels for real-time communications and might not handle some of the default Django startup behavior as expected. - It might have skipped the necessary startup sequence for the database and user models, which causes
AUTH_USER_MODEL
to refer to a model that Django doesn't recognize as installed.
Solution:
- Using Gunicorn is a safe choice if you're not explicitly using ASGI features like WebSockets or long-polling. It works well with standard Django applications, and your application seems to be fine with it.
- If you do need ASGI support (e.g., for WebSockets), then you should revisit your ASGI configuration and ensure that it properly initializes Django and handles the required settings. The error you encountered may also have been related to an incomplete or incorrect ASGI application configuration in
django_project.asgi.py
.
Conclusion:
Switching to gunicorn
works because it uses a WSGI-based approach and properly initializes the Django application. If you need to use daphne
for ASGI (for real-time features like WebSockets), you'll need to ensure that your configuration (especially in django_project.asgi.py
) is correct, and that all dependencies are properly initialized.