Pull to refresh

Comments 38

Все сравнительно стандартно, в работе использую почти такой же. Но для начинающих, возможно, пригодится — сразу будут делать правильно.
UFO just landed and posted this here
Настройки БД должны быть в локальном конфиге еще хотя бы и потому, что пароль от базы нехорошо держать в VCS.
Да и базы (dev/production) должны быть разными. А какие «инциденты» могут быть, не понимаю?
UFO just landed and posted this here
вы правы, статья — перевод и автор любит разные БД. Сами используем одинаковые SQL.
за django-command-extensions отдельное спасибо, буду копать ))
Почему не написать «DEBUG = True» в локальном конфиге dev-сервера и не хардкодить имя домена?

Кстати, как бы попроще включать режим отладки для определенных ip?
Знаю, что есть решения через middleware, но они мне показались странными…
Подому как автор подключает локальный конфиг в конце. Мне кажется подключение локального конфига в начале — практичнее, так как позволяет в основном конфиге использовать разичные настройки из локального.
Ах… Ну я тоже за подключение локального конфига в начале. И в try не оборачивать.
Разве практичнее? Ведь в этом случае глобальные настройки будут перекрывать глобальные. По-моему, должно быть с точностью, да наоборот: в глобальном конфиге только то, что не зависит от платформы, а в локальном уже делать переопределения.
Ведь в этом случае глобальные настройки будут перекрывать глобальные локальные

Предыдущий комментарий надо читать вот так
А зачем вы одни и те же настройки пишите и как локальные, и как глобальные? Для того, чтобы локально можно было переопределить любую глобальную настройку? В тех случаях, с которыми я сталкивался — всюду можно было определить, какие настройки будут общими для всех, а какие — будут заданы локально.

Вместо того, чтобы оставлять в общем конфиге невразумительное
DATABASE_ENGINE = ''
DATABASE_NAME = ''
DATABASE_USER = ''
DATABASE_PASSWORD = ''
DATABASE_HOST = ''
DATABASE_PORT = ''
не лучше ли написать что-то вроде
import env

DATABASE_ENGINE = 'django.db.backends.postgresql_psycopg2'
DATABASE_NAME = env.DATABASE_NAME
DATABASE_USER = env.DATABASE_USER
DATABASE_PASSWORD = env.DATABASE_PASSWORD
DATABASE_HOST = env.DATABASE_HOST
DATABASE_PORT = env.DATABASE_PORT
(здесь evn — импортированный вначале файл с конфигурацией окружения). В такой форме сразу видно, что даная опция вовсе не оставлена пустой, а задана локально.
А зачем вы одни и те же настройки пишите и как локальные, и как глобальные? Для того, чтобы локально можно было переопределить любую глобальную настройку?

Почти. Для того, чтобы в случае надобности можно было переопределить любую глобальную настройку. А когда надобности нет, не писать ни одной лишней строчки :-)

Попробую проиллюстрировать на примере. Я обычно прописываю в settings что-то такое:

DEBUG=False
TEMPLATE_LOADERS = (
    ('django.template.loaders.cached.Loader',
        (
            'django.template.loaders.filesystem.Loader',
            'django.template.loaders.app_directories.Loader',
        )
    ),
)
# ...etc

То есть по умолчанию отладочный режим выключен, а кеширование шаблонов включено. Для боевого сервера это годится, для девелоперской машины — не очень. Поэтому в local_settings девелоперского окружения я просто переопределяю значения так, как мне нужно:

DEBUG=True
TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.Loader',
    'django.template.loaders.app_directories.Loader',
)
В локальных настройках продакшн-сервера я эти атрибуты не переопределяю.

не лучше ли написать что-то вроде...

По-моему, не просто не лучше, а намного хуже. Хотя бы потому что эти атрибуты встречаются во всех окружениях: и в глобальном (хорошо хоть, что их менять не надо), и в каждом из локальных. Короче, не мой выбор :-)
Проблемы с вашим вариантом:
1. По основному конфигу не очевидно, что используется как есть, что будет обязательно переопределено, а что может быть переопределено.
2. В основном конфиге нет возможность делать зависимости от настроек, заданных в локальном конфиге.
3. Зная лишь глобальный конфиг нет возможности определить, как сформировать локальный конфиг. Да и вообще определить, что там может находиться.

