Jak zarządzać ustawieniami lokalnymi a produkcyjnymi w Django?

298

Jaki jest zalecany sposób obsługi ustawień lokalnego programowania i serwera produkcyjnego? Niektóre z nich (takie jak stałe itp.) Mogą być zmieniane / dostępne w obu, ale niektóre z nich (jak ścieżki do plików statycznych) muszą pozostać inne, a zatem nie powinny być nadpisywane przy każdym wdrożeniu nowego kodu.

Obecnie dodaję wszystkie stałe do settings.py. Ale za każdym razem, gdy zmieniam lokalnie stałą, muszę skopiować ją na serwer produkcyjny i edytować plik, aby wprowadzić zmiany specyficzne dla produkcji ... :(

Edycja: wygląda na to, że nie ma standardowej odpowiedzi na to pytanie, zaakceptowałem najpopularniejszą metodę.

akv
źródło
Proszę spojrzeć na konfiguracje django .
JJD,
2
Przyjęta metoda nie jest już najpopularniejsza.
Daniel
2
Ustawienia django-split są bardzo łatwe w użyciu. Nie wymaga przepisania żadnych ustawień domyślnych.
sobolevn
należy użyć pliku base.py i w pliku local.py „z importu .base *”, podobnie jak w pliku production.py „z importu .base *”, musisz uruchomić swój projekt za pomocą: python manage.py runserver - settings = nazwa_projektu.settings.local
Roberth Solís,

Odpowiedzi:

127

W settings.py:

try:
    from local_settings import *
except ImportError as e:
    pass

Możesz zastąpić to, co jest potrzebne local_settings.py; powinien wtedy pozostawać poza kontrolą wersji. Ale skoro wspomniałeś o kopiowaniu, zgaduję, że nie używasz żadnego;)

Och nie
źródło
3
Aby ułatwić śledzenie / wdrażanie nowych ustawień, użyj „local_settings.py” na maszynach produkujących / testujących, a żadnych na rozwoju.
John Mee,
8
Tak właśnie robię - dodając te wiersze na końcu
pliku
61
Takie podejście oznacza, że ​​masz niewersjonowany kod działający w fazie projektowania i produkcji. I każdy programista ma inną bazę kodu, wzywam tutaj anty-wzór.
pydanny
8
@pydanny Problem polega na tym, że Django przechowuje swoją konfigurację w pliku .py. Nie możesz oczekiwać, że wszyscy programiści i serwer produkcyjny będą używać tych samych ustawień, więc musisz zmienić ten plik .py lub zaimplementować alternatywne rozwiązanie (pliki .ini, środowisko itp.).
Tupteq
3
Wolę wywoływać moduł, settings_locala nie local_settingsgrupować go settings.pyw alfabetycznych listach folderów. Unikaj settings_local.pykontroli wersji, .gitignoreponieważ poświadczenia nie należą do Git. Wyobraź sobie przypadkowe pozyskiwanie ich. W settings_local.py.txtzamian trzymam plik szablonu o nazwie .
przełamanie linii,
297

Dwie miarki Django: najlepsze praktyki dla Django 1.5 sugerują użycie kontroli wersji dla plików ustawień i przechowywanie plików w osobnym katalogu:

project/
    app1/
    app2/
    project/
        __init__.py
        settings/
            __init__.py
            base.py
            local.py
            production.py
    manage.py

base.pyPlik zawiera typowe ustawienia (takie jak MEDIA_ROOT lub admin), podczas gdy local.pyiproduction.py mają ustawienia site-specific:

W pliku podstawowym settings/base.py:

INSTALLED_APPS = (
    # common apps...
)

W pliku lokalnych ustawień programistycznych settings/local.py:

from project.settings.base import *

DEBUG = True
INSTALLED_APPS += (
    'debug_toolbar', # and other apps for local development
)

W pliku ustawień produkcji pliku settings/production.py:

from project.settings.base import *

DEBUG = False
INSTALLED_APPS += (
    # other apps for production site
)

Następnie po uruchomieniu django dodajesz --settingsopcję:

# Running django for local development
$ ./manage.py runserver 0:8000 --settings=project.settings.local

# Running django shell on the production site
$ ./manage.py shell --settings=project.settings.production

Autorzy książki umieścili również przykładowy szablon układu projektu na Github.

gen_wood
źródło
62
Zauważ, że zamiast używać za --settingskażdym razem, możesz ustawić DJANGO_SETTINGS_MODULEenvvar. Działa to dobrze np. Z Heroku: ustaw go globalnie na produkcję, a następnie zastąp go dev w pliku .env.
Simon Weber
9
Korzystanie z DJANGO_SETTINGS_MODULEenv var jest tutaj najlepszym pomysłem, dzięki Simon.
kibibu
20
Może być konieczna zmiana BASE_DIRustawień naos.path.dirname(os.path.realpath(os.path.dirname(__file__) + "/.."))
Petr Peller,
5
@rsp według docs Django importowania from django.conf import settingsktóry jest obiektem, który abstrahuje interfejs i oddziela kod od miejsca ustawienia, docs.djangoproject.com/en/dev/topics/settings/...
3
Jeśli ustawię DJANGO_SETTINGS_MODULE za pomocą zmiennej środowiskowej, czy nadal potrzebuję os.environ.setdefault („DJANGO_SETTINGS_MODULE”, „projectname.settings.production”) w moim pliku wsgi.py? Poza tym ustawiłem zmienną środowiskową za pomocą: export DJANGO_SETTINGS_MODULE = nazwa_projektu.settings.local, ale potem jest zgubiona po zamknięciu terminalu. Co mogę zrobić, aby zapewnić, że zostanie zapisany? Czy powinienem dodać tę linię do pliku bashrc?
Kritz
71

