Соглашение о настройках Django по умолчанию для подключаемых приложений? - PullRequest
29 голосов
/ 08 декабря 2011

Что такое djangonautic способ обработки настроек по умолчанию в приложении, если он не определен в settings.py?

В настоящее время я поместил файл default_settings в приложение и рассмотрел несколько вариантов. Я склоняюсь к первому варианту, но могут быть подводные камни, о которых я не знаю, используя globals()

Я в основном видел, как приложения делают FOO = getattr(settings, 'FOO', False) в верхней части файла, который использует настройку, но я думаю, что при таком подходе возникают проблемы с читаемостью / повторением, если значения / имена длинные.


1: Поместить настройки в функцию и выполнить итерации по локальным / глобальным наборам

def setup_defaults():
    FOO = 'bar'
    for key, value in locals().items():
        globals()[key] = getattr(settings, key, value)

setup_defaults()

Плюсы:

  • Нужно только один раз написать имя переменной, чтобы получить настройки по умолчанию с тем же именем из настроек django.

Минусы:

  • Не используется для использования globals () и не знает о каких-либо последствиях

2: запись getattr(settings, 'MY_SETTING', default_settings.MY_SETTING) каждый звонок

Плюсы: - Очень ясно.

Минусы: - повторяющиеся


3: Всегда определять настройки как FOO = getattr(settings, 'FOO', '...setting here...')

Плюсы: - Значения по умолчанию всегда отменяются

Минусы:

  • Повторяющееся (необходимо определить переменную дважды - один раз в строковой форме, один раз в вар)
  • Настройки не так удобочитаемы, так как теперь это третий аргумент

4: Создать служебную функцию для get_or_default(setting)

Плюсы:

  • Простой
  • Не нужно повторять строковое представление настройки

Минусы:

  • Должен назвать это

5: создать класс настроек

class Settings(object):
    FOO = 'bar'

    def __init__(self):
         # filter out the startswith('__') of 
         # self.__dict__.items() / compare to django.conf.settings?

my_settings = Settings()

Минусы:

  • Невозможно сделать из foo.bar.my_settings импорт FOO (на самом деле, это ужасный провал!)

Мне бы очень хотелось услышать отзывы.

Ответы [ 5 ]

41 голосов
/ 08 декабря 2011

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

from django.conf import settings
FOO = getattr(settings, 'FOO', "default_value")

В вашем приложении вы можете импортировать их из модуля settings вашего приложения:

from myapp.settings import *

def print_foo():
    print FOO

Но я думаю, что все согласны с тем, что в Django отсутствует лучшая общая архитектура для этого! Если вы ищете более изощренный способ справиться с этим, есть некоторые сторонние приложения для этого, такие как django-appconf , но вы решаете, хотите ли вы добавить еще одну зависимость для своего приложения или нет !

5 голосов
/ 07 ноября 2017

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

Для меня все настройки принадлежат django.conf.settings и только там.Вы не должны ни читать их откуда-либо, ни копировать их для последующего использования (так как они могут измениться).Вы должны установить их один раз и не беспокоиться о настройках по умолчанию позже.

Я понимаю побуждение отбрасывать префикс приложения при внутреннем использовании настроек приложения, но это тоже ИМХО плохая идея.В случае проблем поиск SOME_APP_FOO не даст результатов, так как он используется так же, как и FOO для внутренних целей.Смущает правильно?И за что, мало букв?Помните , что явное лучше ?

ИМХО лучший способ - просто установить эти значения по умолчанию в собственных настройках Django, и почему бы не использовать уже существующие трубопроводы?Никакие перехваты импорта или перехвата модулей models.py не импортируются всегда для инициализации некоторых дополнительных и сложных мета-классов.

Почему бы не использовать AppConfig.ready для установки значений по умолчанию?

class FooBarConfig(AppConfig):
    name = 'foo_bar'

    def ready(self):
        from django.conf import settings
        settings = settings._wrapped.__dict__
        settings.setdefault('FOO_BAR_SETTING', 'whatever')

Или еще лучше определить их простым и понятным способом в отдельном модуле и импортировать их как (или почти как) Класс настроек делает это:

class FooBarConfig(AppConfig):
    name = 'foo_bar'

    def ready(self):
        from . import app_settings as defaults
        from django.conf import settings
        for name in dir(defaults):
            if name.isupper() and not hasattr(settings, name):
                setattr(settings, name, getattr(defaults, name))

Я неКонечно, использование __dict__ - лучшее решение, но вы поймете, что вы всегда можете использовать комбо hasattr / setattr, чтобы получить эффект.

Таким образом, настройки вашего приложения:

  1. открыт для других - если они должны полагаться на них в некоторых редких случаях, если, конечно, приложения настроены для того, чтобы полагаться друг на друга
  2. читается нормально, как любой другой параметр
  3. красиво объявлено в их собственном модуле
  4. достаточно ленивый
  5. контролирует, как они установлены в django.conf.settings - вы можете реализовать некоторое преобразование имен, если хотите

PS.Есть предупреждение о том, что не нужно изменять настройки во время выполнения , но оно не объясняет почему.Поэтому я думаю, что это один раз, во время инициализации может быть разумным исключением;)

PS2.Не называйте отдельный модуль просто settings, так как это может привести к путанице при импорте settings из django.conf.

4 голосов
/ 20 декабря 2013

Как насчет этого?

В myapp / settings.py:

from django.conf import settings

FOO = 'bar'
BAR = 'baz'

_g = globals()
for key, value in _g.items():
    _g[key] = getattr(settings, key, value)

В myapp / other.py:

import myapp.settings

print myapp.settings.FOO

Учитывая этот ответ от ncoghlan, я чувствую себя нормально, используя globals () таким образом.

1 голос
/ 16 декабря 2016

В ответ на комментарий Фила Гайфорда , раскрывающий проблему настроек, не перезаписанных в тестах (поскольку они уже импортированы в модули), я определил класс AppSettings в __init__.py с помощью:

  • метод __init__ для инициализации каждой настройки на None
  • load метод для загрузки всех настроек из геттеров
  • статические геттеры для каждой настройки

Затем в коде:

from . import AppSettings

def in_some_function():
    some_setting = AppSettings.get_some_setting()

Или, если вы хотите загрузить их все за один раз (но переопределенные настройки в тестах не будут работать для затронутого модуля):

from . import AppSettings

app_settings = AppSettings()
app_settings.load()

def in_some_function():
   print(app_settings.some_setting)

Затем вы можете использовать декоратор override_settings в своих тестах, и при этом у вас останется СУХОЙ и понятный способ использования настроек приложения за счет большего количества инструкций, выполняемых каждый раз, когда вы хотите получить настройку (только для испытаний ...).

0 голосов
/ 08 декабря 2011

Номер 3 самый лучший, потому что он самый простой. И очень последовательный взгляд.

Номер 1: это легко не заметить. Если я открою ваш код и не буду переходить к нижней части, я пропущу его и подумаю, что настройки нельзя переопределить в моем собственном модуле.

Номер 2: не только повторяющийся, его сложнее читать, потому что он слишком длинный, также значения по умолчанию будут определены несколько раз и разбросаны по всему вашему коду.

Номер 4: непоследовательные, повторяющиеся вызовы.

Номер 5: Не соответствует, мы ожидаем, что настройки будут определены в модуле, а не в классе. По крайней мере, я ожидаю, что меня определят как модуль, потому что я видел много приложений, использующих метод 3, и я использую его сам, так что я могу быть предвзятым.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...