Доступ к переменной Apache SetEnv из файла Django wsgi.py

Я пытаюсь выделить секретный ключ Django и DB перейти к переменным окружения, как это широко предлагается, поэтому я могу использовать идентичные базы кода между локальными/производственными серверами.

Проблема, с которой я столкнулся, корректно настраивается, а затем читает экологические вары на рабочем сервере Apache + mod_wsgi.

Vars, установленные в моем профиле пользователя, недоступны, поскольку Apache не запускается в качестве этого пользователя. Vars, установленные в файле виртуальных хостов с помощью SetEnv, недоступны, поскольку область действия несколько отличается.

Я прочитал пару 1, 2 ответов SO, что приводит к этот блог с решением.

Я не могу понять, как применить решение к текущим версиям Django, которые используют файл wsgi.py, который выглядит так:

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

from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

Как я могу применить это решение для блога к файлу wsgi.py, или есть лучшее место для хранения env-vars, где Django может получить от них?

Ответ 1

Если кто-то разочарован ответом Грэма, вот решение, которое действительно работает для оригинального вопроса. Я лично считаю, что установка переменных окружающей среды из 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, если изменения будут изменены. Я считаю это очень небольшим штрафом для оплаты удобства.

Ответ 2

FWIW. Опираясь на переменные окружения для мелкозернистых настроек конфигурации, в целом не очень хорошая идея. Это связано с тем, что не все среды размещения WSGI или коммерческие предложения PaaS поддерживают эту концепцию. Использование переменных среды для мелкозернистых настроек также может эффективно блокировать вас в конкретном предложении PaaS, где вы непосредственно встроили поиск специально названной переменной среды непосредственно в свой код, где соглашение об именах этой переменной окружения относится к этой службе хостинга. Поэтому, хотя использование переменных среды подталкивается определенными службами, всегда будьте осторожны, завися от переменных среды, поскольку это уменьшит переносимость вашего приложения WSGI и затруднит перемещение между механизмами развертывания.

Что все сказано, сообщение в блоге, которое вы упомянули, обычно не поможет. Это связано с тем, что он использует неприятный трюк для настройки переменных среды процесса для каждого запроса на основе параметров WSGI для каждого запроса, заданных с помощью SetEnv в Apache. Это может вызвать различные проблемы в многопоточной конфигурации, если значения переменных среды могут отличаться в зависимости от контекста URL. Для случая Django это не полезно, потому что модуль настроек Django обычно импортируется до того, как будут обработаны какие-либо запросы, а это означает, что переменные среды не будут доступны в требуемое время.

Вся эта область конфигурации развертывания остро нуждается в лучшем способе делать что-то, но, откровенно говоря, это в основном потерянная причина, поскольку услуги хостинга не изменят ситуацию, чтобы обеспечить лучшую стратегию развертывания WSGI. Они сделали свою работу, заставили своих клиентов заблокировать то, как они уже сделали это, и не собираются создавать работу для себя и изменять вещи, даже если существует лучший способ.

Во всяком случае, "все проблемы в информатике могут быть решены другим уровнем косвенности". (http://en.wikipedia.org/wiki/Indirection), и вот что вы можете здесь сделать.

У вас нет переменных окружения для поиска приложений. Импортируйте импортируемый конфигурационный модуль Python для развертывания, который содержит средства для использования API для получения настроек конфигурации. Этот модуль конфигурации реализует различные способы получения фактических настроек на основе механизма развертывания. В некоторых случаях он может захватывать значения из переменных среды. Для других, таких как Apache/mod_wsgi, значения могут быть в этом модуле конфигурации или считаны из отдельного файла конфигурации, который может быть форматом ini, json или yaml. При предоставлении API он также может сопоставлять имена параметров конфигурации с разными именами, используемыми различными предложениями PaaS.

Этот модуль конфигурации не обязательно должен быть частью вашего кода приложения, но его можно вручную помещать в подкаталог "/etc/" в целевой системе. Вам просто нужно установить путь поиска модуля Python, чтобы ваше приложение могло его увидеть. Вся система может быть сделана довольно элегантной как часть более широкого стандарта для развертывания WSGI, но, как я уже сказал, мало стимулов для тяжелой работы по созданию такой вещи, когда существующие предложения PaaS вряд ли изменятся, чтобы использовать такой стандарт.

Ответ 3

Здесь альтернативное решение, которое будет в будущем доказано как 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)

Ответ 4

Для 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()