我试图分离出Django的密钥和数据库传递到环境variables,广泛的build议,所以我可以使用本地/生产服务器之间相同的代码库。
我遇到的问题是正确设置,然后读取运行Apache + mod_wsgi的生产服务器上的环境variables。
在我的用户configuration文件中设置的variables不可用,因为Apache不是以该用户身份运行的。 使用SetEnv在虚拟主机文件中设置的variables不可用,因为范围在某种程度上是不同的。
我已经阅读了一些答案的1,2 ,这导致这个博客的解决scheme。
我无法弄清楚如何将解决scheme应用到使用wsgi.py
文件的Django的当前版本,该文件如下所示:
import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") from django.core.wsgi import get_wsgi_application application = get_wsgi_application()
我怎样才能将这个博客解决scheme应用到wsgi.py文件,还是有一个更好的地方来存储Django可以获得的env-vars?
如果其他人对格雷厄姆的回答感到沮丧,那么这个解决方案实际上对原始问题有效。 我个人发现从Apache设置环境变量是非常有用和实用的,特别是因为我配置了自己的主机环境,可以做任何我想要的。
wsgi.py(在Django 1.5.4中测试)
from django.core.handlers.wsgi import WSGIHandler class WSGIEnvironment(WSGIHandler): def __call__(self, environ, start_response): os.environ['SETTINGS_CONFIG'] = environ['SETTINGS_CONFIG'] return super(WSGIEnvironment, self).__call__(environ, start_response) application = WSGIEnvironment()
值得注意的是,你失去了django.core.wsgi.get_wsgi_application
的未来WSGIHandler()
方法,它只能返回WSGIHandler()
。 如果WSGIHandler.__call__
方法已经更新,并且您也更新了Django,那么如果参数更改,则可能必须更新WSGIEnvironment
类。 我认为这是一个非常小的罚款来支付方便。
FWIW。 依赖细粒度配置设置的环境变量通常不是一个好主意。 这是因为并不是所有的WSGI托管环境或商业PaaS产品都支持这个概念。 使用环境变量进行细粒度设置也可以有效地将您锁定到特定的PaaS产品,您可以直接将特定命名的环境变量直接嵌入代码中,其中该环境变量的命名约定是特定于该托管服务的。 因此,虽然使用环境变量是由某些服务推动的,但要小心依赖于环境变量,因为这会降低WSGI应用程序的可移植性,并使其难以在部署机制之间移动。
这一切都说,你提到的博客文章通常不会帮助。 这是因为它使用在每个请求上设置进程环境变量的令人讨厌的技巧,根据每个请求使用Apache中的SetEnv设置WSGI环境设置。 如果环境变量的值根据URL上下文可能有所不同,这可能会导致多线程配置中的各种问题。 对于Django来说,这并没有什么帮助,因为在处理任何请求之前,通常会导入Django设置模块,这意味着在需要的时候环境变量将不可用。
这个部署配置的整个领域迫切需要一个更好的方式,但坦率地说,它主要是一个失败的原因,因为托管服务不会改变事物,以适应更好的WSGI部署策略。 他们已经完成了自己的工作,把自己的客户锁定在已经做好的方面,即使有更好的方式,也不会为自己创造工作,改变事情。
无论如何,“计算机科学中的所有问题都可以通过另一个间接的层面来解决”。 ( http://en.wikipedia.org/wiki/Indirection ),这就是你可以在这里做的。
没有你的应用程序查找环境变量。 让它导入一个特定于部署的Python配置模块,其中包含使用API获取配置设置的方法。 这个配置模块将根据部署机制实现不同的获取实际设置的方式。 在某些情况下,它可以从环境变量中获取值。 对于其他的如Apache / mod_wsgi,这些值可以在该配置模块中,或者从可以是ini,json或yaml格式的单独配置文件读取。 在提供API时,还可以将配置设置的名称映射到由不同PaaS产品使用的不同名称。
这个配置模块不需要是应用程序代码的一部分,但可以手动放置到目标系统的“/ etc /”子目录中。 您只需要设置Python模块搜索路径,以便您的应用程序可以看到它。 作为WSGI部署的更广泛的更好的标准的一部分,整个系统可以变得非常优雅,但正如我所说的,当现有的PaaS产品不太可能改变以使用这样的标准时,没有什么动力去创造这样的事情。
这是一个可替代的解决方案,与get_wsgi_application
一样是未来的get_wsgi_application
。 它甚至可以让你设置你的Django初始化中使用的环境变量。
# in wsgi.py KEYS_TO_LOAD = [ # A list of the keys you'd like to load from the WSGI environ # into os.environ ] def loading_app(wsgi_environ, start_response): global real_app import os for key in KEYS_TO_LOAD: try: os.environ[key] = wsgi_environ[key] except KeyError: # The WSGI environment doesn't have the key pass from django.core.wsgi import get_wsgi_application real_app = get_wsgi_application() return real_app(wsgi_environ, start_response) real_app = loading_app application = lambda env, start: real_app(env, start)
我不是100%清楚mod_wsgi
如何管理其进程,但我认为它不会经常重新加载WSGI应用程序。 如果是这样,初始化Django的性能损失只会在第一个请求中发生一次。
或者,如果您在初始化Django之前不需要设置环境变量,则可以使用以下命令:
# in wsgi.py KEYS_TO_LOAD = [ # A list of the keys you'd like to load from the WSGI environ # into os.environ ] from django.core.wsgi import get_wsgi_application django_app = get_wsgi_application() def loading_app(wsgi_environ, start_response): global real_app import os for key in KEYS_TO_LOAD: try: os.environ[key] = wsgi_environ[key] except KeyError: # The WSGI environment doesn't have the key pass real_app = django_app return real_app(wsgi_environ, start_response) real_app = loading_app application = lambda env, start: real_app(env, start)
对于Django 1.11:
Apache配置:
<VirtualHost *:80 > ... SetEnv VAR_NAME VAR_VALUE </VirtualHost>
wsgi.py:
import os import django from django.core.handlers.wsgi import WSGIHandler class WSGIEnvironment(WSGIHandler): def __call__(self, environ, start_response): os.environ["VAR_NAME"] = environ.get("VAR_NAME", "") return super(WSGIEnvironment, self).__call__(environ, start_response) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") django.setup(set_prefix=False) application = WSGIEnvironment()