Django: Jak zarządzać ustawieniami deweloperskimi i produkcyjnymi?

129

Pracowałem nad podstawową aplikacją. Teraz na etapie wdrażania stało się jasne, że potrzebuję zarówno ustawień lokalnych, jak i ustawień produkcyjnych.

Byłoby wspaniale wiedzieć, co następuje:

  • Jak najlepiej radzić sobie z ustawieniami rozwoju i produkcji.
  • Jak zachować aplikacje, takie jak django-debug-toolbar, tylko w środowisku programistycznym.
  • Wszelkie inne wskazówki i sprawdzone metody dotyczące ustawień programowania i wdrażania.
Kristian Roebuck
źródło

Odpowiedzi:

109

Te DJANGO_SETTINGS_MODULEzmienne środowisko kontrole który plik ustawień Django załaduje.

Dlatego tworzysz osobne pliki konfiguracyjne dla swoich odpowiednich środowisk (pamiętaj, że mogą one oczywiście import *pochodzić z oddzielnego pliku „wspólnych ustawień”) i używaj ich DJANGO_SETTINGS_MODULEdo kontrolowania, którego z nich użyć.

Oto jak:

Jak zauważono w dokumentacji Django:

Wartość DJANGO_SETTINGS_MODULE powinna być w składni ścieżki Pythona, np. Mysite.settings. Zwróć uwagę, że moduł ustawień powinien znajdować się na ścieżce wyszukiwania importu Pythona.

Więc załóżmy, że utworzyłeś myapp/production_settings.pyi jesteś myapp/test_settings.pyw swoim repozytorium źródłowym.

W takim przypadku należałoby odpowiednio ustawić DJANGO_SETTINGS_MODULE=myapp.production_settingsużywanie pierwszego i DJANGO_SETTINGS_MODULE=myapp.test_settingsdrugiego.


Odtąd problem sprowadza się do ustawienia DJANGO_SETTINGS_MODULEzmiennej środowiskowej.

Ustawienie DJANGO_SETTINGS_MODULEza pomocą skryptu lub powłoki

Następnie można użyć skryptu bootstrap lub Process Manager, aby załadować poprawne ustawienia (poprzez ustawienie środowiska), lub po prostu go uruchomić z powłoki przed rozpoczęciem Django: export DJANGO_SETTINGS_MODULE=myapp.production_settings.

Zauważ, że możesz uruchomić ten eksport w dowolnym momencie z powłoki - nie musi on mieszkać w twoim .bashrcani nic.

Ustawienie DJANGO_SETTINGS_MODULEza pomocą menedżera procesów

Jeśli nie lubisz pisać skryptu bootstrap, który ustawia środowisko (i są bardzo dobre powody, aby tak się czuć!), Polecam skorzystanie z menedżera procesów:


Na koniec zwróć uwagę, że możesz wykorzystać PYTHONPATHzmienną do przechowywania ustawień w zupełnie innej lokalizacji (np. Na serwerze produkcyjnym, przechowywanie ich na /etc/). Pozwala to na oddzielenie konfiguracji od plików aplikacji. Możesz tego chcieć lub nie, zależy to od struktury Twojej aplikacji.

