Місцеві налаштування Django


75

Я намагаюся використовувати local_setting у Django 1.2 , але це не працює для мене. На даний момент я просто додаю local_settings.py до свого проекту.

settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'banco1',                      # Or path to database file if using sqlite3.
        'USER': 'root',                      # Not used with sqlite3.
        'PASSWORD': '123',                  # Not used with sqlite3.
        'HOST': 'localhost',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
    }
}

local_settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'banco2',                      # Or path to database file if using sqlite3.
        'USER': 'root',                      # Not used with sqlite3.
        'PASSWORD': '123',                  # Not used with sqlite3.
        'HOST': 'localhost',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
    }
}

Проблема полягає в тому, що local_settings.py не замінює settings.py . Що не так?


2
На даний момент, коли я читаю це запитання, є три дуже різні і всі цікаві відповіді. Мене здивували коментарі щодо рішення Даніеля. Мені здається, що не існує єдиного рішення. Рішення Даніеля просте та ефективне. Рішення jano є якимось чином чистішим, таким чином, що має сенс лише у вже чистих умовах, за невелику ціну. Рішення Джона є якось більш важким, але цікавим у найбільш обмежених умовах. Загалом, просто виберіть найпростіші, що відповідають вашому випадку використання.
Стефан Гурішон

Відповіді:


138

Ви не можете просто додати local_settings.py, вам потрібно просто імпортувати його.

У самому кінці вашого settings.py додайте це:

try:
    from local_settings import *
except ImportError:
    pass

Блок try / exclude є, так що Python просто ігнорує випадок, коли ви насправді не визначили файл local_settings.


3
Так, grep -r "local_setting" djangoв значній мірі гарантує, що це не "нестандартна" поведінка
Юдзі 'Томіта' Томіта

8
Цей підхід означає, що у вас є неверсійний код, що працює в КОЖНОМУ місці. Це анти-шаблон.
pydanny

2
@pydanny яку альтернативу ти пропонуєш?
Бродней

1
@pydanny - у цій відповіді прямо сказано. local_settings.py (or more commonly prod_settings.py) is NOT in version control, and used in production by specifying --settings=prod_settings or similar.Отже, все ще здається, що неверсійний код працює в кожному місці. Хіба це не так?
Джозеф

1
Дуже погана ідея передавати виняток. Якщо у local_settings.py просто є синтаксична помилка, вона не буде імпортована, і ви можете запустити налаштування за замовчуванням із settings.py, чого ви не очікуєте. Наприклад, якщо settings.py підключається до виробничої бази даних. Краще завжди вимагати local_settings.py і видавати помилку, якщо її не знайти.
Метт

82

Я вважаю, що це найкраща практика:

  • local_settings імпорт з settings
  • local_settingsпереопределяет параметри , які стосуються місцевих умов, в зокрема DATABASES, SECRET_KEY, ALLOWED_HOSTSіDEBUG змінні
  • перейти до команд управління django прапор --settings=local_settings

Ви можете реалізувати local_settingsтак:

from settings import *

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'banco2',                      # Or path to database file if using sqlite3.
        'USER': 'root',                      # Not used with sqlite3.
        'PASSWORD': '123',                  # Not used with sqlite3.
        'HOST': 'localhost',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
    }
}

Кілька додаткових ключових моментів:

  • settings.py знаходиться у контролі версій, написаному таким чином, що він готовий до використання співавторами
  • local_settings.py(або частіше prod_settings.py) НЕ контролюється версіями, а використовується у виробництві шляхом вказівки --settings=prod_settingsабо подібного.

Якщо якомога менше торкатися файлу налаштувань запасів, це також полегшує оновлення вашої версії django. Коли ви оновлюєте Django до наступної версії, подивіться на різницю у фондовій settings.pyта вашій та вживайте необхідних дій залежно від того, що змінилося. Зміни значень за замовчуванням можуть бути важливими, і чим менше ви торкаєтесь оригінального settings.pyфайлу, тим легше буде розпізнати зміни вгору.


2
Мені подобається такий підхід. Якщо якомога менше торкатися порожнього файлу налаштувань, полегшується правильна настройка нових параметрів під час оновлення вашої версії django.
m000

3
Це має бути прийнятою відповіддю, прочитайте коментарі у прийнятій з причин, чому ця проблема є проблематичною ("неверсійний код, що працює в КОЖНОМУ розташуванні").
eggonlegs

