没有通过 django 和 heroku 部署获取静态文件

Not getting static files with django & heroku deployment

我正在使用 Django 1.7(project_name 是 "sonata")和 "Two scoops of Django 1.6" 的项目布局开发一个网络应用程序,所以我有一个 3 层的基本文件夹树。

.
├── docs
├── requirements
├── scripts
└── sonata
    ├── person
    │   └── templatetags
    ├── registration
    ├── sonata
    │   └── settings
    ├── static
    │   ├── css
    │   │   └── images
    │   ├── fonts
    │   └── js
    ├── templates
    │   ├── personApp
    │   └── registrationApp
    └── utils
        └── templatetags

我在 Heroku 上部署时遇到问题。我已实现部署,但静态文件未提供给浏览器。

我知道我应该通过设置强制使用某种方式来提供静态文件,并且已经在谷歌上搜索过了。我已经看过很多方法并阅读了有关使用 Amazon 服务的信息,但我正在寻找最简单的方法,它将使 gUnicorn 的未来生产部署也变得容易(我希望如此)。

我尝试修改设置文件(进一步向下,是 heroku.py 文件,它覆盖 base.py)并更改 STATIC_ROOT、STATICFILES_DIRS 的值并添加该行:

urlpatterns += static(settings.MEDIA_URL, document_root=settings.STATIC_ROOT)

我也试过了运行:

heroku run sonata/manage.py collectstatic

在做

之前
git push heroku master

但没有任何反应,甚至用

检查
heroku run ls sonata/assets 

正在复制文件。

拜托,我想要一些指导,以便真正理解我做错了什么并加以改正。

在了解 heroku 和部署时,我还遇到了一些使用 project_name.wsgi 文件的示例 ProcFiles,但我对此一无所知。

我需要一些帮助,因为我阅读的网页越多,我就越困惑。请假设我对部署知之甚少。 link 会很有用,但它必须显示新手的 material :-(

非常感谢您。

ProcFile:

web: python sonata/manage.py runserver 0.0.0.0:$PORT --noreload

heroku.py:

# -*- coding: utf-8 -*-
"""Heroku settings and globals."""

from __future__ import absolute_import

from .base import *

from os import environ

# TODO Warning! Heroku retrieve values as strings
# TODO we should check (only for Heroku) that 'True' and 'False' are respectively True and False 0, 
# or the equivalent ones, True and False
def get_env_setting(setting):
    """ Gets the environment variable or an Exception.
    This can be used, for example, for getting the SECRET_KEY and not having it hardcoded in the source code 
    Also for setting the active settings file for local development, heroku, production server, etc... """
    try:
        return environ[setting]
    except KeyError:
        error_msg = "Set the %s env variable" % setting
        raise ImproperlyConfigured(error_msg)

########## HOST CONFIGURATION
# See: https://docs.djangoproject.com/en/1.5/releases/1.5/#allowed-hosts-required-in-production
ALLOWED_HOSTS = ['*']
########## END HOST CONFIGURATION

########## EMAIL CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'

# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-host
EMAIL_HOST = environ.get('EMAIL_HOST', 'smtp.gmail.com')

# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-host-password
EMAIL_HOST_PASSWORD = environ.get('EMAIL_HOST_PASSWORD', '')

# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-host-user
EMAIL_HOST_USER = environ.get('EMAIL_HOST_USER', 'your_email@example.com')

# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-port
EMAIL_PORT = environ.get('EMAIL_PORT', 587)

# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-subject-prefix
EMAIL_SUBJECT_PREFIX = '[%s] ' % SITE_NAME

# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-use-tls
EMAIL_USE_TLS = True

# See: https://docs.djangoproject.com/en/dev/ref/settings/#server-email
SERVER_EMAIL = EMAIL_HOST_USER
########## END EMAIL CONFIGURATION

########## DATABASE CONFIGURATION
import dj_database_url
DATABASES['default'] = dj_database_url.config()
# DATABASES = {}
########## END DATABASE CONFIGURATION


########## CACHE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#caches
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
    }
}
########## END CACHE CONFIGURATION