Zamiast tego settings.pyużyj tego układu:

.
└── settings/
    ├── __init__.py  <= not versioned
    ├── common.py
    ├── dev.py
    └── prod.py

common.py jest miejscem, gdzie mieszka większość twojej konfiguracji.

prod.py importuje wszystko ze wspólnego i zastępuje wszystko, czego potrzebuje do zastąpienia:

from __future__ import absolute_import # optional, but I like it
from .common import *

# Production overrides
DEBUG = False
#...

Podobnie, dev.pyimportuje wszystko z common.pyi zastępuje wszystko, czego potrzebuje do zastąpienia.

Wreszcie __init__.pydecydujesz, które ustawienia załadować, i gdzie przechowujesz sekrety (dlatego ten plik nie powinien być wersjonowany):

from __future__ import absolute_import
from .prod import *  # or .dev if you want dev

##### DJANGO SECRETS
SECRET_KEY = '(3gd6shenud@&57...'
DATABASES['default']['PASSWORD'] = 'f9kGH...'

##### OTHER SECRETS
AWS_SECRET_ACCESS_KEY = "h50fH..."

W tym rozwiązaniu podoba mi się:

  1. Wszystko jest w twoim systemie wersjonowania, z wyjątkiem tajemnic
  2. Najbardziej konfiguracja jest w jednym miejscu: common.py.
  3. Wchodzą rzeczy prod.pyspecyficzne dla produktu, wchodzą rzeczy specyficzne dla twórców dev.py. To proste.
  4. Można zastąpić rzeczy z common.pyna prod.pylub dev.py, można zastąpić nic__init__.py .
  5. To prosty python. Brak ponownych importów hacków.
MiniQuark
źródło
2
Wciąż próbuję ustalić, co ustawić w moim projekcie.wsgi i zarządzać plikami.py dla pliku ustawień. Czy rzucisz na to trochę światła? W szczególności w moim pliku manage.py mam os.environ.setdefault("DJANGO_SETTINGS_MODULE", "foobar.settings")foobar to folder z __init__.pyplikiem, a ustawienia to folder z __init__.pyplikiem, który zawiera moje tajemnice i importuje dev.py, który następnie importuje plik common.py. EDYCJA Nevermind, nie miałem zainstalowanego modułu, który był wymagany. Mój błąd! To działa świetnie !!
teewuane
5
Dwie rzeczy: 1) lepiej ustawić Debug = True w swoim dev.py zamiast = False w swoim prod.py. 2) Zamiast przełączać się na init .py, przełączaj używając środowiska DJANGO_SETTINGS_MODULE var. Pomoże to we wdrożeniach PAAS (np. Heroku).
Rob Grant,
Gdy korzystam z tej instalacji w programie django 1.8.4 i próbuję uruchomić serwer, otrzymuję komunikat „django.core.exceptions.ImproperlyConfigured: Ustawienie SECRET_KEY nie może być puste.”, Nawet jeśli mam plik SECRET_KEY w moim pliku .py init . Czy coś brakuje?
polarcare
czy korzystanie z czegoś takiego jak AWS_SECRET_ACCESS_KEY = os.getenv („AWS_SECRET_ACCESS_KEY”) nie jest bezpieczniejsze? Szczere pytanie - wiem, dlaczego nie chcesz wersji, ale inną alternatywą jest pobranie jej ze środowiska. Co oczywiście nasuwa pytanie o ustawienie zmiennej środowiskowej, ale można to pozostawić mechanizmowi wdrażania, nie?
JL Peyret
20

Używam nieco zmodyfikowanej wersji stylu „jeśli DEBUG”, który opublikował Harper Shelby. Oczywiście w zależności od środowiska (win / linux / etc.) Kod może wymagać drobnych poprawek.

W przeszłości używałem „if DEBUG”, ale okazało się, że od czasu do czasu muszę przeprowadzać testy z ustawieniem DEUBG na False. To, co naprawdę chciałem odróżnić, czy środowiskiem była produkcja czy rozwój, co dało mi swobodę wyboru poziomu DEBUG.

PRODUCTION_SERVERS = ['WEBSERVER1','WEBSERVER2',]
if os.environ['COMPUTERNAME'] in PRODUCTION_SERVERS:
    PRODUCTION = True
else:
    PRODUCTION = False

DEBUG = not PRODUCTION
TEMPLATE_DEBUG = DEBUG

# ...

if PRODUCTION:
    DATABASE_HOST = '192.168.1.1'
else:
    DATABASE_HOST = 'localhost'

Nadal uważam ten sposób ustawiania za trwający. Nie widziałem żadnego sposobu obsługi ustawień Django, który obejmowałby wszystkie podstawy, a jednocześnie nie był to całkowity problem z konfiguracją (nie jestem zaniepokojony metodami plików ustawień 5x).