Да, ваш вариант гибче, но он в целом менее читабелен (что противоречит правилу «Readability counts» :) и боюсь он быстро становится трудноподдерживаемым. «Короче, не мой выбор» :D
Всё, что вы описали — не проблемы, а решения, к которым долго и сознательно шли :)

1. По основному конфигу не очевидно, что используется как есть, что будет обязательно переопределено, а что может быть переопределено.
У нас действует соглашение, по которому глобальный конфиг является полностью рабочим. Единственное исключение — атрибут DATABASES, который обязательно всё-таки указать вручную.

В репозиторий мы помещаем шаблон локального конфига — settings_local.py.template, в котором указан шаблон обязательного атрибута DATABASES. В том же файле содержатся дополнительные атрибуты, которые желательно (но не обязательно) изменить для лучшего функционирования рабочей копии.

2. В основном конфиге нет возможность делать зависимости от настроек, заданных в локальном конфиге.
Это не проблема, а умышленное поведение. Я почему всюду этот settings и обзываю глобальным: он ни разу не должен зависеть от окружения и везде одинаков. И я, если честно, не могу придумать адекватного случая, когда такая зависимость нужна.

Поможете? :-)

3. Зная лишь глобальный конфиг нет возможности определить, как сформировать локальный конфиг. Да и вообще определить, что там может находиться.
Чем-то этот пункт напоминает п.1. Поэтому ответ тот же: настройки, которые желательно изменить, перечисляются в local_settings.py.template, на основе которого уже и лепится local_settings.

Или я его неправильно понял?
TEMPLATE_DIRS = ()
for root, dirs, files in os.walk(PROJECT_PATH)
if 'templates' in dirs: TEMPLATE_DIRS += (os.path.join(root, 'templates'),)


При такой схеме сложно будет объяснить вашей любимой IDE, где именно искать шаблоны.
Я делаю так:

TEMPLATE_DIRS = (
os.path.abspath(PROJECT_DIR),
os.path.abspath(PROJECT_DIR) + '/templates', # тут общие шаблоны -- base, 404, 500...
)

и пишу имена шаблонов более развернуто:

return render_to_response('geo/templates/index.html', content, RequestContext(request))
return render_to_response('geo/templates/index.html', content, RequestContext(request))
Посмотрите django-annoying. Авось пригодится.
посмотрите direct_to_template(geo/index.html', content) — и со стандартной джангой можно писать проще
а зачем такая жуть: 'geo/templates/index.html'? Почему не просто 'geo/index.html'? Папку geo можно создать в PROJECT_ROOT/templates или в PROJECT_ROOT/geo/templates/
Я бы хотел, чтобы шаблоны приложения были внутри его директории. Бывает, что приложение достаточно независимо может работать.

или в PROJECT_ROOT/geo/templates/
Это не понял. У меня же так и есть? Или предлагается создать внутри "/geo/templates/" еще папку geo только для того, чтобы сократить путь с 'geo/templates/index.html' до 'geo/index.html'? А IDE как поймет, где эти шаблоны искать?
Обратите внимание на эту штуку:

TEMPLATE_LOADERS = (
    #...
    'django.template.loaders.app_directories.load_template_source',
)

она делает ровно то, что Вы хотите. Строго говоря, чтобы шаблоны из директории приложения подхватывались, TEMPLATE_DIRS можно и не заполнять.
Да, в geo/templates/ делаем папку geo. Это полезный паттерн, и так делает большинство сторонних приложений. Чем он полезен:

а) папку с шаблонами можно безболезненно перемещать между geo/templates и PROJECT_ROOT/templates
б) в одном приложении можно задать несколько «неймспейсов» для шаблонов
в) не требуется никаких хаков (вроде вашего) для того, чтобы подключать шаблоны
г) шаблоны гарантированно (ну почти, с точностью до названия приложения) не «мусорят» и не перекрывают шаблоны других приложений. У Вас, если что, перекрывают, и это может привести (и когда-нибудь обязательно приведет) к странным багам.

Про IDE вообще не понял, если честно. Зачем IDE «искать шаблоны», что под этим подразумевается?
Наверное, каждый месяц кто-нибудь у себя в блоге напишет о том, как настраивать settings.py) Думаю, число статей с советами за несколько сотен должно перевалить было уже давно.

for root, dirs, files in os.walk(PROJECT_PATH)
    if 'templates' in dirs: TEMPLATE_DIRS += (os.path.join(root, 'templates'),)


