Forums

Django CORS: Spinning in Circles

Hello! I have a backend application running Django here on PythonAnywhere, and am going through it something fierce trying to get an external Frontend application (hosted on Vercel) to access my backend. All calls to the backend result in CORS errors. I'm clearly fundamentally misunderstanding how CORS works, although I have read several blogs posts (this and this have been particularly helpful) and the Django cors headers docs on the subject. It's been about two weeks and this is holding up the frontend team, eeep. I have a Hacker PythonAnywhere subscription, if that matters or affects my settings somehow.

There are two potential ways I believe I have to move forward: 1) get the CORS headers set up appropriately or 2) disable CORS backend-wide, let it rip. I would happily do 2) at this point just to move things forward, but even that fails for me.

Current backend setup:

I am running inside of a virtualenv on PythonAnywhere. Within that venv I have the following requirements.txt:

asgiref==3.6.0
attrs==22.2.0
colorama==0.4.6
dj-database-url==1.2.0
dj-static==0.0.6
Django==4.1.6
django-cors-headers==3.13.0
django-cors-middleware==1.5.0
django-environ==0.9.0
django_debug_toolbar==3.8.1
djangorestframework==3.14.0
exceptiongroup==1.1.0
factory-boy==3.2.1
Faker==16.6.0
gitdb==4.0.10
GitPython==3.1.30
iniconfig==2.0.0
packaging==23.0
pluggy==1.0.0
psycopg2==2.9.5
psycopg2-binary==2.9.5
pytest==7.2.1
pytest-django==4.5.2
python-dateutil==2.8.2
python-decouple==3.7
python-dotenv==0.21.1
pytz==2022.7.1
six==1.16.0
smmap==5.0.0
sqlparse==0.4.3
static3==0.7.0
tomli==2.0.1
tzdata==2022.7

Note that Django==4.1.6 (docs), django-cors-headers==3.13.0 (docs) and django-cors-middleware==1.5.0 (docs) are all the current up to date version.

Here is my settings.py file:

import os
import environ
env = environ.Env()
environ.Env.read_env()
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SECRET_KEY = env('SECRET_KEY')
DEBUG = env('DEBUG')
ALLOWED_HOSTS = [
'0.0.0.0',
'localhost',
'http://localhost:3000',
'http://localhost:8000',
'http://dbajamteam.pythonanywhere.com',
'https://dbajamteam.pythonanywhere.com',
'dbajamteam.pythonanywhere.com'
]


INSTALLED_APPS = [
'rest_framework',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'jamrequestmodule',
# 'corsheaders',
]

MIDDLEWARE = [
# These should work per Django docs, but when enabled, PythonAnywhere VirtualEnv does not load.
# The error states corsheaders/signals.py received an unexpected keyword argument 'providing_args'
# StackOverflow suggests this is an issue with dependencies (Django and CorsHeaders mismatch)
# Both within, and outside of, the VirtualEnv, Django and CorsHeaders are both the most up-to-date versions: which per the docs are compatible
# 'corsheaders.middleware.CorsMiddleware',
# 'django.middleware.common.CommonMiddleware',
# "jamrequestmodule.middleware.CorsMiddleware",

'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',

]
CORS_ALLOW_ALL_ORIGINS = True
INTERNAL_IPS = ['127.0.0.1']
ROOT_URLCONF = 'jamrequestmodule.urls'

TEMPLATES = [
{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [],
    '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',
        ],
    },
},
]

WSGI_APPLICATION = 'jamrequestmodule.wsgi.application'
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 = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

API_PORT = env('API_PORT')
API_HOST = env('API_HOST')

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
    'rest_framework.renderers.JSONRenderer',
    ]
}



# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/

STATIC_URL = '/static/'

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


# CORS_ORIGIN_WHITELIST = [
#      'http://localhost:3000'
# ]

# CORS_ORIGIN_WHITELIST = ['*']

# CORS_ORIGIN_ALLOW_ALL = False

# CORS_ORIGIN_WHITELIST = [
#      "http://jambuddy.vercel.app",
#      "https://jambuddy.vercel.app",
#      'http://localhost:3000',
#      'https://localhost:3000',
#      'http://localhost:8000',
#      'https://localhost:8000',
# ]

# CORS_ORIGIN_REGEX_WHITELIST = (
#     r'^(https?://)?(www\.)?(jambuddy\.vercel\.app|localhost:3000)\.*$'
# )

CORS_ALLOWED_ORIGINS = [
 "http://jambuddy.vercel.app",
 "https://jambuddy.vercel.app",
 "http://jambuddy.vercel.app/",
 "https://jambuddy.vercel.app/",
 'http://localhost:3000',
 'https://localhost:3000',
 'http://localhost:8000',
 'https://localhost:8000',
]

# CORS_ORIGIN_ALLOW_ALL = True
# CORS_ALLOW_CREDENTIALS = True

Case 1: As I understand it, when I enabled (uncomment) corsheaders under INSTALLED_APPS and corsheaders.middleware.CorsMiddleware and django.middleware.common.CommonMiddleware under MIDDLEWARE, and at the bare minium, enabled my CORS_ORIGIN_WHITELIST with the URLs of my frontend and local apps, this should allow my app to accept outside calls. However, when I do this, I get the following error trace and the app does not run (the classic PythonAnywhere Something went wrong...):

File "/home/dbajamteam/.virtualenvs/myvirtualenv/lib/python3.10/site-packages/corsheaders/models.py", line 4, in <module>
from .signals import check_request_enabled  # noqa
File "/home/dbajamteam/.virtualenvs/myvirtualenv/lib/python3.10/site-packages/corsheaders/signals.py", line 5, in <module>
check_request_enabled = django.dispatch.Signal(
TypeError: Signal.__init__() got an unexpected keyword argument 'providing_args'

Per this StackOverflow discussion, Django removed the need for providing_args in 4.0, so clearly Signals.py in corsheaders is providing it somehow when it is not needed?

Case 2: Alternatively, if I enabled CORS_ORIGIN_ALLOW_ALL, I should be able to bypass CORS completely for dev purposes. But doing that (although the backend site runs without error) does not actually allow the frontend to make the calls, and they still receive the pre-flight connection error:

Access to XMLHttpRequest at 'https://dbajamteam.pythonanywhere.com/api/instruments/' from origin 'https://jambuddy.vercel.app' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Would appreciate any assistance or ideas! I'm sure its one of those little things that just gets overlooked.

The unexpected keyword argument error is a version mismatch between corsheaders and Django. Perhaps corsheaders does not yet support Django 4+. Check the corsheaders documentation for the versions of Django that is supports.

As to the use of CORS_ORIGIN_ALLOW_ALL, the github page for cors headers suggests that the correct setting to use is CORS_ALLOW_ALL_ORIGINS

Thank you so much for your response! Unfortunately, both those suggestions did not work for me.

Per the django-cors-headers docs:

Django 3.2 to 4.1 supported.

I rolled back Django to 4.1, then to 4.0.9 within the virtual environment, both to the same effect (providing_args error).

I tried CORS_ALLOW_ALL_ORIGINS = true instead of CORS_ORIGIN_ALLOW_ALL = true, to the same effect (site runs, but still get CORS pre-flight error).

Will take any additional suggestions!

Did you reload your web app on the Web tab after making those changes?

Remove the package django-cors-middleware.