16

正如广泛建议的那样,我正在尝试将 Django 的密钥和 DB 传递到环境变量中,这样我就可以在本地/生产服务器之间使用相同的代码库。

我遇到的问题是正确设置然后读取运行 Apache + mod_wsgi 的生产服务器上的环境变量。

我的用户配置文件中设置的变量不可用,因为 Apache 不是以该用户身份运行的。虚拟主机文件中设置的SetEnv变量不可用,因为范围有所不同。

我已经阅读了一些1 , 2的 SO 答案,这导致了这个博客的解决方案。

我不知道如何将解决方案应用于使用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()

如何将该博客解决方案应用于 wsgi.py 文件,或者是否有更好的地方来存储 Django 可以获取的环境变量?

4

5 回答 5

23

如果其他人对格雷厄姆的回答感到沮丧,这里有一个实际上适用于原始问题的解决方案。我个人发现从 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()。如果该方法被更新并且您也更新了 Django,那么如果参数发生更改WSGIHandler.__call__,您可能必须更新类。WSGIEnvironment我认为这是为了方便而支付的非常小的罚款。

于 2014-01-14T21:12:41.223 回答
10

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 产品极不可能改变以使用这样的标准时,几乎没有动力去努力创建这样的东西.

于 2013-11-04T00:19:38.397 回答
5

这是另一种解决方案,它与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)
于 2015-09-05T18:53:48.893 回答
2

对于 Django 1.11:

阿帕奇配置:

<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()
于 2017-04-30T22:06:56.110 回答
0

在将生产和开发代码都跟踪到版本控制中时,我遇到了同样的问题。我使用不同的 wsgi 脚本解决了这个问题,一个用于生产服务器,一个用于开发服务器。如此处所述,创建两个不同的设置文件并在 wsgi 脚本中引用它们。例如以下是 wsgi_production.py 文件

...
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings.production")
...

并在 wsgi_development.py 文件中

...
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings.development")
...
于 2018-05-16T10:58:59.877 回答