Thomas Orozco
źródło
7
Dla wyjaśnienia, ponieważ settings.pyplik jest przechowywany SiteName/settings.pydomyślnie, jeśli umieścisz swoje alternatywne pliki ustawień w tym samym katalogu, wiersz dodany do bin /ivate powinien brzmieć DJANGO_SETTINGS_MODULE="SiteName.test_settings"W przeciwnym razie doskonała odpowiedź!
alexbhandari
2
przez przypadek znasz tutorial jak to zrobić krok po kroku, jestem nowy w Django i nie wiem, gdzie ustawić DJANGO_SETTINGS_MODULE lub PYTHONPATH
Jesus Almaral - Hackaprende
Wydaje się, że to rozwiązanie nie sprawdza się w przypadku środowiska conda. W środowisku conda nie ma kosza / aktywacji.
Pouya Yousefi
1
@PouyaYousefi: absolutnie nie musisz używać virtualenv, aby użyć tej odpowiedzi. Odpowiedź tak naprawdę sprowadza się do dwóch kroków: a) użyj oddzielnych plików ustawień i b) użyj, DJANGO_SETTINGS_MODULEaby wybrać ten, którego chcesz użyć. Modyfikacja bin/activate to jedna z tych drugich (TBH, i tak nie uważam już, że to dobry pomysł, więc usunąłem to), ale nie jest to jedyne.
Thomas Orozco,
Jest to również przydatne, jeśli używasz Django w wydaniu społecznościowym pycharm i musisz poprawnie uruchomić testy jednostkowe zarówno w wierszu poleceń, jak i społeczności pycharm. Załóżmy, że utworzyłeś tylko jeden prosty plik konfiguracyjny w myapp / settings.py w swoim repozytorium źródłowym. W takim przypadku należy ustawić „DJANGO_SETTINGS_MODULE = myapp.settings” w menu RUN / Edit Configuration / Environment variable, aby użyć jej później do uruchamiania przypadków testowych.
F.Tamy
58

Domyślnie używaj ustawień produkcyjnych, ale utwórz plik o nazwie settings_dev.py w tym samym folderze co settings.pyplik. Dodaj tam nadpisania, takie jak DEBUG=True.

Na komputerze, który będzie używany do programowania, dodaj to do pliku ~/.bashrc pliku:

export DJANGO_DEVELOPMENT=true

Na dole twojego settings.py pliku dodaj następujące informacje.

# Override production variables if DJANGO_DEVELOPMENT env variable is set
if os.environ.get('DJANGO_DEVELOPMENT'):
    from settings_dev import *  # or specific overrides

(Pamiętaj, że *w Pythonie należy unikać importowania )

Domyślnie serwery produkcyjne niczego nie zastępują. Gotowe!

W porównaniu z innymi odpowiedziami, ta jest prostsza, ponieważ nie wymaga aktualizacji PYTHONPATHani ustawień, DJANGO_SETTINGS_MODULEktóre pozwalają na pracę tylko nad jednym projektem django na raz.

cs01
źródło
8
dlaczego to nie jest prawidłowa odpowiedź? SO to naprawdę bałagan w dzisiejszych czasach. Ty cs01
codyc4321
if os.environ.get('DJANGO_DEVELOPMENT', 'true')również działa. Wspominam o tym tylko dlatego, że powyższa is not truemetoda nie została zaimportowana dla mnie w Pythonie 3.6.
brt
1
@brt to zły pomysł: zawsze użyje twoich DEVustawień, które będą wyciekać prywatne dane na serwerze publicznym. Naprawdę chcesz tylko sprawdzić, czy DJANGO_DEVELOPMENTzmienna środowiskowa istnieje (tj is not None.).
cs01
Dzięki za informację @ cs01. Zdałem sobie sprawę, że zrobiłem coś nie tak, kiedy wysadziłem moją witrynę, ładując nieprawidłowe ustawienia, ale nie byłem pewien, dlaczego settings_dev.pyładuję się na serwerze.
brt
2
@ cs01 Posunąłbym się do tego, aby upewnić się, że istnieje i jest prawdą, po prostu oddając is not Noneczek. Również os.getenvjest skrótem
Tjorriemorrie,
35

Zwykle mam jeden plik ustawień na środowisko i wspólny plik ustawień:

/myproject/
  settings.production.py
  settings.development.py
  shared_settings.py

Każdy z moich plików środowiskowych ma:

try:
    from shared_settings import *
except ImportError:
    pass

Dzięki temu mogę w razie potrzeby zastąpić wspólne ustawienia (poprzez dodanie modyfikacji poniżej tej sekcji).

Następnie wybieram pliki ustawień do użycia, łącząc je z settings.py:

