简体   繁体   中英

Django ManifestStaticFilesStorage + DEBUG = False gives error in uploaded files: They don't show

I have a problem and I will do the best to explain it, to see if you can help me out.

CONTEXT

I have a system running in a test server, which is set up like a production environment in order to test my code before merging to master and give the go to the production server provider of my client to update the code. This means it runs in DEBUG = False . All good, all perfect for months.

I decided to activate the ManifestStaticFilesStorage setting in order to have a hash number added in my static files, I've used it before and it's a good way to break cache rules when updating files (like CSS rules that refuse to load). There is an issue with cache that may be solvable messing around with the server but that's not an option in this case.

Everything went smoothly:

  1. No issues in collectstatic other than a few missing static files (already solved)
  2. Static files loaded perfectly

BUT...

THE PROBLEM

This system manages content (images, audio files and custom fonts). When I activated the ManifestStaticFilesStorage setting, all uploaded files started to throw 404 errors (and some occasional 500 error) in the server access log. Meaning, they look like this:

破碎的图像

You can see the broken image icon but you can also see the background colores of each square (color which is injected by JS because it can be customized in the custom CMS). These images are uploaded in the CMS and they live in the media folder configured in the settings file.

Of course, if I go to DEBUG = True , everything gets fixed (come on. -.-), I went and recreated production enviroment in local: same issue: DEBUG = False bad, DEBUG = True works

THOUGHTS

  • 404 means the file is not there. Guess what? it's there, they all are
  • The occasional 500 means permissions. Well, I haven't changed the permissions. Also, permissiones where the same. Also, I'm using WebFaction, it handles all that for me
  • Console in browser says network error. In the Network tab it doesn't even show the 404 errors or the few 500 that may appear
  • Broken DB? Nope. Besides, if it were broken, DEBUG = True would fail
  • Apache error log? Nothing to show for. The access log shows the access errors, meaning errors on file that are correctly placed

So, I ran out of ideas. Maybe someone out there has the answer, I hope so. I will still be trying to solve it but I can use the help, please.

RELEVANT CODE

storage.py

class ManifestStaticFilesStorageNotStrict(ManifestStaticFilesStorage):
    """A relaxed implementation of django's ManifestStaticFilesStorage.
    """
    manifest_strict = False

settings.py (redacted)

# -*- coding: utf-8 -*-

# Standard libs imports
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os

# Django libs imports
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _

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

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = <SECRET KEY HERE (: >

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

ALLOWED_HOSTS = ['*']

##########################
# APPLICATION DEFINITION #
##########################

DJANGO_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.sitemaps',
    'django.contrib.staticfiles',
]

THIRD_PARTY_APPS = [
    # https://github.com/audiolion/django-behaviors
    'behaviors.apps.BehaviorsConfig',
    # https://github.com/zostera/django-bootstrap4
    'bootstrap4',
    # https://django-ckeditor.readthedocs.io/en/latest/
    'ckeditor',
    # https://github.com/praekelt/django-recaptcha
    'captcha',
    # https://django-tables2.readthedocs.io/en/latest/index.html
    'django_tables2',
    # https://github.com/django-extensions/django-extensions
    'django_extensions',
]

CUSTOM_APPS = [
    <CUSTOM APPS HERE (: >
]

ELASTICSEARCH_DSL = {
    <ELASTICSEARCH DATA HERE (: >
}

INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + CUSTOM_APPS

SITE_ID = 1

AUTH_USER_MODEL = 'users.User'

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',

    # https://github.com/PaesslerAG/django-currentuser
    'django_currentuser.middleware.ThreadLocalUserMiddleware',
]

ROOT_URLCONF = 'main.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'main/templates')],
        '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',
                'main.context_processors.add_to_context',
            ],
        },
    },
]

DJANGO_TABLES2_TEMPLATE = 'django_tables2/bootstrap-responsive.html'

WSGI_APPLICATION = 'main.wsgi.application'

LOGIN_URL = reverse_lazy('back_office:auth:login')

SILENCED_SYSTEM_CHECKS = ['captcha.recaptcha_test_key_error']

#####################
# DATABASE SETTINGS #
#####################
#
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases

