Ile razy mieliście sytuację, w której pisaliście aplikację, wszystko odpowiednio konfigurowaliście i po jakimś czasie próbując uruchomić ją lokalnie występowały dziwne błędy? Ja miałem tak za każdym razem jak stawiałem nową aplikację. Byłem bardzo leniwy i nie chciało mi się tego poprawiać. Nawet przy budowaniu tego bloga źle skonfigurowałem pliki statyczne i dostęp do bazy. Z każdą poprawką musiałem nadpisywać sobie ustawienia. Aż w końcu doszło do sytuacji w której zacomitowałem błędną bazę i wrzuciłem to na produkcję. Takie rzeczy nie powinny mieć miejsca. W końcu znalazłem bardzo fajny i skuteczny sposób, mianowicie local_settings .
Import lokalnych ustawień
Żeby użyć lokalnych ustawień wystarczy na samym końcu pliku settings.py dodać odpowiedni import.
try:
from local_settings import *
except ImportError:
# No local settings was found, skipping.
pass
Wiem, że ten zapis nie jest zbyt elegancki i sam w jednym z wpisów tłumaczyłem, żeby tak nie robić, ale w tym przypadku jest to uzasadnione. Chodzi bowiem o to, że takie nadpisywane ustawienia mają wiele zastosowań. Możemy podejść do tego z innej strony i wymusić dodanie tego pliku do projektu. Wystarczy wyrzucić odpowiedni wyjątek
try:
from local_settings import *
except ImportError:
raise Exception("A local_settings.py file is required")
Tylko jest tu jedna bardzo ważna uwaga, którą ponownie potwórzę. Plik ten nie może być przechowywany w repo! Także pierwszą i kluczową rzeczą jest dodanie go do gitignore . I co to nam właściwie daje? A no dość spory komfort, bo z jednej strony wszyscy, którzy będą chcieli pracować nad naszym kodem będą mieli niski próg wejścia, a z drugiej strony na serwerze produkcyjnym wszystkie hasła będą bezpieczne.
Debug i Django Debug Toolbar
Jak wspomniałem wcześniej ustawienia lokalne mają wiele zastosowań. Jednym z nich jest nadpisywanie ustawień. Podczas błędnego działania aplikacji powinniśmy wyświetlać użytkownikom odpowiednią zaślepkę. Nie chcemy bowiem, żeby widzieli pełną ścieżkę błędu lub informacje z djdt . Obie opcje są bardzo proste do ustawienia i można je włączyć lub wyłączyć poprzez zmianę odpowiedniej flagi w ustawieniach. Ciągłe przestawianie tego jak chcemy zbadać jakiś błąd jest jednak uciążliwe. I tu lokalne ustawienia przychodzą nam z pomocą. Wystarczy, że w głównych ustawieniach będziemy mieli
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
Lokalnie natomiast nadpiszemy to. W efekcie plik local_settings.py będzie wyglądał następująco
DEBUG = True
Tym prostym zabiegiem jesteśmy bezpieczni, bo na produkcji nikt nie zobaczy informacji, które powinien znać tylko deweloper, a z drugiej strony lokalnie nie będziemy musieli nadpisywać tego przy każdej potrzebie. Proste, a bardzo pomocne.
Backend bazy danych
Spójrzmy teraz na to z innej strony. Nasz kod jest na repozytorium. Jeśli jest to kod OpenSource, to w pliku settings.py nie powinnismy trzymać żadnych haseł, zwłaszcza do bazy danych. Z drugiej strony, warto gdybyśmy lokalnie mieli podpiętą inną bazę danych, na przykład do testów. To również jest bardzo proste. Wystarczy, że w głównych ustawieniach będziemy mieli domyślną konfigurację bazy
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
Natomiast teraz zamieścimy plik local_settings.py na serwerze produkcyjnym już z normalną konfiguracją bazy
DATABASES = {
'default': {
'ENGINE': 'engine',
'NAME': 'name',
'USER': 'user',
'PASSWORD': 'password',
'HOST': 'host',
}
}
Konfiguracja skrzynki pocztowej
Jeśli kiedykolwiek wysyłaliście wiadomości do użytkowników mailem ze swojej aplikacji to wiecie, że wymaga to podania danych do skrzynki pocztowej w ustawieniach. No i tu rodzi się problem, bo nikt nie chce żeby dane do jego skrzynki były ogólnodostępne. Jak widać jest to dokładnie ten sam problem co z bazą danych. Wystarczy, że cała konfiguracja skrzynki będzie w pliku local_settings.py na zdalnym serwerze
EMAIL_HOST = 'host'
EMAIL_PORT = 587
EMAIL_HOST_USER = 'user'
EMAIL_HOST_PASSWORD = 'password'
EMAIL_USE_TLS = True
Sentry
Przy większych aplikacjach monitorowanie jest niezbędne. Jednym z popularniejszych narzędzi, które to ułatwia jest sentry. Konfiguracja jest bardzo prosta i odbywa się w kilku krokach. Można ją znaleźć pod tym adresem . Do czego jednak zmierzam? Wyobraźmy sobie, że mamy na przykład 3 środowiska. Jedno lokalne, na którym pracujemy, drugie testowe na zdalnym serwerze i trzecie produkcyjne. Wiadomo, że na tym testowym dużego ruchu nie będzie, a przynajmniej nie powinno. Również informacje o błędach średnio nas tam interesują. Dlatego warto dodać konfigurację sentry tylko w local_settings na serwerze produkcyjnym
RAVEN_CONFIG = {
'dsn': 'https://<key>:<secret>@sentry.io/<project>',
# If you are using git, you can also automatically configure the
# release based on the git info.
'release': raven.fetch_git_sha(os.path.abspath(os.pardir)),
}
W ten sposób będziemy zbierać informację o błędach tylko z tego miejsca, które nas interesuje.
Middleware
Czasem korzystamy z gotowych middleware, które udostępniają ciekawe funkcje. Mogą to być nasze własne kawałki kodu albo gotowe, napisane przez kogoś innego. Może się zdarzyć, że ktoś będzie próbował wejść na nasz adres po IP. Możemy też mieć naszą stronę dostępną pod różnymi domenami. Spróbujmy zatem napisać prosty middleware, który przekieruje użytkownika na poprawny adres jeśli ten wejdzie w nieaktywną lub błędną domenę, która dalej kieruje na nasz serwer
from django.http import HttpResponseRedirect
DOMAIN = 'ddeby.pl'
SITE = 'https://ddeby.pl'
class RedirectMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if not request.get_host().startswith(DOMAIN):
return HttpResponseRedirect('https://ddeby.pl/')
return self.get_response(request)
Teraz wystarczy dodać odpowiedni wpis w ustawieniach
MIDDLEWARE = [
[ ... ]
'app.middleware.RedirectMiddleware'
]
W ten prosty sposób mamy proste zabezpieczenie. Ale jest tu jedna luka w myśleniu. Lokalnie nie zawsze chcemy nadpisywać sobie hosty i wchodzimy przez localhost:8000 . Musielibyśmy w local_settings nadpisywać cały blok z middleware. Jeśliby byłoby ich więcej, to stałoby się to trudne do utrzymania. Zróbmy zatem prostą sztuczkę i dodajmy flagę, którą możemy sterować. Nasz poprawiony middleware powinien zatem wyglądać następująco
from django.conf import settings
from django.http import HttpResponseRedirect
DOMAIN = 'ddeby.pl'
SITE = 'https://ddeby.pl'
ALLOW_DOMAIN_REDIRECTS = getattr(settings, 'ALLOW_DOMAIN_REDIRECTS', False)
class RedirectMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if ALLOW_DOMAIN_REDIRECTS and not request.get_host().startswith(DOMAIN):
return HttpResponseRedirect('https://ddeby.pl/')
return self.get_response(request)
Teraz mamy już więcej możliwości. Możemy w normalnych ustawieniach zostawić to włączone i wyłączać w tych lokalnych. Możemy również zostawić to wyłączone i włączać w ustawieniach produkcyjnych. Wszystko zależy od zapotrzebowania. Jeśli pracujemy głównie samodzielnie i często eksperymentujemy z różnymi serwerami, to warto skorzystać z pierwszej opcji. Jeśli jednak nasze serwery są raczej niezmienne a my pracujemy w większym gronie, to drugie rozwiązanie wydaje się być bardziej rozsądne.
Podsumowanie
Ustawienia lokalne to nie jest niezbędna wiedza dla programistów. Jednak doświadczeni deweloperzy, pracujący w większych gronach i przy dużej ilości projektów zapewne doceniają taką możliwość. A Wy znacie inne, ciekawe zastosowania local_settings.py. Podzielcie się nimi w komentarzu