ln -s settings.development.py settings.py
Daniel Watkins
źródło
2
Jak sobie radzisz z zakazem pep8 import *? Czy wyłączysz ten czek? Zapakowałem ten import w plik, exec()ale nie mogę mieć warunków warunkowych dla zmiennych, które nie są zdefiniowane w tym pliku, ani nie mogę zmienić INSTALLED_APPSzmiennej, ponieważ jest „niezdefiniowana”
Michaił
11
Nie lintrujemy naszych plików ustawień, ponieważ tak naprawdę nie są one kodem, ale konfiguracją wyrażoną w Pythonie.
Daniel Watkins,
17

Oto jak robię to w 6 prostych krokach:

  1. Utwórz folder w katalogu projektu i nadaj mu nazwę settings.

    Struktura projektu:

    myproject/
           myapp1/
           myapp2/              
           myproject/
                  settings/
  2. Tworzenie cztery pliki Pythona wewnątrz settingskatalogu mianowicie __init__.py, base.py, dev.pyiprod.py

    Pliki ustawień:

    settings/
         __init__.py
         base.py
         prod.py
         dev.py 
  3. Otwórz __init__.pyi wypełnij następującą treścią:

    init .py:

    from .base import *
    # you need to set "myproject = 'prod'" as an environment variable
    # in your OS (on which your website is hosted)
    if os.environ['myproject'] == 'prod':
       from .prod import *
    else:
       from .dev import *
  4. Otwórz base.pyi wypełnij wszystkie typowe ustawienia (które będą używane zarówno w produkcji, jak i programowaniu). Na przykład:

    base.py:

    import os
    ...
    INSTALLED_APPS = [...]
    MIDDLEWARE = [...]
    TEMPLATES = [{...}]
    ...
    STATIC_URL = '/static/'
    STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
    MEDIA_ROOT = os.path.join(BASE_DIR, '/path/')
    MEDIA_URL = '/path/'
  5. Otwórz dev.pyi dołącz te rzeczy, które są specyficzne dla rozwoju, na przykład:

    dev.py:

    DEBUG = True
    ALLOWED_HOSTS = ['localhost']
    ...
  6. Otwórz prod.pyi dołącz te rzeczy, które są specyficzne dla produkcji, na przykład:

    prod.py:

    DEBUG = False
    ALLOWED_HOSTS = ['www.example.com']
    LOGGING = [...]
    ...
Ahtisham
źródło
10

Utwórz wiele settings*.pyplików, ekstrapolując zmienne, które muszą się zmieniać w zależności od środowiska. Następnie na końcu settings.pypliku głównego :

try:
  from settings_dev import *
except ImportError:
  pass

Zachowujesz oddzielne settings_*pliki dla każdego etapu.

U góry settings_dev.pypliku dodaj to:

import sys
globals().update(vars(sys.modules['settings']))

Aby zaimportować zmienne, które musisz zmodyfikować.

Ten wpis wiki zawiera więcej pomysłów na podział ustawień.

Burhan Khalid
źródło
Dzięki Burham! Podczas wdrażania aplikacji wystarczyłoby usunąć plik settings_dev, aby zobaczyć moje ustawienia wdrażania w akcji?
Kristian Roebuck
Tak, lub zamień import nasettings_prod.py
Burhan Khalid
1
Edycja pliku master settings.py we wdrożeniu oznacza, że ​​będzie to kolidować z kontrolą wersji, więc niekoniecznie jest to najlepsza droga naprzód. Powiedziałbym, że opcja Thomasa Orozco jest najlepsza - możesz ustawić DJANGO_SETTINGS_MODULE w swoim skrypcie po aktywacji virtualenv lub w konfiguracji gunicorn lub mod_wsgi
Steve Jalim
1
Być może należy wspomnieć, że nigdy nie dodajesz do kontroli źródła plików specyficznych dla etapu. Założyłem, że zrozumiano, że nie można przesuwać ustawień specyficznych dla etapu projektu.
Burhan Khalid
Jeśli używasz virtualenv, będzie to zazwyczaj domyślnie ustawione na {{project_name}}. Więc „ustawienia” nie będą kluczem w sys.modules. Będzie to „myproject.settings” (lub jakakolwiek inna nazwa twojego projektu). Możesz użyć, modname = "%s.settings" % ".".join(__name__.split('.')[:-1])aby uzyskać pełną nazwę modułu, a następnie globals().update(vars(sys.modules[modname])). Uważam, że dobrze mi to wychodzi. Oczywiście rezygnacja z programistycznego określania nazwy modułu na rzecz łańcucha prawdopodobnie zadziałaby w większości przypadków.
Eric
9

