2

我正在使用 Django 1.7(project_name 是“sonata”)和“Two scoops of Django 1.6”的项目布局开发一个 webapp,所以我有一个 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,但我对此一无所知。

我可以使用一些帮助,因为我阅读的网页越多,我就越困惑。请假设我对部署知之甚少。一个链接会很有用,但它必须显示新手的材料:-(

非常感谢您提前。

过程文件:

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

基础.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
4

3 回答 3

2

我终于明白了。我必须在 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:

档案

web: gunicorn --chdir sonata --log-file - sonata.wsgi:application
于 2015-09-28T13:27:15.940 回答
1

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

我不熟悉Two Scoops of Django(我知道这本书,但我从未读过它),所以我不确定那个项目布局的全部内容。这肯定没有错,但对我来说似乎过于复杂,特别是对于一个完全初学者来说,因为它与布置 Django 项目的“规范”方式有些偏离。因此,如果您刚开始使用 Django,我真的建议您查看并完成 官方 Django 教程(该页面上的“第一步”部分)。

现在假设您当前的布局,对于 Heroku,您需要存储库根目录中的两个文件(在 .etc 目录所在的同一级别docsscriptsProcfilerequirements.txt. 假设你的 WSGI 入口点在sonata/sonata目录中(它应该被称为wsgi.py),Procfile 应该是这样的:

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

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

requirements.txt文件应至少具有:

Django>=1.7
gunicorn

在里面。该文件告诉 Heroku 使用 Pip(Python 的包管理器)安装哪些 Python 包。在这种情况下,最新版本的 Django 1.7 分支和Gunicorn。它还应该列出您的项目具有的其他 Python 依赖项。这个文件的存在也告诉 Heroku 你的应用程序是一个 Python 应用程序。您应该认真阅读Heroku 上的 Django 入门 指南,因为它涵盖了基础知识和进一步阅读的指南。我同意 Heroku 的文档对于初学者来说有点泛滥,但是如果您一次迈出一步并使用 Google 来解决您不清楚的事情,您应该相对较快地进行工作设置。

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

您在设置static()功能时也可能存在错误,应该是(如果您还想提供媒体文件):

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

同样,这只会在很短的时间内起作用,直到您的应用程序由于某种原因重新启动,此时您将拥有一个损坏的应用程序,因为所有静态文件都消失了。

另请注意,您不需要collectstatic在 Heroku 上手动运行。它将检测 Django 应用程序并在部署时自动运行它。即使没有,您也无法在推送存储库之前执行此操作。反之亦然,您首先推送然后collectstatic运行(您会看到它自动运行)。

在我看来,您真的对此束手无策,所以我的建议是退后一步。制作一个非常简单的香草 Django 应用程序(按照我提到的教程),并尝试将其部署到 Heroku,您应该遵循我链接的他们的 Django 部署基础知识。即使采用这种方法,也有可能的警告,但这些超出了这个问题的范围。一步一步(尽可能地往回走,直到你弄清楚),你会没事的。

于 2015-07-05T06:52:44.210 回答
1

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

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

对于 django 1.7,您应该使用cookiecutter 配方这是 1.7 版本的链接;默认存储库是 django 1.8。

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

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

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

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

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

请确保您遵循指南以确保您的项目设置正确。

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

我建议您改为遵循django 的 heroku 教程,它更简单,应该让您立即开始。

于 2015-07-05T07:18:43.547 回答