从Django wsgi.py文件访问Apache SetEnv变量

时间:2013-11-03 15:55:30

标签: django apache ubuntu

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

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

我的用户个人资料中设置的Vars不可用,因为Apache不是以该用户身份运行的。使用SetEnv在虚拟主机文件中设置的变量不可用,因为范围在某种程度上不同。

我已经阅读了几个12个SO答案,这会导致this blog找到解决方案。

我无法弄清楚如何将解决方案应用于使用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可以获取的env-vars?

5 个答案:

答案 0 :(得分: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()。如果WSGIHandler.__call__方法已更新并且您也更新了Django,则可能必须更新WSGIEnvironment类,如果参数更改。为方便起见,我认为这是一个非常小的惩罚。

答案 1 :(得分: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产品极不可能改变使用这样的标准时,很少有动力去创造这样的事情。

答案 2 :(得分: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)

答案 3 :(得分:1)

对于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()

答案 4 :(得分:0)

我遇到了分离生产和开发代码的同样问题,同时将它们跟踪到版本控制中。我使用不同的wsgi脚本解决了这个问题,一个用于生产服务器,一个用于开发服务器。创建两个不同的设置文件here. 并在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")
...