T. Stone
źródło
Jest to rodzaj rzeczy, na które pozwalają ustawienia Django jako rzeczywistego pliku kodu, a ja sugerowałem. Sam nic takiego nie zrobiłem, ale zdecydowanie jest to rozwiązanie, które może być lepszą odpowiedzią ogólną niż moje.
Harper Shelby
3
Właśnie natknąłem się na to po raz pierwszy i zdecydowałem (z powodzeniem!) Skorzystać z twojego rozwiązania, z niewielką różnicą: użyłem uuid.getnode (), aby znaleźć uuid mojego systemu. Więc testuję, czy uuid.getnode () == 12345678901 (właściwie inna liczba) zamiast użytego testu os.environ. Nie mogłem znaleźć dokumentacji, która przekonałaby mnie, że os.environ ['COMPUTERNAME'] jest unikalny dla każdego komputera.
Joe Golton,
os.environ ['COMPUTERNAME'] nie działa na Amazon AWS Ubuntu. Otrzymuję KeyError.
nve everest
Podczas korzystania z UUID to rozwiązanie okazało się dla mnie najlepsze i najprostsze. Nie wymaga wielu skomplikowanych i nadmiernie modułowych patchworków. W środowisku produkcyjnym nadal musisz umieścić hasła do bazy danych i klucz SECRET_KEY w osobnym pliku znajdującym się poza kontrolą wersji.
nve everest
os.environ['COMPUTERNAME']niestety nie działa w PythonAnywhere. Otrzymujesz KeyError.
nbeuchat
14

Korzystam z settings_local.py i settings_production.py. Po wypróbowaniu kilku opcji odkryłem, że łatwo jest tracić czas na złożone rozwiązania, gdy posiadanie dwóch plików ustawień jest łatwe i szybkie.

Kiedy używasz mod_python / mod_wsgi dla swojego projektu Django, musisz wskazać go na plik ustawień. Jeśli wskażesz na app / settings_local.py na lokalnym serwerze i app / settings_production.py na serwerze produkcyjnym, życie stanie się łatwe. Po prostu edytuj odpowiedni plik ustawień i zrestartuj serwer (serwer programistyczny Django uruchomi się ponownie automatycznie).

Kai
źródło
2
A co z lokalnym serwerem programistycznym? czy istnieje sposób, aby poinformować serwer WWW django (uruchomić przy użyciu python manage.py runserver), którego pliku ustawień użyć?
akv
2
@akv, jeśli dodasz --settings = [nazwa modułu] (bez rozszerzenia .py) na końcu polecenia runserver, możesz określić, którego pliku ustawień użyć. Jeśli masz zamiar to zrobić, zrób sobie przysługę i utwórz skrypt powłoki / plik wsadowy ze skonfigurowanymi ustawieniami programistycznymi. Zaufaj mi, twoje palce będą ci wdzięczne.
T. Stone,
to jest rozwiązanie, którego używam. włamywanie się do pliku ustawień, który będzie używany zarówno do produkcji, jak i rozwoju, jest nieuporządkowane
George Godik
4
Myślę, że lepiej jest używać settings.py w fazie programowania, ponieważ nie trzeba go cały czas określać.
Andre Bossard,
Czy mam rację, zakładając, że ta metoda wymaga importu modułu ustawień przez serwer proxy, django.conf.settings? W przeciwnym razie konieczne będzie edytowanie deklaracji importu, aby wskazywały właściwy plik ustawień podczas przesyłania na żywo.
Groady,
8

TL; DR: Sztuką jest zmodyfikowanie os.environmentprzed zaimportowaniem settings/base.pydowolnego settings/<purpose>.py, co znacznie uprości rzeczy.


Samo myślenie o tych wszystkich powiązanych plikach sprawia mi ból głowy. Łączenie, importowanie (czasem warunkowo), przesłonięcie, łatanie tego, co było już ustawione w przypadku DEBUGzmiany przypadku później. Co za koszmar!

Przez lata przeszedłem przez różne rozwiązania. Wszystkie działają nieco , ale bardzo bolesne w zarządzaniu. WTF! Czy naprawdę potrzebujemy całego tego problemu? Zaczęliśmy od tylko jednego settings.pypliku. Teraz potrzebujemy dokumentacji, aby poprawnie połączyć wszystkie te elementy we właściwej kolejności!

Mam nadzieję, że w końcu trafiłem na (mój) najsłodszy punkt dzięki poniższemu rozwiązaniu.

Podsumujmy cele (niektóre wspólne, niektóre moje)

  1. Zachowaj tajemnicę w tajemnicy - nie przechowuj ich w repozytorium!

  2. Ustaw / czytaj klucze i sekrety poprzez ustawienia środowiska, styl 12-czynnikowy .

  3. Mają rozsądne domyślne ustawienia rezerwowe. Idealnie do lokalnego rozwoju nie potrzebujesz niczego więcej oprócz domyślnych.

  4. … Ale staraj się zachować bezpieczeństwo domyślnej produkcji. Lepiej pominąć zastąpienie ustawienia lokalnie, niż pamiętać o dostosowaniu ustawień domyślnych bezpiecznych dla produkcji.

  5. Mają możliwość włączania DEBUG/ wyłączania w sposób, który może mieć wpływ na inne ustawienia (np. Używając kompresji javascript lub nie).

  6. Przełączanie między ustawieniami celu, takimi jak lokalny / testowanie / inscenizacja / produkcja, powinno opierać się tylko na DJANGO_SETTINGS_MODULEniczym innym.

  7. … Ale pozwalają na dalszą parametryzację poprzez ustawienia środowiska takie jak DATABASE_URL.

  8. … Pozwalają również na używanie różnych ustawień celu i uruchamianie ich lokalnie obok siebie, np. konfiguracja produkcji na lokalnym komputerze dewelopera, aby uzyskać dostęp do bazy danych produkcji lub arkuszy stylów skompresowanych testów dymu.

  9. Błąd, jeśli zmienna środowiskowa nie jest jawnie ustawiona (wymagająca co najmniej pustej wartości), szczególnie w środowisku produkcyjnym, np. EMAIL_HOST_PASSWORD.

  10. Odpowiedz na domyślny DJANGO_SETTINGS_MODULEzestaw w manage.py podczas start-projektu django-admin

  11. Przechowywać warunkowe do minimum, jeśli warunek jest przeznaczona typ środowiska (np. Do produkcji nastawionej pliku dziennika i to obrót), zastępują ustawienia w pliku związanego przeznaczona ustawień.