########## SECRET CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
SECRET_KEY = get_env_setting('SECRET_KEY')
########## END SECRET CONFIGURATION

base.py:

# -*- coding: utf-8 -*-
"""Common settings and globals."""

from os.path import abspath, basename, dirname, join, normpath
from sys import path


from os import environ

def get_env_setting(setting):
    """ Gets the environment variable or an Exception.
    This can be used, for example, for getting the SECRET_KEY and not having it hardcoded in the source code 
    Also for setting the active settings file for local development, heroku, production server, etc... """
    try:
        return environ[setting]
    except KeyError:
        error_msg = "Set the %s env variable" % setting
        raise ImproperlyConfigured(error_msg)


########## PATH CONFIGURATION
# Absolute filesystem path to the Django project directory:
DJANGO_ROOT = dirname(dirname(abspath(__file__)))

# Absolute filesystem path to the top-level project folder:
SITE_ROOT = dirname(DJANGO_ROOT)

# Site name:
SITE_NAME = basename(DJANGO_ROOT)

# Add our project to our pythonpath, this way we don't need to type our project
# name in our dotted import paths:
path.append(DJANGO_ROOT)
########## END PATH CONFIGURATION


########## DEBUG CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#debug
DEBUG = False

# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-debug
TEMPLATE_DEBUG = DEBUG
########## END DEBUG CONFIGURATION


########## MANAGER CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#admins
ADMINS = (
    ('Your Name', 'your_email@example.com'),
)

# See: https://docs.djangoproject.com/en/dev/ref/settings/#managers
MANAGERS = ADMINS
########## END MANAGER CONFIGURATION


########## DATABASE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#databases
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.',
        'NAME': '',
        'USER': '',
        'PASSWORD': '',
        'HOST': '',
        'PORT': '',
    }
}
########## END DATABASE CONFIGURATION


########## GENERAL CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#time-zone
TIME_ZONE = 'Europe/Madrid'

# See: https://docs.djangoproject.com/en/dev/ref/settings/#language-code
LANGUAGE_CODE = 'es-es'
DEFAULT_CHARSET = 'utf-8'

# See: https://docs.djangoproject.com/en/dev/ref/settings/#site-id
SITE_ID = 1

# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n
USE_I18N = True

LOCALE_PATHS = (# Idiomas disponibles en la aplicación
                SITE_ROOT + '/locale',
                )

LANGUAGES = (# Ruta donde buscar ficheros de idioma
                 ('es', 'Español'),
                 ('gl', 'Galego'),
                 ('en', 'English'),
                 ('it', 'Italiano'),
             )

# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-l10n
USE_L10N = True

# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-tz
USE_TZ = True
########## END GENERAL CONFIGURATION


########## MEDIA CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root
MEDIA_ROOT = normpath(join(SITE_ROOT, 'media'))

# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-url
MEDIA_URL = '/media/'
########## END MEDIA CONFIGURATION


########## STATIC FILE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root
STATIC_ROOT = normpath(join(SITE_ROOT, 'assets'))

# See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url
STATIC_URL = '/static/'

# See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS
STATICFILES_DIRS = (
    normpath(join(SITE_ROOT, 'static')),
)

# See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders
STATICFILES_FINDERS = (
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
)
########## END STATIC FILE CONFIGURATION

########## LOGIN REDIRECTION
# The URL you'd like to redirect users to that aren't logged in
LOGIN_URL = '/registration/login/'
#########################################

########## LOGIN NOT REQUIRED
# Tuple of regular expressions that lists your exceptions to the default login required on every page.
LOGIN_EXEMPT_URLS = (
 r'^registration/login\.html$',
 r'^admin/',
)
#########################################

########## SECRET CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
# Note: This key should only be used for development and testing.
SECRET_KEY = r"I am not going to show you my secret key, sorry"
########## END SECRET CONFIGURATION


########## SITE CONFIGURATION
# Hosts/domain names that are valid for this site
# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
ALLOWED_HOSTS = []
########## END SITE CONFIGURATION