Używam niesamowitych konfiguracji django , a wszystkie ustawienia są przechowywane w moim settings.py:

from configurations import Configuration

class Base(Configuration):
    # all the base settings here...
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    ...

class Develop(Base):
    # development settings here...
    DEBUG = True 
    ...

class Production(Base):
    # production settings here...
    DEBUG = False

Aby skonfigurować projekt Django, po prostu postępowałem zgodnie z dokumentacją .

Riccardo Leschiutta
źródło
7

Oto podejście, którego używamy:

  • settingsmoduł do ustawień podzielone na wiele plików do odczytu;
  • .env.jsonplik do przechowywania poświadczeń i parametrów, które chcemy wykluczyć z naszego repozytorium git, lub które są specyficzne dla środowiska naturalnego;
  • env.pyplik do zapoznania się z .env.jsonpliku

Biorąc pod uwagę następującą strukturę:

...
.env.json           # the file containing all specific credentials and parameters
.gitignore          # the .gitignore file to exclude `.env.json`
project_name/       # project dir (the one which django-admin.py creates)
  accounts/         # project's apps
    __init__.py
    ...
  ...
  env.py            # the file to load credentials
  settings/
    __init__.py     # main settings file
    database.py     # database conf
    storage.py      # storage conf
    ...
venv                # virtualenv
...

Z .env.jsontakimi jak:

{
    "debug": false,
    "allowed_hosts": ["mydomain.com"],
    "django_secret_key": "my_very_long_secret_key",
    "db_password": "my_db_password",
    "db_name": "my_db_name",
    "db_user": "my_db_user",
    "db_host": "my_db_host",
}

Oraz project_name/env.py:

<!-- language: lang-python -->
import json
import os


def get_credentials():
    env_file_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    with open(os.path.join(env_file_dir, '.env.json'), 'r') as f:
        creds = json.loads(f.read())
    return creds


credentials = get_credentials()

Możemy mieć następujące ustawienia:

<!-- language: lang-py -->
# project_name/settings/__init__.py
from project_name.env import credentials
from project_name.settings.database import *
from project_name.settings.storage import *
...

SECRET_KEY = credentials.get('django_secret_key')

DEBUG = credentials.get('debug')

ALLOWED_HOSTS = credentials.get('allowed_hosts', [])

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    ...
]

if DEBUG:
    INSTALLED_APPS += ['debug_toolbar']

...

# project_name/settings/database.py
from project_name.env import credentials

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': credentials.get('db_name', ''),
        'USER': credentials.get('db_user', ''),
        'HOST': credentials.get('db_host', ''),
        'PASSWORD': credentials.get('db_password', ''),
        'PORT': '5432',
    }
}

zalety tego rozwiązania to:

  • poświadczenia i konfiguracje specyficzne dla użytkownika do lokalnego rozwoju bez modyfikowania repozytorium git;
  • konfiguracja specyficzna dla środowiska , możesz mieć na przykład trzy różne środowiska z trzema różnymi, .env.jsontakimi jak programowanie, tworzenie i produkcja;
  • poświadczenia nie znajdują się w repozytorium

Mam nadzieję, że to pomoże. Po prostu daj mi znać, jeśli zobaczysz jakieś zastrzeżenia dotyczące tego rozwiązania.

Charlesthk
źródło
zakładając, gdzie envma zastąpić dev, proditd.? Co znajduje się w starym settings.pypliku? Co jest storage.pyi database.py?
dbinott
Cześć @dbinott, możesz łatwo zaktualizować env.pyplik , abyś mógł wybrać, za pomocą zmiennej środowiskowej, który plik załadować
Charlesthk
Na przykład: conf = os.environ.get ('CONF', '') file_ = f ".env. {Conf} .json"
Charlesthk
Dlaczego miałbyś mieć json w przeciwieństwie do natywnego typu danych Pythona?
nalot
4