Nie jest

  1. Nie pozwól django odczytać ustawienia DJANGO_SETTINGS_MODULE z pliku.
    Ugh! Pomyśl, jak to jest meta. Jeśli potrzebujesz pliku (np. Docker env), przeczytaj go w środowisku przed rozpoczęciem procesu django.

  2. Nie zastępuj DJANGO_SETTINGS_MODULE w kodzie projektu / aplikacji, np. na podstawie nazwy hosta lub nazwy procesu.
    Jeśli jesteś leniwy, aby ustawić zmienną środowiskową (jak dlasetup.py test ), zrób to w oprzyrządowaniu tuż przed uruchomieniem kodu projektu.

  3. Unikaj magii i łatania tego, jak django odczytuje swoje ustawienia, wstępnie przetwarzaj ustawienia, ale nie przeszkadzaj później.

  4. Żadnych skomplikowanych bzdur opartych na logice. Konfiguracja powinna być stała i zmaterializowana, a nie obliczana w locie. Zapewnienie zastępczych ustawień domyślnych jest tutaj wystarczającą logiką.
    Czy naprawdę chcesz debugować, dlaczego lokalnie masz poprawny zestaw ustawień, ale w produkcji na zdalnym serwerze, na jednej ze stu maszyn, coś obliczonego inaczej? O! Testy jednostkowe? Dla ustawień? Poważnie?

Rozwiązanie

Moja strategia polega na doskonałej Django Environ używanego z iniplikami styl, zapewniając os.environmentdomyślne dla rozwoju lokalnego, niektóre minimalne i krótkie settings/<purpose>.pypliki, które mają import settings/base.py POos.environment został ustawiony z INIpliku. To skutecznie daje nam rodzaj zastrzyku ustawień.

Sztuczka polega na zmodyfikowaniu os.environmentprzed zaimportowaniemsettings/base.py .

Aby zobaczyć pełny przykład, wykonaj repozytorium: https://github.com/wooyek/django-settings-strategy

.
   manage.py
├───data
└───website
    ├───settings
          __init__.py   <-- imports local for compatibility
          base.py       <-- almost all the settings, reads from proces environment 
          local.py      <-- a few modifications for local development
          production.py <-- ideally is empty and everything is in base 
          testing.py    <-- mimics production with a reasonable exeptions
          .env          <-- for local use, not kept in repo
       __init__.py
       urls.py
       wsgi.py

ustawienia / .env

Domyślne ustawienia lokalne. Tajny plik, w którym najczęściej ustawiane są wymagane zmienne środowiskowe. Ustaw je na puste wartości, jeśli nie są wymagane w rozwoju lokalnym. Zapewniamy wartości domyślne tutaj, a nie wsettings/base.py ulegną awarii na żadnym innym komputerze, jeśli brakuje ich w środowisku.

ustawienia / local.py

To, co się tutaj dzieje, to ładowanie środowiska settings/.env, a następnie importowanie wspólnych ustawień settings/base.py. Następnie możemy zastąpić kilka, aby ułatwić rozwój lokalny.

import logging
import environ

logging.debug("Settings loading: %s" % __file__)

# This will read missing environment variables from a file
# We wan to do this before loading a base settings as they may depend on environment
environ.Env.read_env(DEBUG='True')

from .base import *

ALLOWED_HOSTS += [
    '127.0.0.1',
    'localhost',
    '.example.com',
    'vagrant',
    ]

# https://docs.djangoproject.com/en/1.6/topics/email/#console-backend
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend'

LOGGING['handlers']['mail_admins']['email_backend'] = 'django.core.mail.backends.dummy.EmailBackend'

# Sync task testing
# http://docs.celeryproject.org/en/2.5/configuration.html?highlight=celery_always_eager#celery-always-eager

CELERY_ALWAYS_EAGER = True
CELERY_EAGER_PROPAGATES_EXCEPTIONS = True

settings / production.py

W przypadku produkcji nie powinniśmy oczekiwać pliku środowiska, ale łatwiej go mieć, jeśli coś testujemy. Ale w każdym razie, aby nie wprowadzać kilku wartości domyślnych, więc settings/base.pymogą odpowiednio zareagować.

environ.Env.read_env(Path(__file__) / "production.env", DEBUG='False', ASSETS_DEBUG='False')
from .base import *

Głównym przedmiotem zainteresowania są DEBUGi ASSETS_DEBUGzastępują, zostaną one zastosowane do pytonaos.environ TYLKO, jeśli brakuje go ze środowiska i pliku.

Będą to nasze domyślne ustawienia produkcyjne, nie trzeba ich umieszczać w środowisku lub pliku, ale w razie potrzeby można je zastąpić. Schludny!

settings / base.py

Są to twoje najczęściej waniliowe ustawienia django, z kilkoma warunkami warunkowymi i dużą ilością czytania ich ze środowiska. Prawie wszystko jest tutaj, utrzymując spójne i możliwie najbardziej zbliżone środowiska.

Główne różnice są poniżej (mam nadzieję, że są to oczywiste):

import environ

# https://github.com/joke2k/django-environ
env = environ.Env()

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