6
Я не бачу, чим це відрізняється від прийнятої відповіді. Ці локальні_налаштування також не повинні бути версіями. Якщо це можливо, розробники можуть випадково змінити local_settings на виробничій машині.
GergelyPolonkai

Єдине, що мене турбує - це import *. stackoverflow.com/questions/2386714/why-is-import-bad
azmeuk

1
@azmeuk Контекст важливий. import *загалом погано, але цілком прийнятно, коли це єдиний імпорт у файлі. І це має бути імпорт вlocal_settings.py
janos

11

Оскільки тема регулярно виникає, дозвольте мені підсумувати, чому ви можете розглянути такий підхід:

  • німий файл налаштувань дуже швидко і легко змінюється; особливо у виробничому середовищі. Питон не потрібен: будь-який ідіот може заскочити і змінити пароль бази даних у файлі, в якому просто вказані імена та значення; особливо у порівнянні зі складним файлом налаштувань python, повним таємничих небезпечних імен BIGCAPS.

  • заявка settingsповинна бути повністю відокремлена від програми code. Ви можете поставити config.ini поза коренем сховища і більше ніколи не турбуватися про те, що репо витягує ваші налаштування, чи ваші особисті налаштування забруднюють репо, або про той розумний код у ваших налаштуваннях. .

Це не стосуватиметься невеликих проектів, але для великих проектів я дійшов висновку, що стратегія local_settings просто не вирізає її; з часом вкрадається достатньо програм програмування, що стає важко впоратися; насамперед у міру того, як налаштування стають похідними та / або взаємозалежними. Можуть бути хороші обґрунтування для того, щоб налаштування реагували відповідно до локальних налаштувань, що змушує імпорт local_settingsфайлу повзати до середини settings.py. Я вважаю, що все починає бути безладним, коли це трапляється.

Моє поточне рішення - використовувати configфайл, я називаю його "local.ini". Він містить лише ті значення, які насправді змінюються між розгорнутими екземплярами. Коду не існує: це лише значення та логічні значення:

[global]
domain = 127.0.0.1:8000
database_host = 127.0.0.1
database_name = test_database
debug = Yes
google_analytics_id = UA-DEV-1
payments = testing
use_cdn = No

Завдяки цьому я можу поводитися з settings.pyбудь-яким іншим фрагментом коду програми: налаштувати його, зареєструвати та розгорнути, не турбуючись про тестування будь-якого коду, який може ховатися в коді python local_settings. У моїй програмі settings.pyвідсутні умови перегонів, які виникають тоді, коли пізніші налаштування залежать від місцевих налаштувань, і я можу вмикати та вимикати функції, записуючи простий для слідування лінійний код. Більше не потрібно поспішно налаштовувати файл local_settings, коли я забув додати якесь нове значення, і більше немає, daves_local_settings.pyа bobs_local_settings.pyфайли пробираються до сховища.

from ConfigParser import RawConfigParser
parser = RawConfigParser()

APPLICATION_ROOT = path.abspath(path.dirname(__file__))
parser.readfp(open(path.join(APPLICATION_ROOT, 'local.ini')))

# simple variables
DATABASE_HOST = parser.get('global', 'database_host')
DATABASE_NAME = parser.get('global', 'database_name')

# interdependencies
from version import get_cdn_version
CDN = 'd99phdomw5k72k.cloudfront.net'
if parser.getboolean('global', 'use_cdn'):
    STATIC_URL = '/{}/static/{}/'.format(CDN, get_cdn_version())
else:
    STATIC_URL = '/static/'


# switches
payments = parser.get('global', 'payments')
if payments == 'testing':
    PAYMENT_GATEWAY_ENDPOINT = 'https://api.sandbox.gateway.com'
else:
    PAYMENT_GATEWAY_ENDPOINT = 'https://api.live.gateway.com'

Якщо ви стикаєтеся з BOFH , як це було в мене одного разу, він особливо схвилювався завдяки можливості вставити файл local.iniу /etcкаталог, /etc/ourapp.iniі, таким чином, зберігати сам каталог додатків як чистий експорт сховища. Звичайно, ви могли це зробити за допомогою local_settings.py, але останнє, що він хотів зробити, це возитися з кодом python. Простий конфігураційний файл, з яким він міг впоратися.


Що викликає у вас бажання використовувати інший синтаксис (і вимагати парсер) на відміну від звичайного файлу Python? Ось чому ми використовуємо простір імен.
Joost