А это зачем? Можно просто добавть os.path.join(PROJECT_ROOT, 'templates') в TEMPLATE_DIRS. Шаблоны в папках templates у приложений 'django.template.loaders.app_directories.Loader' ведь сам находит.
Шаблоны приложений храню в app_dir/templates/_app_name_/
Все шаблоны разных приложений собираю (копирую) в одном templates проекта.
Если обновляются шаблоны стороннего приложения, редко когда удается туда не полезть своими грязными ручонками.
С таким подходом, код выше не нужен.
Кошмар, код не оформлен, что критично для Python.
После такого вообще пропадает желание читать:

if socket.gethostname() == 'your.domain.com':
DEBUG = False
else:
DEBUG = True
Комментарии — зло:
# Set DEBUG = True if on the production server

Вот за это я и ненавижу комментарии.
'your.domain.com' должна быть константой с именем PRODUCTION_SERVER

И весь кусок должен быть написан так:

DEBUG = socket.gethostname() == PRODUCTION_SERVER
DEBUG = socket.gethostname() != PRODUCTION_SERVER
Точняк.
Меня все еще комментарий сбивает с толку.
Это смотря где эти комментарии :)
Вот, если бы не ваш комментарий к статье, я бы не узнал удобую фичу. :)
Спасибо за комментарий.
Большое спасибо за статью!
Некоторые хитрости знал, но остальные возьму на вооружение.

Пишите еще, делитесь практическим опытом.

Хотелось бы про использование Django и MongoDB почитать.

Может кто-нибудь из знающих людей здесь напишет?!
try:
    from local_settings import *
except ImportError:
    pass


несколько раз натыкался, когда в local_settings опечатка, или ошибка, продакшн сервер запускается с настройками по умолчанию. ИМХО, лучше как-то так сделать:

if os.path.isfile(os.path.join(PROJECT_PATH, "local_settings.py")):
    from local_settings import *


в этом случае при ошибках в local_settings, вывалится исключение. И как по мне — удобнее в local_settings перекрывать настройки по умолчанию — подключаю в конце settings.py
А как по мне, лучше джанге вообще не позволять запускаться без local_settings:

try:
    from local_settings import *
except Exception, e:
    import os, warnings
    warnings.warn("Unable import local settings [%s]: %s" % (type(e),  e))
    sys.exit(1)

Маловероятно ведь, что локальные настройки не понадобятся в принципе, согласитесь? Окружения — то, в котором ведётся разработка и боевое — чаще всего существенно различаются. С другой стороны, если в настройках есть какие-то проблемы, то мы о них узнаем ещё на этапе запуска сервера и вовремя всё исправим.
По поводу локальных настроек — мне лично неудобно, что их приходится хранить локально — бывает систему переустановлю или еще что.

Я просто использую socket.gethostname() для того чтобы определить на какой машине запущен проект и, в зависимости от этого, разные настройки применяю :)
На мой взгляд гораздо проще и удобней пути в settings.py проставлять следующим образом:
import os

def rel(*x):
    return os.path.join(os.path.abspath(os.path.dirname(__file__)), *x)
    
#указываем путь так:

MEDIA_ROOT = rel('media')
Всё хорошо, только вот не понятно, зачем таким образом странным шаблоны подключать.
Только вот теперь константы
DATABASE_ENGINE = ''
DATABASE_NAME = ''
DATABASE_USER = ''
DATABASE_PASSWORD = ''
DATABASE_HOST = ''
DATABASE_PORT = ''
вроде как deprecated.

Лучше использовать DATABASES = {
'engine':…

}.
Я все настройки по умолчанию помещаю в файл settings_default.py, а в settings.py у меня следующее:

from settings_default import *

DATABASES['default']['NAME'] = os.path.abspath(os.path.join(PROJECT_PATH, '../database.db'))

Также у меня есть файл settings_production.py который выглядит похожим образом и при деплое переименовывается в settings.py. Ещё можно добавить файл settings_template.py со списком типичных параметров которые необходимо задавать индивидуально для каждой машины и использовать его в качестве основы для settings_production_xx.py

Это позволяет не просто переопределять значения переменных, но и модифицировать их. Ещё не забывайте что Джанго по какой-то причине импортирует файл settings.py дважды.
Sign up to leave a comment.

Articles