# Where BASE_DIR is a django source root, ROOT_DIR is a whole project root
# It may differ BASE_DIR for eg. when your django project code is in `src` folder
# This may help to separate python modules and *django apps* from other stuff
# like documentation, fixtures, docker settings
ROOT_DIR = BASE_DIR

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env('SECRET_KEY')

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env('DEBUG', default=False)

INTERNAL_IPS = [
    '127.0.0.1',
]

ALLOWED_HOSTS = []

if 'ALLOWED_HOSTS' in os.environ:
    hosts = os.environ['ALLOWED_HOSTS'].split(" ")
    BASE_URL = "https://" + hosts[0]
    for host in hosts:
        host = host.strip()
        if host:
            ALLOWED_HOSTS.append(host)

SECURE_SSL_REDIRECT = env.bool('SECURE_SSL_REDIRECT', default=False)

# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases

if "DATABASE_URL" in os.environ:  # pragma: no cover
    # Enable database config through environment
    DATABASES = {
        # Raises ImproperlyConfigured exception if DATABASE_URL not in os.environ
        'default': env.db(),
    }

    # Make sure we use have all settings we need
    # DATABASES['default']['ENGINE'] = 'django.contrib.gis.db.backends.postgis'
    DATABASES['default']['TEST'] = {'NAME': os.environ.get("DATABASE_TEST_NAME", None)}
    DATABASES['default']['OPTIONS'] = {
        'options': '-c search_path=gis,public,pg_catalog',
        'sslmode': 'require',
    }
else:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            # 'ENGINE': 'django.contrib.gis.db.backends.spatialite',
            'NAME': os.path.join(ROOT_DIR, 'data', 'db.dev.sqlite3'),
            'TEST': {
                'NAME': os.path.join(ROOT_DIR, 'data', 'db.test.sqlite3'),
            }
        }
    }

STATIC_ROOT = os.path.join(ROOT_DIR, 'static')

# django-assets
# http://django-assets.readthedocs.org/en/latest/settings.html

ASSETS_LOAD_PATH = STATIC_ROOT
ASSETS_ROOT = os.path.join(ROOT_DIR, 'assets', "compressed")
ASSETS_DEBUG = env('ASSETS_DEBUG', default=DEBUG)  # Disable when testing compressed file in DEBUG mode
if ASSETS_DEBUG:
    ASSETS_URL = STATIC_URL
    ASSETS_MANIFEST = "json:{}".format(os.path.join(ASSETS_ROOT, "manifest.json"))
else:
    ASSETS_URL = STATIC_URL + "assets/compressed/"
    ASSETS_MANIFEST = "json:{}".format(os.path.join(STATIC_ROOT, 'assets', "compressed", "manifest.json"))
ASSETS_AUTO_BUILD = ASSETS_DEBUG
ASSETS_MODULES = ('website.assets',)

Ostatni bit pokazuje moc tutaj. ASSETS_DEBUGma sensowną wartość domyślną, którą można zastąpićsettings/production.py a nawet to, które można zastąpić ustawieniem środowiska! Tak!

W efekcie mamy mieszaną hierarchię ważności:

  1. settings / .py - ustawia wartości domyślne na podstawie celu, nie przechowuje tajemnic
  2. settings / base.py - jest w większości kontrolowany przez środowisko
  3. ustawienia środowiska procesowego - dziecko 12-czynnikowe!
  4. ustawienia / .env - lokalne ustawienia domyślne dla łatwego uruchamiania
Janusz Skonieczny
źródło
Hej Janusz ... więc w pliku .env byłyby wszystkie klucze API, klucze uwierzytelniania i hasła itp.? Podobnie jak TWILLIO_API = "abc123"? Lub TWILLIO_API = env („TWILLIO_API”)?
dbinott,
Tak, ale to tylko awaria ustawień środowiska. Ten plik jest przydatny do programowania, ale nie jest zapisywany w repozytorium ani przekazywany do produkcji, gdzie należy ściśle używać ustawień środowiska lub odpowiednika platformy, który z kolei ustawi ustawienia środowiska dla procesu serwera.
Janusz Skonieczny
7

Zarządzam moimi konfiguracjami za pomocą django-split-settings .

Jest to drop-in zamiennik dla ustawień domyślnych. Jest prosty, ale konfigurowalny. A refaktoryzacja istniejących ustawień nie jest wymagana.

Oto mały przykład (plik example/settings/__init__.py):

from split_settings.tools import optional, include
import os

if os.environ['DJANGO_SETTINGS_MODULE'] == 'example.settings':
    include(
        'components/default.py',
        'components/database.py',
        # This file may be missing:
        optional('local_settings.py'),

        scope=globals()
    )

Otóż ​​to.

Aktualizacja

Napisałem wpis na blogu o zarządzaniu djangoustawieniami django-split-sttings. Spójrz!

sobolevn
źródło
1
Próbowałem tego ... wpadłem na ścianę, kiedy próbowałem uruchomić moje testy jednostkowe django .. po prostu nie mogłem dowiedzieć się, jak określić, z którego pliku ustawień
mam
Stworzyłem dla ciebie list
sobolevn
Mam coś jak to w moim kodu, więc sprawdź flagę settings.DEBUG wiedzieć, czy chcę importować rzeczy .. że flaga jest zawsze ustawiona na false w Django testów jednostkowych (patrz tutaj ), więc moja praca wokół jest, aby zastąpić je przy każdy test tak
wyglądał
oto inne pytanie: mój uwsgi.iniplik ma różne ustawienia w różnych programistach / programistach .. masz pomysł, jak wybrać wartości z pliku ustawień?
abbood
przepraszam, nie dostaję konfiguracji. możesz zadać osobne pytanie z dalszymi szczegółami, a ja postaram się ci pomóc.
sobolevn
6