Тому що a regular Python fileбуде виконано, тоді як конфігураційний файл - це дуже проста колекція німих налаштувань; немає спокуси змусити його робити «розумні» речі, і будь-який ідіот (наприклад, люди, які не є пітонами, або ви самі через 12 місяців) можуть придумати, як оновити основні налаштування, не боячись зламати все.
Джон Мі

Хоча синтаксис практично ідентичний. Я думаю, це питання переваг.
Джоост,

1
Як я вже сказав, виплата невеликих проектів невелика; але як тільки ваш settings.py перевищить 50 рядків, або якщо import local_settings.pyйого вже не буде в самому низу, ви побачите більше значення.
Джон Мі

Для мене найгіршим у цьому є те, що вам потрібно писати код для кожного окремого значення налаштувань. І якщо ви просто імпортуєте все у файлі, вам потрібно буде відкинути змінні з stings, якщо вони щось інше. Крім того, я люблю об’єднувати речі. У мене також є багато списків та словників.
kagronick

9

Я зберіг копію __local_settings.py:

  • local_settings.py в контролі версій ігнорується, але ні __local_settings.py
  • оновлення, README.mdщоб повідомити команду про те, як налаштувати: cp {__,}local_settings.py(які роблять копію для своїх local_settings)

В минулому

Раніше я імпортував ці налаштування.

# settings.py
DATABASE = {...}

try:
    from .local_settings import *
except ImportError:
    pass

зараз

Я просто імпортую самі налаштування з local_settings.py.

І за допомогою наступної команди: python manage.py runserver --settings=<proj>.local_settings.

# local_settings.py & __local_settings.py
from .settings import *

DATABASE = {...}

І оскільки я зазвичай не взаємодію manage.pyбезпосередньо, оскільки деякі параметри явно потрібні мені (наприклад, address:port). Тому я вклав усі ці накази у свою Makefile.

Наприклад, ось мій Makefile:

run:
    python manage.py runserver 0.0.0.0:8000 --settings=<proj>.local_settings

sh:
    python manage.py shell_plus --settings=<proj>.local_settings

dep:
    npm install
    pip install -r requirements.txt

Отже:

make dep
make sh 
make run

Висновок

За умови, що ви не використовуєте Makefileяк робочий процес, ви можете використовувати попередній метод, але якщо ви використовуєте makefile, то я вважаю, що краще бути більш чітким у вашому Makefile.


Makefile - це лише ідея та думка. Спробуйте скористатися попереднім підходом, можливо, взагалі буде кращим
Йо

6

Перед запуском сервера зробіть

export DJANGO_SETTINGS_MODULE=your_app_name.local_settings де ваше_апп_ім'я має бути замінено на ім'я вашого додатка. І не забудьте зробити

from settings import *

у вашому файлі local_settings.py


2
Вам слід опустити .py з рядка, (і не забудьте замінити
назву

4

Ще один підхід полягає у використанні python-dotenv змінних середовища та середовища для налаштування параметрів для різних середовищ.

Створіть .envфайл поряд з вашим settings.py:

# .env
SECRET_KEY=your-secret-key
DATABASE_PASSWORD=your-database-password

Додайте наступний код до свого settings.py:

# settings.py
from dotenv import load_dotenv
load_dotenv()

# OR, explicitly providing path to '.env'
from pathlib import Path  # python 3.4+
env_path = Path('.') / '.env'
load_dotenv(dotenv_path=env_path)

На даний момент проаналізовані ключі / значення з .envфайлу присутні як змінні середовища, і до них можна зручно отримати доступ за допомогою os.getenv():

# settings.py
import os
SECRET_KEY = os.getenv('SECRET_KEY')
DATABASE_PASSWORD = os.getenv('DATABASE_PASSWORD')   

1

Я знайшов подібне рішення. Це моя конфігурація для цього випадку:

settings.py:

DEBUG = False

try:
    from local_settings import *

except ImportError:
    pass

if DEBUG is False:
    ALLOWED_HOSTS = ['sth.com']
    DATABASES = {
        ....
    }

local_settings.py:

from settings import *
ALLOWED_HOSTS = ['*']
DEBUG = True
DATABASES = {
    ...
}

1

Додайте це в кінець файлу settings.py

try:
    from .local_settings import *
except ImportError:
    pass

І створіть файл local_settings.py з вашими новими налаштуваннями, наприклад

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'banco2',                      # Or path to database file if using sqlite3.
        'USER': 'root',                      # Not used with sqlite3.
        'PASSWORD': '123',                  # Not used with sqlite3.
        'HOST': 'localhost',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.