Używam następującej struktury plików:

project/
   ...
   settings/
   settings/common.py
   settings/local.py
   settings/prod.py
   settings/__init__.py -> local.py

Podobnie __init__.pyjest z linkiem (ln w unixie lub mklink w oknach) do local.pylub może być, prod.pywięc konfiguracja jest nadal w project.settingsmodule jest czysta i zorganizowana, a jeśli chcesz użyć określonej konfiguracji, możesz użyć zmiennej środowiskowej DJANGO_SETTINGS_MODULEdoproject.settings.prod jeśli trzeba aby uruchomić polecenie dla środowiska produkcyjnego.

W plikach prod.pyi local.py:

from .shared import *

DATABASE = {
    ...
}

i shared.pyplik pozostaje jako globalny bez określonych konfiguracji.

Felipe Buccioni
źródło
3

opierając się na odpowiedzi CS01:

jeśli masz problemy ze zmienną środowiskową, ustaw jej wartość na łańcuch (np. zrobiłem DJANGO_DEVELOPMENT="true").

Zmieniłem również przepływ pracy plików cs01 w następujący sposób:

#settings.py
import os
if os.environ.get('DJANGO_DEVELOPMENT') is not None:
    from settings_dev import * 
else:
    from settings_production import *
#settings_dev.py
development settings go here
#settings_production.py
production settings go here

W ten sposób Django nie musi czytać całego pliku ustawień przed uruchomieniem odpowiedniego pliku ustawień. To rozwiązanie jest przydatne, jeśli plik produkcyjny wymaga rzeczy, które znajdują się tylko na serwerze produkcyjnym.

Uwaga: w Pythonie 3 zaimportowane pliki muszą mieć .dołączony (np. from .settings_dev import *)

Brian Lee
źródło
1

Jeśli chcesz zachować 1 plik ustawień, a Twój programistyczny system operacyjny różni się od produkcyjnego, możesz umieścić go na dole pliku settings.py:

from sys import platform
if platform == "linux" or platform == "linux2":
    # linux
    # some special setting here for when I'm on my prod server
elif platform == "darwin":
    # OS X
    # some special setting here for when I'm developing on my mac
elif platform == "win32":
    # Windows...
    # some special setting here for when I'm developing on my pc

Czytaj więcej: Jak sprawdzić system operacyjny w Pythonie?

Użytkownik
źródło
1

Wydaje się, że odpowiedź na to pytanie została udzielona, ​​jednak metoda, której używam w połączeniu z kontrolą wersji, jest następująca:

Skonfiguruj plik env.py w tym samym katalogu, co ustawienia w moim lokalnym środowisku programistycznym, które również dodaję do .gitignore:

env.py:

#!usr/bin/python

DJANGO_ENV = True
ALLOWED_HOSTS = ['127.0.0.1', 'dev.mywebsite.com']

.gitignore:

mywebsite/env.py

settings.py:

if os.path.exists(os.getcwd() + '/env.py'):
    #env.py is excluded using the .gitignore file - when moving to production we can automatically set debug mode to off:
    from env import *
else:
    DJANGO_ENV = False

DEBUG = DJANGO_ENV

Po prostu uważam, że to działa i jest znacznie bardziej eleganckie - dzięki env.py łatwo jest zobaczyć nasze lokalne zmienne środowiskowe i możemy sobie z tym wszystkim poradzić bez wielu plików settings.py lub podobnych. Ta metoda pozwala na użycie wszelkiego rodzaju lokalnych zmiennych środowiskowych, których nie chcielibyśmy ustawić na naszym serwerze produkcyjnym. Wykorzystując .gitignore poprzez kontrolę wersji, zapewniamy również płynną integrację wszystkiego.