Problem z większością tych rozwiązań polega na tym, że ustawienia lokalne są stosowane przed typowymi lub po nich.

Dlatego niemożliwe jest zastąpienie takich rzeczy

  • ustawienia specyficzne dla env definiują adresy puli pamięci memcached, aw głównym pliku ustawień ta wartość służy do konfigurowania zaplecza pamięci podręcznej
  • ustawienia specyficzne dla env dodają lub usuwają aplikacje / oprogramowanie pośrednie do domyślnego

w tym samym czasie.

Jedno rozwiązanie można zaimplementować przy użyciu plików konfiguracyjnych w stylu „ini” z klasą ConfigParser. Obsługuje wiele plików, interpolację ciągów leniwych, wartości domyślne i wiele innych korzyści. Po załadowaniu pewnej liczby plików można załadować więcej plików, a ich wartości zastąpią poprzednie, jeśli takie istnieją.

Ładujesz jeden lub więcej plików konfiguracyjnych, w zależności od adresu komputera, zmiennych środowiskowych, a nawet wartości we wcześniej załadowanych plikach konfiguracyjnych. Następnie wystarczy użyć przeanalizowanych wartości, aby wypełnić ustawienia.

Jedną strategią, którą z powodzeniem zastosowałem, jest:

  • Załaduj domyślne defaults.ini plik
  • Sprawdź nazwę komputera i załaduj wszystkie pliki, które pasowały do ​​odwróconej nazwy FQDN, od najkrótszego dopasowania do najdłuższego dopasowania (więc załadowałem net.ini, a net.domain.ininastępnienet.domain.webserver01.ini każdy z nich, być może, zastępując wartości z poprzedniego). To konto dotyczy także komputerów programistów, dzięki czemu każdy może skonfigurować preferowany sterownik bazy danych itp. Na potrzeby rozwoju lokalnego
  • Sprawdź, czy zadeklarowano „nazwę klastra”, a następnie załaduj cluster.cluster_name.ini, która może definiować takie rzeczy, jak baza danych i adresy IP pamięci podręcznej

Jako przykład czegoś, co można dzięki temu osiągnąć, można zdefiniować wartość „subdomeny” na env, która jest następnie używana w ustawieniach domyślnych (jako hostname: %(subdomain).whatever.net ) do zdefiniowania wszystkich niezbędnych nazw hostów i plików cookie, które django musi działać.

To jest tak SUCHE, jakie mogłem uzyskać, większość (istniejących) plików miała tylko 3 lub 4 ustawienia. Poza tym musiałem zarządzać konfiguracją klienta, więc istniał dodatkowy zestaw plików konfiguracyjnych (takich jak nazwy bazy danych, użytkownicy i hasła, przypisana subdomena itp.), Jeden lub więcej na klienta.

Można to skalować tak nisko lub tak wysoko, jak to konieczne, wystarczy umieścić w pliku konfiguracyjnym klucze, które chcesz skonfigurować dla środowiska, a gdy zajdzie potrzeba nowej konfiguracji, ustaw poprzednią wartość w domyślnej konfiguracji i zastąp ją tam gdzie konieczne.

Ten system okazał się niezawodny i działa dobrze z kontrolą wersji. Od dawna zarządza dwoma oddzielnymi klastrami aplikacji (15 lub więcej oddzielnych instancji strony django na maszynę), z ponad 50 klientami, gdzie klastry zmieniają rozmiar i członków w zależności od nastroju sysadmin .. .

przepisane
źródło
1
Czy masz przykład, w jaki sposób ładujesz ustawienia z ini do ustawień Django?
kaleissin
Zobacz docs.python.org/2/library/configparser.html . Możesz załadować parser, a config = ConfigParser.ConfigParser() następnie odczytać pliki config.read(array_of_filenames)i uzyskać wartości za pomocą config.get(section, option). Najpierw ładujesz swoją konfigurację, a następnie używasz jej do odczytu wartości ustawień.
przepisany
5

Pracuję również z Laravelem i podoba mi się tam implementacja. Próbowałem go naśladować i połączyć z rozwiązaniem zaproponowanym przez T. Stone'a (patrz wyżej):

PRODUCTION_SERVERS = ['*.webfaction.com','*.whatever.com',]

def check_env():
    for item in PRODUCTION_SERVERS:
        match = re.match(r"(^." + item + "$)", socket.gethostname())
        if match:
            return True

if check_env():
    PRODUCTION = True
else:
    PRODUCTION = False

DEBUG = not PRODUCTION

Może coś takiego by ci pomogło.

Robert Kuzma
źródło
4

Pamiętaj, że settings.py jest plikiem kodu na żywo. Zakładając, że DEBUG nie jest ustawiony na produkcję (co jest najlepszą praktyką), możesz zrobić coś takiego:

if DEBUG:
    STATIC_PATH = /path/to/dev/files
else:
    STATIC_PATH = /path/to/production/files

Całkiem proste, ale teoretycznie można przejść do dowolnego poziomu złożoności opartego tylko na wartości DEBUG - lub innej zmiennej lub sprawdzeniu kodu, którego chcesz użyć.

Harper Shelby
źródło
4

Do większości moich projektów używam następującego wzorca:

  1. Utwórz settings_base.py, w którym przechowuję ustawienia wspólne dla wszystkich środowisk
  2. Ilekroć muszę używać nowego środowiska z określonymi wymaganiami, tworzę nowy plik ustawień (np. Settings_local.py), który dziedziczy zawartość settings_base.py i zastępuje / dodaje odpowiednie zmienne ustawień ( from settings_base import *)