########## FIXTURE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FIXTURE_DIRS
FIXTURE_DIRS = (
    normpath(join(SITE_ROOT, 'fixtures')),
)
########## END FIXTURE CONFIGURATION


########## TEMPLATE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors
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.tz',
    'django.contrib.messages.context_processors.messages',
    'django.core.context_processors.request',
)

# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-loaders
TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.Loader',
    'django.template.loaders.app_directories.Loader',
)

# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-dirs
TEMPLATE_DIRS = (
    normpath(join(SITE_ROOT, 'templates')),
    normpath(join(SITE_ROOT, 'templates/registration')),
    normpath(join(SITE_ROOT, 'templates/person')),
)
########## END TEMPLATE CONFIGURATION


########## MIDDLEWARE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#middleware-classes
MIDDLEWARE_CLASSES = (
    # Default Django middleware.
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    # Custom middleware
    'sonata.settings.middleware.LoginRequiredMiddleware',
)
########## END MIDDLEWARE CONFIGURATION


########## URL CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf
ROOT_URLCONF = '%s.urls' % SITE_NAME
########## END URL CONFIGURATION


########## APP CONFIGURATION
DJANGO_APPS = (
    # Default Django apps:
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # Useful template tags:
    # 'django.contrib.humanize',

    # Admin panel and documentation:
    'django.contrib.admin',
    # 'django.contrib.admindocs',
)

# Apps specific for this project go here.
LOCAL_APPS = (
    'person',
    'registration',
    'utils',
)

# See: https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
INSTALLED_APPS = DJANGO_APPS + LOCAL_APPS
########## END APP CONFIGURATION


########## LOGGING CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#logging
# 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,
        },
    }
}
########## END LOGGING CONFIGURATION


########## WSGI CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application
WSGI_APPLICATION = '%s.wsgi.application' % SITE_NAME

Procfile 告诉 Heroku 它应该启动什么进程。没有它,Heroku 无法启动应用程序。但首先,对于 Python 应用程序,您需要在存储库的根目录下有一个 requirements.txt 文件。

我不熟悉 Two Scoops of Django(我知道这本书,但我已经 从来没有读过),所以我不确定该项目布局的全部内容。它是 肯定没有错,但对我来说似乎过于复杂,尤其是对于 完全是初学者,因为它有点偏离“规范”的铺设方式 出一个 Django 项目。所以我真的建议如果你刚刚开始 与 Django 一起来看看并完成 official Django tutorial(该页面上的“第一步”部分)。

现在假设您当前的布局,对于 Heroku,您需要在根目录中的两个文件 您的存储库(与 docsscripts 等目录处于同一级别 是):Procfilerequirements.txt。假设您的 WSGI 入口点是 在 sonata/sonata 目录中(应该叫 wsgi.py),Procfile 应该有这样的东西:

web: gunicorn sonata.sonata.wsgi --log-file -

请注意,您在此处显示的 Procfile 应该能够 运行(假设 Python 路径是正确的),但是您使用的是 应该的开发服务器 not 在部署环境中完成。我 Procfile 中的 gunicorn 东西 是标准生产质量 Python WSGI 应用程序服务器和 Procfile 简单地说 运行 gunicorn 这个应用程序。

requirements.txt 文件应该至少有:

Django>=1.7
gunicorn