źródło
Najprostsze rozwiązanie. Można również zdefiniować wszystko w Configklasie wewnątrz env.pypliku. Następnie zamiast import *modułu można zaimportować plik from env import Config. W ten sposób nie musisz też tego używać, jeśli os.pathsprawdzisz, co znacznie upraszcza całą sprawę.
Siddharth Pant
0

Użyj settings.pydo produkcji. W tym samym katalogu utwórz settings_dev.pydla przesłonięć.

# settings_dev.py

from .settings import * 

DEBUG = False

Na maszynie deweloperskiej uruchom aplikację Django z:

DJANGO_SETTINGS_MODULE=<your_app_name>.settings_dev python3 manage.py runserver

Na maszynie do produkcji działaj tak, jakbyś właśnie miał settings.pyi nic więcej.

ZALETY

  1. settings.py (używany do produkcji) jest całkowicie niezależny od faktu, że istnieją jakiekolwiek inne środowiska.
  2. Aby zobaczyć różnicę między produkcją a dev, wystarczy spojrzeć na jedno miejsce - settings_dev.py. Nie ma potrzeby, aby zebrać konfiguracje rozproszone settings_prod.py, settings_dev.pyasettings_shared.py .
  3. Jeśli ktoś doda ustawienie do konfiguracji produktu po rozwiązaniu problemu z produkcją, możesz mieć pewność, że pojawi się ono również w konfiguracji deweloperskiej (chyba że zostanie wyraźnie zastąpione). W ten sposób rozbieżność między różnymi plikami konfiguracyjnymi zostanie zminimalizowana.
Alex Yursha
źródło
0

W przypadku problemu z ustawieniami plików wybieram kopiowanie

Project
   |---__init__.py   [ write code to copy setting file from subdir to current dir]
   |---settings.py  (do not commit this file to git)
   |---setting1_dir
   |         |--  settings.py
   |---setting2_dir
   |         |--  settings.py

Kiedy uruchomisz django, zostanie uruchomiony __init__py. W tej chwili settings.py in setting1_dirwymieni settings.py in Project.

Jak wybrać inne środowisko?

  • modyfikować __init__.pybezpośrednio.
  • zrobić plik bash do modyfikacji __init__.py.
  • zmodyfikuj env w Linuksie, a następnie __init__.pyprzeczytajmy tę zmienną.

Po co używać w ten sposób?

Ponieważ nie lubię tak wielu plików w tym samym katalogu, zbyt wiele plików zmyli innych partnerów i niezbyt dobrze dla IDE. (IDE nie może znaleźć pliku, którego używamy)

Jeśli nie chcesz widzieć tych wszystkich szczegółów, możesz podzielić projekt na dwie części.

  1. stwórz swoje małe narzędzie, takie jak Spring Initializr, tylko do konfiguracji projektu. (zrób coś jak kopiuj plik)
  2. Twój kod projektu
kyakya
źródło
0

Używam innego pliku app.yaml do zmiany konfiguracji między środowiskami w silniku aplikacji w chmurze Google.

Możesz użyć tego do utworzenia połączenia proxy w poleceniu terminala:

./cloud_sql_proxy -instances=<INSTANCE_CONNECTION_NAME>=tcp:1433

https://cloud.google.com/sql/docs/sqlserver/connect-admin-proxy#macos-64-bit

Plik: app.yaml

# [START django_app]
service: development
runtime: python37

env_variables:
  DJANGO_DB_HOST: '/cloudsql/myproject:myregion:myinstance'
  DJANGO_DEBUG: True

handlers:
# This configures Google App Engine to serve the files in the app's static
# directory.
- url: /static
  static_dir: static/

# This handler routes all requests not caught above to your main app. It is
# required when static routes are defined, but can be omitted (along with
# the entire handlers section) when there are no static files defined.
- url: /.*
  script: auto
# [END django_app]
Rodrigo Grossi
źródło
-1

To jest moje rozwiązanie z różnymi środowiskami do tworzenia, testowania i produkowania

import socket

[...]

DEV_PC = 'PC059'
host_name = socket.gethostname()

if host_name == DEV_PC:
   #do something
   pass
elif [...]
Massimo Variolo
źródło