(Aby uruchomić manage.py z ustawień niestandardowych plik, wystarczy użyć opcji --settings polecenia: manage.py <command> --settings=settings_you_wish_to_use.py)

dzida
źródło
3

Moje rozwiązanie tego problemu stanowi także mieszankę niektórych rozwiązań już tutaj podanych:

  • Trzymam plik o nazwie, local_settings.pyktóry ma zawartość USING_LOCAL = Truew dev i USING_LOCAL = Falsein prod
  • W settings.pyrobię importu na tym pliku, aby uzyskać USING_LOCALustawienie

Następnie opieram wszystkie moje ustawienia zależne od środowiska:

DEBUG = USING_LOCAL
if USING_LOCAL:
    # dev database settings
else:
    # prod database settings

Wolę to od posiadania dwóch oddzielnych plików settings.py, które muszę utrzymywać, ponieważ łatwiej mogę uporządkować ustawienia w jednym pliku niż rozłożyć je na kilka plików. W ten sposób, kiedy aktualizuję ustawienie, nie zapomnę zrobić tego dla obu środowisk.

Oczywiście, że każda metoda ma swoje wady i ta nie jest wyjątkiem. Problem polega na tym, że nie mogę nadpisać local_settings.pypliku za każdym razem, gdy wprowadzam zmiany do produkcji, co oznacza, że ​​nie mogę po prostu skopiować wszystkich plików na ślepo, ale z tym mogę sobie poradzić.

Miguel Ventura
źródło
3

Używam odmiany tego, co jpartogi wspomniałem powyżej, które uważam za nieco krótsze:

import platform
from django.core.management import execute_manager 

computername = platform.node()

try:
  settings = __import__(computername + '_settings')
except ImportError: 
  import sys
  sys.stderr.write("Error: Can't find the file '%r_settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file local_settings.py does indeed exist, it's causing an ImportError somehow.)\n" % (computername, __file__))
  sys.exit(1)

if __name__ == "__main__":
  execute_manager(settings)

Zasadniczo na każdym komputerze (programistycznym lub produkcyjnym) mam odpowiedni plik hostname_settings.py, który jest dynamicznie ładowany.

stratosgear
źródło
3

Istnieją również ustawienia Django Classy. Osobiście jestem jego wielkim fanem. Jest zbudowany przez jedną z najbardziej aktywnych osób na IRC Django. Używałbyś zmiennych środowiska do ustawiania rzeczy.

http://django-classy-settings.readthedocs.io/en/latest/

SudoKid
źródło
3

1 - Utwórz nowy folder w aplikacji i ustaw do niego ustawienia nazw.

2 - Teraz stwórz w nim nowy __init__.pyplik i zapisz w nim

from .base import *

try:
    from .local import *
except:
    pass

try:
    from .production import *
except:
    pass

3 - Utwórz trzy nowe pliki w nazwie folderu ustawień local.pyi production.pyoraz base.py.

4 - Wewnątrz base.pyskopiuj całą zawartość poprzedniego settings.pyfolderu i zmień nazwę na coś innego, powiedzmy old_settings.py.

5 - W pliku base.py zmień ścieżkę BASE_DIR, aby wskazywała nową ścieżkę ustawień

Stara ścieżka-> BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

Nowa ścieżka -> BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

W ten sposób katalog projektu można uporządkować i zarządzać nim między produkcją a rozwojem lokalnym.

Jack Ryan
źródło
2

Aby użyć innej settingskonfiguracji w innym środowisku, utwórz inny plik ustawień. W skrypcie instalacyjnym uruchom serwer, używając --settings=<my-settings.py>parametru, za pomocą którego możesz używać różnych ustawień w różnych środowiskach.

Korzyści z zastosowania tego podejścia :

  1. Twoje ustawienia będą modułowe w zależności od środowiska

  2. Możesz zaimportować master_settings.pyzawierającą konfigurację podstawową do environmnet_configuration.pyi zastąpić wartości, które chcesz zmienić w tym środowisku.

  3. Jeśli masz duży zespół, każdy programista może mieć własny, local_settings.pyktóry może dodać do repozytorium kodu bez ryzyka modyfikacji konfiguracji serwera. Możesz dodać te ustawienia lokalne do .gitnorejeśli używasz git lub .hginorejeśli Mercurial dla kontroli wersji (lub jakiegokolwiek innego). W ten sposób ustawienia lokalne nie będą nawet częścią rzeczywistej bazy kodu, która utrzyma go w czystości.

Moinuddin Quadri
źródło
2

Moje ustawienia zostały podzielone w następujący sposób

settings/
     |
     |- base.py
     |- dev.py
     |- prod.py  

Mamy 3 środowiska

  • dev
  • inscenizacja
  • produkcja

Teraz oczywiście inscenizacja i produkcja powinny mieć maksymalnie możliwe podobne środowisko. Więc zatrzymaliśmyprod.py dla obu.

Ale był przypadek, w którym musiałem zidentyfikować działający serwer jako serwer produkcyjny. @T. Odpowiedź Stone'a pomogła mi napisać czek w następujący sposób.

from socket import gethostname, gethostbyname  
PROD_HOSTS = ["webserver1", "webserver2"]

DEBUG = False
ALLOWED_HOSTS = [gethostname(), gethostbyname(gethostname()),]


