TechLife devBlog

Django – personalizacja settings.py

Internet 28th Jun 2009

Jak każdy djangowic dobrze wie plik settings.py przechowuje wszelkie ustawienia aplikacji począwszy od nazwy strony a na listach middleware'u czy liście zainstalowanych aplikacji skończywszy. Ponieważ zawiera on wszelkie ustawienia, musi być inny dla developera a inny dla serwera produkcyjnego (np. z powodu tego, że developer ma DEBUG=True).

Pierwszą możliwością rozwiązania tego problemu jest ignorowanie pliku settings.py przez repozytorium kodu, tak aby różne zawartości tego pliku nie powodowały konfliktów. Wadą takiego rozwiązania jest potrzeba ręcznego edytowania tego pliku na serwerze kiedy np. dodajemy nową aplikację do INSTALLED_APPS. Moje repozytorium aktualizuje stronę na serwerze po każdym commicie, więc ręczna edycja settings.py jest męcząca.

Drugą możliwością jest stworzenie osobnych ustawień dla serwera i osobnych dla developera. Django umożliwia uruchomienie serwera z podaniem pliku ustawień jako parametr:

python manage.py runserver --settings=settings-developer.py

Wszystko wydaje się być ok. Jeżeli coś zmieniamy to dokonujemy zmiany w settings.py, settings-developer.py i po commitcie wszystko gra. Czasami jednak przy testowaniu różnych rzeczy w pliku developera można zapomnieć o settings.py i po commicie znowu strona jest rozwalona. Poza tym nie każdy developer korzysta z tych samych ustawień. Jeden używa Django Debug Toolbar inny ColorSQLMiddleware więc trzeba by zrobić tyle settings-xxx ilu developerów. A później przy zmianie czegoś globalnego edytować wszystkie...

Ostatecznym rozwiązaniem okazało się przeciążenie pliku settings.py przez ponowną deklarację zmiennych lub rozszerzenie już istniejących wartości. Jak to zrobić?

  1. Tworzymy plik, który będzie przetrzymywał nasze lokalne ustawienia, np. settings-local.py
  2. Na końcu pliku settings.py dodajemy kod:
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
execfile('%s/settings-local.py' % BASE_DIR)

Teraz w pliku settings-local.py możemy ponownie zadeklarować zmienne które mają zostać przeciążone lub rozszerzyć zadeklarowane już w settings.py listy. Np. kiedy jeden z developerów chce użyć Django Debug Toolbar dodaje do settings-local.py taki kod:

MIDDLEWARE_CLASSES += (
    'debug_toolbar.middleware.DebugToolbarMiddleware',
)

INSTALLED_APPS += (
    'debug_toolbar',
)

DEBUG_TOOLBAR_PANELS = (
    'debug_toolbar.panels.sql.SQLDebugPanel',
    'debug_toolbar.panels.headers.HeaderDebugPanel',
    'debug_toolbar.panels.cache.CacheDebugPanel',
    'debug_toolbar.panels.profiler.ProfilerDebugPanel',
    'debug_toolbar.panels.request_vars.RequestVarsDebugPanel',
    'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel',
    'debug_toolbar.panels.templates.TemplatesDebugPanel',
    # If you are using the profiler panel you don't need the timer
    # 'debug_toolbar.panels.timer.TimerDebugPanel',
)

Plik settings-local.py wywalamy z repozytorium i w ten sposób otrzymujemy elastyczny mechanizm współdzielenia wartości globalnych przy jednoczesnej personalizacji ustawień lokalnych.

Komentarze z jogger.pl

Dominik Szopa 30.06.2009 / 13:23

A nie wystarczy zamiast

execfile('%s/settings-local.py' % BASE_DIR)

Dać po prostu:

from moj_projekt.settings-local.py import *

Wtedy wszystkie ustawienia zdefiniowane wyżej zostaną nadpisane.

trójkąt 30.06.2009 / 13:30

Przy imporcie możesz jeszcze raz zadeklarować zmienną ale nie możesz dołożyć czegoś do już istniejącej. Bo przy konstrukcji typu:

MIDDLEWARE_CLASSES += (
'debug_toolbar.middleware.DebugToolbarMiddleware',
)

parser nie będzie wiedział co to za zmienna

NameError: name 'MIDDLEWARE_CLASSES' is not defined

Jaro 30.06.2009 / 14:25

Zamiast os.path.abspath(__file__) można użyć os.getcwd(). Zawsze trochę czytelniej.

pats 02.07.2009 / 16:08

kod
try: import socket hostname = socket.gethostname().replace('.','_') exec 'from settings_%s import *' % hostname
except ImportError, e: import sys sys.stderr.write('Unable to read settings_%s.py\n' % hostname) sys.exit(1)
kod

pats 02.07.2009 / 16:08

try: import socket hostname = socket.gethostname().replace('.','_') exec 'from settings_%s import *' % hostname except ImportError, e: import sys sys.stderr.write('Unable to read settings_%s.py\n' % hostname) sys.exit(1)



Komentarze