DATABASES = {
    <DATABASE DATA HERE (: >
}

##########################
# AUTHENTICATION BACKEND #
##########################
#
# https://docs.djangoproject.com/en/2.0/topics/auth/customizing/

AUTHENTICATION_BACKENDS = [
    <AUTHENTICATION_BACKENDS DATA HERE (: >
]

################################
# PASSWORD VALIDATION SETTINGS #
################################
#
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation'
                '.UserAttributeSimilarityValidator',
    },
    # {
    #     'NAME': 'django.contrib.auth.password_validation'
    #             '.MinimumLengthValidator',
    # },
    {
        'NAME': 'main.validators.password_validators'
                '.CustomMinimumLengthValidator',
    },
    # {
    #     'NAME': 'django.contrib.auth.password_validation'
    #             '.CommonPasswordValidator',
    # },
    {
        'NAME': 'main.validators.password_validators'
                '.CustomCommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation'
                '.NumericPasswordValidator',
    },
]

##################
# GOOGLE ANALYTICS
##################

GOOGLE_ANALYTICS_ID = ""


##################
# EMAIL SETTINGS #
##################

<SETTINGS HERE (: >

#################################
# INTERNATIONALIZATION SETTINGS #
#################################
#
# https://docs.djangoproject.com/en/1.11/topics/i18n/

LANGUAGE_CODE = 'es'

LANGUAGES = (
    ('en', _('English')),
    ('es', _('Spanish')),
)

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

LOCALE_PATHS = (
    os.path.join(BASE_DIR, 'locale'),
)

#############################
# STATIC FILES SETTINGS     #
# (CSS, JavaScript, Images) #
#############################
#
# https://docs.djangoproject.com/en/1.11/howto/static-files/

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

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

##############
# RECAPTCHA #
##############

<MORE SETTINGS HERE (: >

################
# FILE STORAGE #
################
#
# This settings is for a custom random number to add to all uploaded
# files in order to break cache

DEFAULT_FILE_STORAGE = 'main.storage.CustomFileSystemStorage'

##################
# LOCAL SETTINGS #
##################
#
# This is the file that contains local configurations like DB passwords,
# keys, user for an API, etc.
#
# This import is done at the end because it will override the default settings
# stablish here.

try:
    from .local_settings import *  # noqa
except Exception as e:
    pass

###################
# CKEDITOR CONFIG #
###################
CKEDITOR_CONFIGS = {
    'default': {
        # 'skin': 'moono',
        'toolbar': 'full',
        'skin': 'office2013',
        'width': '100%',
    }
}

########################
# STATIC FILES STORAGE #
########################
STATICFILES_STORAGE = \
    'main.storage.ManifestStaticFilesStorageNotStrict'

local_settings.py (redacted)

# Production settings

DEBUG = False

###################
# ALLOWED DOMAINS #
###################

ALLOWED_HOSTS = [
    'localhost',
    'localhost:8000',
    '127.0.0.1',
    '127.0.0.1:8000',
    <DOMAIN DATA HERE (: >
]


#############
# DATABASES #
#############

DATABASES = {
    <DATABASE DATA HERE (: >
}


################
# STATIC FILES #
################

STATIC_ROOT = '<PATH TO SERVER STATIC FOLDER>'

MEDIA_ROOT = '<PATH TO SERVER STATIC FOLDER>/media'

Thanks in advance.

Well, after hitting my head to the wall a lot, a good night sleep and fresh ideas, I found the problem. It was the silliest thing ever, as usual.

If you can see in the settings.py , the MEDIA_URL is set to /media/ . Normally, this wouldn't be a problem in an Apache server but in WebFaction, it turned out to be the culprit of my headache.

For those who don't know, WebFaction obliges you to create apps for everything: Python env, PHP env, Static env, WordPress, Joomla, etc. By creating an app, you have to assing a unique path inside the Website definition, which is a set of apps running under a given domain, so all apps have paths under the same domain and you don't have to modify (in most cases) the httpd.conf file.

I have an app for the Django code and another app for the static files, under the domain static/ . In the local_settings.py , you can see that the MEDIA_ROOT is inside this static path.

In a regular Apache deploy, you just assign the /media/ URL to the path and that's it. In WebFaction, given that there is no static app for using the media/ , the MEDIA_URL variable has to have static/media as assigned value when switching to DEBUG = False .

Just to remember: DEBUG = False make Django stop serving the static files and leave that entirely to the server where is deployed. So, in DEBUG = True , it didn't matter which was the media URL because Django is smart enough to see past that. But when I made the switch to DEBUG = False , it naturally failed.

Silly me, but well, this is how we learn.

Regards.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM