Reputation: 166
I have a simple Django FileForm for multiple files upload (basically txts with polygon coordinates). I don't want to save the uploaded files anywhere but only treat them on the fly (memory?) and return a leaflet map with the polygons and a new file of mine (which transforms and exports all the txts in a .dxf format file (CAD combatible)).
I have done all the logic and my programm seems to work fine on development but on production (Heroku) when i hit the submit button on my form I keep getting CSRF Forbidden 403 message. I am really frustrated. I have used csfr decorators (csrf_exempt, requires_csrf_token, ensure_csrf_cookie) on my views but none seems to work. I have included enctype="multipart/form-data"> and {% csrf_token %} on my form template.
What's the problem? It's hard for me to test and try because on development everything is ok and I have to deploy everytime only to see if another approach works.
Is it because I am not saving the files on a model and on a disk (What's the logic here?), or is it because I'm missing something on the CSRF part on my settings?
settings.py (when deploying)
"""
Django settings for myKAEK project.
Generated by 'django-admin startproject' using Django 4.0.4.
For more information on this file, see
https://docs.djangoproject.com/en/4.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.0/ref/settings/
"""
from pathlib import Path
import os
from decouple import config
from decouple import config, Csv
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = config('SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
ALLOWED_HOSTS = ['127.0.0.1', 'localhost', 'mykaek-gr.herokuapp.com', 'myKAEK.gr', 'www.myKAEK.gr']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.gis',
'djgeojson',
'storages',
'debug_toolbar',
'myKAEKapp',
]
MIDDLEWARE = [
"debug_toolbar.middleware.DebugToolbarMiddleware",
'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',
]
INTERNAL_IPS = [
"127.0.0.1",
]
ROOT_URLCONF = 'myKAEK.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 = 'myKAEK.wsgi.application'
GOOGLE_MAPS_API = config('GOOGLE_MAPS_API')
# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.contrib.gis.db.backends.postgis',
# 'NAME': config('DB_NAME'),
# 'USER': config('DB_USER'),
# 'PASSWORD': config('DB_PASSWORD'),
# 'HOST': config('DB_HOST'),
# 'PORT': config('DB_PORT'),
# }
#}
# Password validation
# https://docs.djangoproject.com/en/4.0/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/4.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/
# STATIC_URL = 'static/'
# STATIC_ROOT = os.path.join(BASE_DIR)
# STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
# MEDIA_URL = '/media/'
# MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
USE_S3 = config('USE_S3') == 'TRUE'
if USE_S3:
# aws settings
AWS_ACCESS_KEY_ID = config('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = config('AWS_SECRET_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = config('AWS_STORAGE_BUCKET_NAME')
AWS_DEFAULT_ACL = 'public-read'
AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com'
AWS_S3_OBJECT_PARAMETERS = {'CacheControl': 'max-age=86400'}
# s3 static settings
AWS_LOCATION = 'static'
STATIC_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/{AWS_LOCATION}/'
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
# s3 public media settings
PUBLIC_MEDIA_LOCATION = 'media'
MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/{PUBLIC_MEDIA_LOCATION}/'
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
# 'myKAEKapp.storage_backends.PublicMediaStorage'
else:
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'mediafiles')
STATICFILES_DIRS = (os.path.join(BASE_DIR, 'static'),)
# MEDIA_URL = '/mediafiles/'
# MEDIA_ROOT = os.path.join(BASE_DIR, 'mediafiles')
# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# Heroku: Update database configuration from $DATABASE_URL.
import dj_database_url
db_from_env = dj_database_url.config(conn_max_age=120)
DATABASES = { 'default': dj_database_url.config() }
DATABASES['default'].update(db_from_env)
DATABASES['default']['ENGINE'] = 'django.contrib.gis.db.backends.postgis'
CSRF_TRUSTED_ORIGINS = ["https://mykaek-gr.herokuapp.com/", "https://mykaek.gr/"]
forms.py
class UploadFileForm(forms.Form):
files = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
html
<div class="row justify-content-center align-items-center text-center" style="padding-top: 30px; width: 500px; margin: auto;">
<fieldset name="Multiple Files Upload">
<form method="post" action="/draw-polygons/" enctype="multipart/form-data">{% csrf_token %}
<dl><input class="form-control" type="file" id="formFileMultiple" name="files" required multiple></dl>
<button type="submit" class="btn btn-outline-dark" style="margin-top: 10px;">Φόρτωση</button>
</form>
</fieldset>
</div>
view.py
def upload_multiple_files(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
files_list = request.FILES.getlist('files')
if form.is_valid():
for f in files_list:
try:
'''my code here'''
except:
'''my code here'''
'''my code here'''
return render(request, "multiple/success.html", context)
else:
form = UploadFileForm()
return render(request, 'multiple/multiple.html', {'title': 'myKAEK.gr | Draw Polygons', 'form': form})
def txt_dxf(request):
topos = cache.get('topos')
doc = ezdxf.new("R2000")
msp = doc.modelspace()
doc.layers.add('POLYS_FROM_TXTS')
for i in topos:
msp.add_lwpolyline(i, dxfattribs={"layer": "POLYS_FROM_TXTS"})
out = io.BytesIO()
doc.write(out, fmt='bin')
out.seek(0)
return FileResponse(out, as_attachment=True, filename='polygons_from_txt.dxf')
urls
urlpatterns = [
path('draw-polygons/', views.upload_multiple_files, name='upload_multiple_files'),
path('draw-polygons/export_dxf/', views.txt_dxf, name='txt_dxf'),
]
If I may add something I saw after my original question was posted:
While my custom domain served through cloudflare returns CSRF Forbidden 403, my herokuapp server returns for the same page Bad request 400 I don't know if it's relevant but I thought I should add.
Also when I use @csrf_exempt on my view and delete {% csrf_token %} from template I get 400 Bad Request on my custom domain too.
Is it beacouse I use Cloudflare (with cache disabled) between my custom domain and herokuapp?
Please help I really need to get this function work.
Upvotes: 0
Views: 394
Reputation: 361
Try doing this using Django Forms, it will save you from writing extra code and will handle such edge cases.
https://docs.djangoproject.com/en/4.1/topics/http/file-uploads/#uploading-multiple-files
Upvotes: 0