在里面。这个文件告诉 Heroku 使用 Pip 安装哪些 Python 个包(Python's 包管理器)。在 本例最新版本的 Django 1.7 分支和 Gunicorn。还应该 列出您的项目具有的其他 Python 个依赖项。这个文件的存在 也是告诉 Heroku 您的应用程序是 Python 应用程序的原因。你应该 真的仔细看了Getting started with Django on Heroku 指南,因为它涵盖了基础知识和进一步阅读的指南。我同意 Heroku 的文档对于初学者来说有点乱, 但是如果你一步一步对不清楚的地方使用 Google 对您来说,您应该相对较快地完成工作设置。

至于静态文件,是的,你可以用static()函数来做 在 urlpatterns 中,即使您使用的是 Gunicorn 而不是开发服务器 但前提是 settings.py 中有 DEBUG = True,这绝对不应该 投入生产!如果这只是一个分期或测试,没关系,但即便如此 基本事实仍然存在:Heroku 基本上只是一个应用程序服务器。 它不适用于提供任何静态文件。理论上你可以服务 他们使用 static() 技巧从 Heroku 获取,但只要您进行新部署 或者一旦应用程序重新启动(这可能而且确实经常发生 由于多种原因)所有 的静态文件将消失。所以,实际上, 您必须 将您的静态文件托管在其他地方,无论是 S3 还是其他地方。

您在设置 static() 函数的方式上也可能存在错误,它 应该是(如果您还想提供媒体文件):

urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) +\
               static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

同样,这只会在很短的时间内起作用,直到您的应用重新启动为止 原因,此时您的应用程序将损坏,因为所有静态文件 都走了。

另请注意,您无需在 Heroku 上手动 运行 collectstatic。它 将检测 Django 应用程序并在部署时自动 运行 它。即使 它没有,在推送存储库之前你不能这样做。这是另一种方式 周围,​​你先推然后 collectstatic 运行s (你会看到它是 运行 自动)。

在我看来你真的被这个打结了,所以我的建议 将是退后一步。制作一个非常简单的香草 Django 应用程序(通过以下 我提到的教程),并尝试将其部署到 Heroku,你应该 遵循我链接的他们的 Django 部署基础知识。即使那里有那种方法 是可能的警告,但这些不在这个问题的范围之内。一步 一次(尽可能多地回溯,直到你弄清楚)你会没事的。

I'm developing a webapp with Django 1.7 (project_name is "sonata") and the project layout from the "Two scoops of Django 1.6", so I have a 3-tier basic folder tree.

您所指的 Two Scoops 模板仅适用于 1.6,您不应将其用于 1.7。

对于 django 1.7,您应该使用 cookiecutter recipe这是 1.7 版本的 link;默认存储库适用于 django 1.8。)

我不知道为什么你有一个 heroku.py 文件(这不是 2scoops 模板的一部分)并且不需要。

2scoops 的默认模板假设如下:

  1. 您将使用 Heroku 部署您的应用程序代码。
  2. 您将使用 AWS S3 部署静态文件。
  3. 您将为您的数据库使用来自 Heroku 的默认(免费)postgresql 服务。
  4. 您将使用 sendgrid(同样来自 Heroku)发送电子邮件。
  5. 您将使用 memcachier(托管的内存缓存服务,同样来自 Heroku)作为缓存。

确保这是您想要开始的方式,因为您需要单独设置 AWS 凭证。

假设您从头开始使用 django 1.7 的正确模板,要在 heroku 上启动它,您必须确保 为 Heroku 设置适当的环境变量,以确保事物可以正确连接。 default readme(作为项目的一部分生成)中列出了执行此操作的步骤。

请确保您 follow the guidelines 以确保您的项目设置正确。

作为初学者,这可能需要消化很多内容,尤其是如果您不熟悉 django/heroku/AWS。

我建议您改为遵循 heroku tutorial for django,这要简单得多,应该可以让您立即开始。

我终于明白了。我必须在 heroku.py 中包含以下几行:

heroku.py

# Important for Heroku
BASE_DIR = dirname(dirname(abspath(__file__)))

STATIC_ROOT = 'static' # Important for Heroku
STATIC_URL = '/static/'

STATICFILES_DIRS = (
    path.join(BASE_DIR, 'static'),  # Important for Heroku
)

我不得不为静态文件添加一些行到 wsgi.py:

wsgi.py

from django.core.wsgi import get_wsgi_application
from whitenoise.django import DjangoWhiteNoise

application = get_wsgi_application()
application = DjangoWhiteNoise(application)

同时更改 Procfile:

Procfile

web: gunicorn --chdir sonata --log-file - sonata.wsgi:application