AttributeError: 'Settings' object has no attribute 'ROOT_URLCONF' on Heroku

11,774

The problem occurred because I have switched from a single settings file to a settings package, updated the references in manage.py, so it works locally, but didn't reflect that in wsgi.py which is what configures the server on Heroku.

wsgi.py now sets environment-specific settings:

os.environ["DJANGO_SETTINGS_MODULE"] = "main.settings." + os.environ["ENV"]

Share:
11,774
KindOfGuy
Author by

KindOfGuy

Learning to love Django, but falling in love with StackOverflow faster. Making web apps for blue-sky, social good.

Updated on June 13, 2022

Comments

  • KindOfGuy
    KindOfGuy almost 2 years

    I am getting this error when I deploy to Heroku:

    2014-05-07T11:50:06.927955+00:00 heroku[router]: at=info method=GET path=/favicon.ico host=staging.mysite.com request_id=eead056c-f89d-4fcd-b282-71a023631a71 fwd="80.237.234.148" dyno=web.1 connect=1ms service=4ms status=500 bytes=238
    2014-05-07T11:50:06.925723+00:00 app[web.1]: 2014-05-07 06:50:06 [7] [ERROR] Error handling request
    2014-05-07T11:50:06.925732+00:00 app[web.1]: Traceback (most recent call last):
    2014-05-07T11:50:06.925746+00:00 app[web.1]:     urlconf = settings.ROOT_URLCONF
    2014-05-07T11:50:06.925735+00:00 app[web.1]:   File "/app/.heroku/python/lib/python2.7/site-packages/gunicorn/workers/sync.py", line 126, in handle_request
    2014-05-07T11:50:06.925738+00:00 app[web.1]:     respiter = self.wsgi(environ, resp.start_response)
    2014-05-07T11:50:06.925740+00:00 app[web.1]:   File "/app/.heroku/python/lib/python2.7/site-packages/django/core/handlers/wsgi.py", line 241, in __call__
    2014-05-07T11:50:06.925742+00:00 app[web.1]:     response = self.get_response(request)
    2014-05-07T11:50:06.925744+00:00 app[web.1]:   File "/app/.heroku/python/lib/python2.7/site-packages/django/core/handlers/base.py", line 82, in get_response
    2014-05-07T11:50:06.925751+00:00 app[web.1]:     return func(self._wrapped, *args)
    2014-05-07T11:50:06.925749+00:00 app[web.1]:   File "/app/.heroku/python/lib/python2.7/site-packages/django/utils/functional.py", line 185, in inner
    2014-05-07T11:50:06.925753+00:00 app[web.1]: AttributeError: 'Settings' object has no attribute 'ROOT_URLCONF'
    

    Locally it works fine, and in the remote bash shell, it seems that django can access ROOT_URLCONF, as I am running:

    ~ $ python manage.py shell
    Python 2.7.4 (default, Apr 6 2013, 22:14:13)
    In [2]: from django.conf import settings
    In [3]: settings.ROOT_URLCONF
    Out[3]: 'urls'
    

    The settings file therefore seems accessible to manage.py and includes the correct ROOT_URLCONF variable.

    I have tried very many configurations with no luck. Any ideas?

    Here are my current manage.py and settings configurations (env variable ENV is set to staging here):

    # manage.py
    
    #!/usr/bin/env python
    import os
    import sys
    
    environment = os.environ['ENV']
    
    if __name__ == "__main__":
        os.environ.setdefault("DJANGO_SETTINGS_MODULE", 'main.settings.' + environment)
    
        from django.core.management import execute_from_command_line
        execute_from_command_line(sys.argv)
    

    settings/ folder contains an empty __init__.py and:

    # staging.py
    
    from .base import *
    
    MIDDLEWARE_CLASSES = (
        'django.middleware.common.CommonMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django_facebook.auth_backends.FacebookBackend',
        'fandjango.middleware.FacebookMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        # 'debug_toolbar.middleware.DebugToolbarMiddleware',
        # Uncomment the next line for simple clickjacking protection:
        # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
    )
    
    INSTALLED_APPS = (
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.sites',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'south',
        'crewcal',
        'django_extensions',
        'registration',
        'django_facebook',
        'fandjango',
        'django.contrib.humanize',
        'storages',
        'datetimewidget',
        'postman',
        'notification',
        # 'raven.contrib.django.raven_compat',
        # Uncomment the next line to enable the admin:
        'django.contrib.admin',
        # Uncomment the next line to enable admin documentation:
        # 'django.contrib.admindocs',
    )
    
    
    
    # base.py
    
    ENV = 'DEVELOPMENT'
    
    import os
    import sys
    
    MAIN_APP_DIR = os.path.dirname(__file__)
    PROJECT_NAME = MAIN_APP_DIR.split('/')[-1]
    project_root = lambda f: os.path.abspath(os.path.join(MAIN_APP_DIR, '../../', f))
    
    
    PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
    PROJECT_DIR = os.path.join(PROJECT_ROOT,'../../crewcal')
    
    WEB_ROOT = project_root('webroot')
    
    
    sys.path.insert(0, os.path.join(MAIN_APP_DIR, '../'))
    sys.path.insert(0, MAIN_APP_DIR)
    
    dev = 'DEVELOPMENT'
    
    DEBUG = (ENV is dev)
    TEMPLATE_DEBUG = DEBUG
    
    TESTING = 'test' in sys.argv
    
    ADMINS = (
        ('Me', '[email protected]'),
    )
    
    MANAGERS = ADMINS
    
    DATABASE_CONFIGS = {
        'DEVELOPMENT': {
            'default': {
                'ENGINE': 'django.db.backends.postgresql_psycopg2', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
                'NAME': 'ssc',                      # Or path to database file if using sqlite3.
                'USER': 'postgres',                      # Not used with sqlite3.
                'PASSWORD': 'xxx',                  # Not used with sqlite3.
                'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
                'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
            }
        },
    }
    
    DATABASES = DATABASE_CONFIGS[ENV]
    
    SITE_ID = 1
    
    # If you set this to False, Django will make some optimizations so as not
    # to load the internationalization machinery.
    USE_I18N = False
    
    # If you set this to False, Django will not format dates, numbers and
    # calendars according to the current locale.
    USE_L10N = True
    
    gettext = lambda s: s
    LANGUAGES = (('en', gettext('English')),)
    
    MEDIA_ROOT = os.path.join(WEB_ROOT, 'media')
    MEDIA_URL = '/media/'
    
    STATIC_ROOT = os.path.join(PROJECT_DIR, 'static')
    
    AWS_ACCESS_KEY_ID = 'SECRET'
    AWS_SECRET_ACCESS_KEY = 'SECRET'
    AWS_STORAGE_BUCKET_NAME = 'vvv'
    
    # URL prefix for static files.
    # Example: "http://media.lawrence.com/static/"
    try:
        if os.environ['ENV'] == 'staging' or 'production':
            STATIC_URL = 'https://s3-eu-west-1.amazonaws.com/vvv/'
    except:
        STATIC_URL = '../mysite/static/'
    
    # Additional locations of static files
    STATICFILES_DIRS = (
        # Put strings here, like "/home/html/static" or "C:/www/django/static".
        # Always use forward slashes, even on Windows.
        # Don't forget to use absolute paths, not relative paths.
    )
    
    # List of finder classes that know how to find static files in
    # various locations.
    STATICFILES_FINDERS = (
        'django.contrib.staticfiles.finders.FileSystemFinder',
        'django.contrib.staticfiles.finders.AppDirectoriesFinder',
    #    'django.contrib.staticfiles.finders.DefaultStorageFinder',
    )
    
    # Make this unique, and don't share it with anybody.
    SECRET_KEY = os.environ['SECRET_KEY']
    
    # List of callables that know how to import templates from various sources.
    TEMPLATE_LOADERS = (
        'django.template.loaders.filesystem.Loader',
        'django.template.loaders.app_directories.Loader',
    #     'django.template.loaders.eggs.Loader',
    )
    
    try:
        if os.environ['ENV'] == 'staging' or 'production':
            EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
    except:
        EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
    
    EMAIL_FILE_PATH = (
        #os.path.join(SITE_ROOT, '/app-email-messages') # change this to a proper location)
    )
    ACCOUNT_ACTIVATION_DAYS = 7
    EMAIL_HOST = 'smtp.gmail.com'
    EMAIL_HOST_USER = '[email protected]'
    EMAIL_HOST_PASSWORD = os.environ['EMAIL_HOST_PASSWORD']
    EMAIL_PORT = 587
    EMAIL_USE_TLS = True
    
    POSTMAN_AUTO_MODERATE_AS = True
    
    ROOT_URLCONF = 'main.urls'
    
    # Python dotted path to the WSGI application used by Django's runserver.
    WSGI_APPLICATION = 'wsgi.application'
    
    
    PROJECT_PATH = os.path.realpath(os.path.dirname(os.path.dirname(__file__)))
    
    TEMPLATE_DIRS = (
        os.path.join(PROJECT_PATH, 'templates/'),
        # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
        # Always use forward slashes, even on Windows.
        # Don't forget to use absolute paths, not relative paths.
    )
    
    STATICFILES_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
    
    FACEBOOK_APP_ID = 'app-id'
    FACEBOOK_APP_SECRET = os.environ['FACEBOOK_APP_SECRET']
    
    # fandjango settings
    FACEBOOK_APPLICATION_ID = 'app-id'
    FACEBOOK_APPLICATION_SECRET_KEY = os.environ['FACEBOOK_APP_SECRET']
    FACEBOOK_APPLICATION_NAMESPACE = 'me'
    
    try:
        if os.environ['DOMAIN'] == 'crew':
            DOMAIN = 'crew'
            EVENT_PUB_COMMITTED_CRITICAL_MASS = 3
        elif os.environ['DOMAIN'] == 'staging':
            DOMAIN = 'staging'
            EVENT_PUB_COMMITTED_CRITICAL_MASS = 2
    except:
        DOMAIN = 'crew'
        EVENT_PUB_COMMITTED_CRITICAL_MASS = 3
    
    
    try:
        if os.environ['ENV'] == 'staging':
            MAX_PROPOSED_EVENTS = 3
    except:
        MAX_PROPOSED_EVENTS = 300
    
    POSTMAN_DISALLOW_ANONYMOUS = True
    POSTMAN_AUTO_MODERATE_AS = True
    
    
    INTERNAL_IPS = ('127.0.0.1',)
    
    LOGIN_URL = '/login'
    LOGOUT_URL = '/logout'
    LOGIN_REDIRECT_URL = "/user"
    
    AUTH_PROFILE_MODULE = 'me.UserProfile'
    AUTHENTICATION_BACKENDS = (
        'crewcal.backends.EmailOrUsernameModelBackend',
        'django.contrib.auth.backends.ModelBackend',
        'django_facebook.auth_backends.FacebookBackend',
        'insensitive.backends.CaseInsensitiveModelBackend'
    )
    
    # A sample logging configuration. The only tangible logging
    # performed by this configuration is to send an email to
    # the site admins on every HTTP 500 error when DEBUG=False.
    # See http://docs.djangoproject.com/en/dev/topics/logging for
    # more details on how to customize your logging configuration.
    LOGGING = {
        'version': 1,
        'disable_existing_loggers': False,
        'filters': {
            'require_debug_false': {
                '()': 'django.utils.log.RequireDebugFalse'
            }
        },
        'handlers': {
            'mail_admins': {
                'level': 'ERROR',
                'filters': ['require_debug_false'],
                'class': 'django.utils.log.AdminEmailHandler'
            }
        },
        'loggers': {
            'django.request': {
                'handlers': ['mail_admins'],
                'level': 'ERROR',
                'propagate': True,
            },
        }
    }
    
    TEMPLATE_CONTEXT_PROCESSORS = (
        "django.contrib.auth.context_processors.auth",
        "django.core.context_processors.debug",
        "django.core.context_processors.i18n",
        "django.core.context_processors.media",
        "django.core.context_processors.static",
        "django.core.context_processors.request",
        "django.core.context_processors.tz",
        "django.contrib.messages.context_processors.messages",
        "django_facebook.context_processors.facebook"
    )
    
    
    try:
        if os.environ['ENV'] == 'production':
            DEBUG = False
            ALLOWED_HOSTS = ['me.org.uk',]
            # Parse database configuration from $DATABASE_URL
            import dj_database_url
            DATABASES['default'] =  dj_database_url.config()
    
            # Honor the 'X-Forwarded-Proto' header for request.is_secure()
            SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
        if os.environ['ENV'] == 'staging':
            DEBUG = False
            ALLOWED_HOSTS = ['me.org.uk',]
            # Parse database configuration from $DATABASE_URL
            import dj_database_url
            DATABASES['default'] =  dj_database_url.config()
    
            # Honor the 'X-Forwarded-Proto' header for request.is_secure()
            SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
    except:
        pass