if any(host in PROD_HOSTS for host in ALLOWED_HOSTS):
    SESSION_COOKIE_SECURE = True
    CSRF_COOKIE_SECURE = True  
Kishor Pawar
źródło
1

Rozróżniam go w manage.py i utworzyłem dwa osobne pliki ustawień: local_settings.py i prod_settings.py.

W manage.py sprawdzam, czy serwer jest serwerem lokalnym czy serwerem produkcyjnym. Jeśli jest to serwer lokalny, załaduje plik local_settings.py i jest to serwer produkcyjny, który załaduje prod_settings.py. Zasadniczo tak to wyglądałoby:

#!/usr/bin/env python
import sys
import socket
from django.core.management import execute_manager 

ipaddress = socket.gethostbyname( socket.gethostname() )
if ipaddress == '127.0.0.1':
    try:
        import local_settings # Assumed to be in the same directory.
        settings = local_settings
    except ImportError:
        import sys
        sys.stderr.write("Error: Can't find the file 'local_settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file local_settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
        sys.exit(1)
else:
    try:
        import prod_settings # Assumed to be in the same directory.
        settings = prod_settings    
    except ImportError:
        import sys
        sys.stderr.write("Error: Can't find the file 'prod_settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file prod_settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
        sys.exit(1)

if __name__ == "__main__":
    execute_manager(settings)

Odkryłem, że łatwiej jest rozdzielić plik ustawień na dwa osobne pliki niż robić wiele ifs w pliku ustawień.

Joshua Partogi
źródło
1

Alternatywnie do utrzymywania innego pliku, jeśli chcesz: Jeśli używasz git lub innego VCS do przesyłania kodów z lokalnego na serwer, możesz dodać plik ustawień do .gitignore.

Umożliwi to bezproblemowe wyświetlanie różnych treści w obu miejscach. SO na serwerze możesz skonfigurować niezależną wersję settings.py, a wszelkie zmiany dokonane na lokalnym nie będą odzwierciedlane na serwerze i odwrotnie.

Ponadto usunie plik settings.py z github, co jest wielką wadą, którą widziałem wielu początkujących.

sprksh
źródło
0

Myślę, że najlepsze rozwiązanie sugeruje @T. Kamień, ale nie wiem, dlaczego po prostu nie używaj flagi DEBUG w Django. Piszę poniższy kod dla mojej witryny:

if DEBUG:
    from .local_settings import *

Zawsze proste rozwiązania są lepsze niż złożone.

seyedrezafar
źródło
-2

Odpowiedzi tutaj były bardzo pomocne. (Czy to zostało ostatecznie rozwiązane? Ostatnią odpowiedzią był rok temu.) Po rozważeniu wszystkich wymienionych podejść wymyśliłem rozwiązanie, którego tutaj nie widziałem.

Moje kryteria to:

  • Wszystko powinno być pod kontrolą źródła. Nie lubię kłopotliwych kawałków.
  • Najlepiej zachować ustawienia w jednym pliku. Zapominam rzeczy, jeśli nie patrzę wprost na nie :)
  • Brak ręcznych zmian do wdrożenia. Powinny być w stanie przetestować / pchnąć / wdrożyć za pomocą jednego polecenia sieci szkieletowej.
  • Unikaj wycieków ustawień programistycznych do produkcji.
  • Trzymaj się jak najbliżej „standardowego” (* kaszel *) układu Django.

Myślałem, że włączenie hosta ma jakiś sens, ale potem pomyślałem, że prawdziwym problemem są różne ustawienia dla różnych środowisk i miałem chwilę aha. Umieszczam ten kod na końcu mojego pliku settings.py:

try:
    os.environ['DJANGO_DEVELOPMENT_SERVER'] # throws error if unset
    DEBUG = True
    TEMPLATE_DEBUG = True
    # This is naive but possible. Could also redeclare full app set to control ordering. 
    # Note that it requires a list rather than the generated tuple.
    INSTALLED_APPS.extend([
        'debug_toolbar',
        'django_nose',
    ])
    # Production database settings, alternate static/media paths, etc...
except KeyError: 
    print 'DJANGO_DEVELOPMENT_SERVER environment var not set; using production settings'

W ten sposób aplikacja domyślnie przyjmuje ustawienia produkcyjne, co oznacza, że ​​wyraźnie „dodajesz do białej listy” swoje środowisko programistyczne. O wiele bezpieczniej jest zapomnieć o ustawieniu zmiennej środowiskowej lokalnie, niż na odwrót i zapomniałeś ustawić coś w produkcji i zezwolić na użycie niektórych ustawień programisty.

Podczas programowania lokalnego, albo z poziomu powłoki, albo w pliku .bash_profile, lub gdziekolwiek:

$ export DJANGO_DEVELOPMENT_SERVER=yep

(Lub jeśli programujesz w systemie Windows, ustaw za pomocą Panelu sterowania lub jak to się nazywa w dzisiejszych czasach ... Windows zawsze sprawiał, że był tak niejasny, że można ustawić zmienne środowiskowe.)

Dzięki takiemu podejściu ustawienia programistów znajdują się w jednym (standardowym) miejscu i po prostu zastępują ustawienia produkcyjne w razie potrzeby. Wszelkie zmiany ustawień programistycznych powinny być całkowicie bezpieczne, aby zobowiązać się do kontroli źródła bez wpływu na produkcję.

Jason Boyd
źródło
Lepiej jest po prostu utrzymywać różne pliki konfiguracyjne i wybierać za pomocą standardowej zmiennej env DJango DJANGO_SETTINGS_MODULE
Rob Grant,