Bagaimana mengelola pengaturan lokal vs produksi di Django?

298

Apa cara yang disarankan untuk menangani pengaturan untuk pengembangan lokal dan server produksi? Beberapa dari mereka (seperti konstanta, dll) dapat diubah / diakses di keduanya, tetapi beberapa dari mereka (seperti path ke file statis) harus tetap berbeda, dan karenanya tidak boleh ditimpa setiap kali kode baru digunakan.

Saat ini, saya menambahkan semua konstanta ke settings.py. Tetapi setiap kali saya mengubah konstanta secara lokal, saya harus menyalinnya ke server produksi dan mengedit file untuk perubahan spesifik produksi ... :(

Sunting: sepertinya tidak ada jawaban standar untuk pertanyaan ini, saya telah menerima metode yang paling populer.

akv
sumber
Silakan lihat di django-configuration .
JJD
2
Metode yang diterima tidak lagi yang paling populer.
Daniel
2
django-split-settings sangat mudah digunakan. Tidak perlu menulis ulang pengaturan default.
sobolevn
Anda harus menggunakan file base.py dan di local.py Anda "dari .base import *", sama dengan production.py Anda "dari .base import *", Anda perlu menjalankan proyek Anda dengan: python manage.py runserver - settings = project_name.settings.local
Roberth Solís

Jawaban:

127

Dalam settings.py:

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

Anda dapat mengganti apa yang dibutuhkan di local_settings.py; itu harus tetap di luar kendali versi Anda kemudian. Tapi karena Anda menyebutkan menyalin, saya kira Anda tidak menggunakannya;)

Oh tidak
sumber
3
Untuk memudahkan pelacakan / penyebaran pengaturan baru, gunakan "local_settings.py" pada mesin produksi / pengujian dan tidak ada pada pengembangan.
John Mee
8
Itulah yang saya lakukan - menambahkan garis-garis itu di akhir settings.py sehingga mereka dapat menimpa pengaturan default
daonb
61
Pendekatan ini berarti Anda memiliki kode tidak berversi yang berjalan dalam pengembangan dan produksi. Dan setiap pengembang memiliki basis kode yang berbeda. Saya menyebutnya anti-pola di sini.
pydanny
8
@ pydanny Masalahnya adalah Django menyimpan konfigurasinya dalam file .py. Anda tidak dapat mengharapkan bahwa semua pengembang dan server produksi akan menggunakan pengaturan yang sama, jadi Anda perlu mengubah file .py ini atau menerapkan beberapa solusi alternatif (file .ini, lingkungan dll.).
Tupteq
3
Saya lebih suka memanggil modul settings_localdaripada local_settingsmengelompokkannya settings.pydalam daftar folder alfabet. Jauhkan settings_local.pydari kontrol versi menggunakan .gitignorekarena kredensial bukan milik Git. Bayangkan open source mereka secara tidak sengaja. Saya menyimpan git file template yang disebut settings_local.py.txtsebagai gantinya.
fmalina
297

Dua sendok Django: Praktik Terbaik untuk Django 1.5 menyarankan menggunakan kontrol versi untuk file pengaturan Anda dan menyimpan file dalam direktori terpisah:

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

The base.pyfile berisi pengaturan umum (seperti MEDIA_ROOT atau ADMIN), sementara local.pydan production.pymemiliki pengaturan spesifik:

Dalam file dasar settings/base.py:

INSTALLED_APPS = (
    # common apps...
)

Dalam file pengaturan pengembangan lokal settings/local.py:

from project.settings.base import *

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

Dalam file pengaturan produksi file settings/production.py:

from project.settings.base import *

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

Kemudian ketika Anda menjalankan Django, Anda menambahkan --settingsopsi:

# 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

Para penulis buku juga telah memasang contoh template tata letak proyek di Github.

gene_wood
sumber
62
Perhatikan bahwa alih-alih menggunakan --settingssetiap waktu, Anda dapat mengatur DJANGO_SETTINGS_MODULEenvvar. Ini berfungsi baik dengan, misalnya, Heroku: mengaturnya secara global untuk produksi, lalu menimpanya dengan dev di file .env Anda.
Simon Weber
9
Menggunakan DJANGO_SETTINGS_MODULEenv var adalah ide terbaik di sini, terima kasih Simon.
kibibu
20
Anda mungkin perlu mengubah BASE_DIRpengaturan keos.path.dirname(os.path.realpath(os.path.dirname(__file__) + "/.."))
Petr Peller
5
@rsp menurut dokumen Django, Anda mengimpor from django.conf import settingsyang merupakan objek yang abstrak antarmuka dan memisahkan kode dari lokasi pengaturan, docs.djangoproject.com/en/dev/topics/settings/…
3
Jika saya mengatur DJANGO_SETTINGS_MODULE melalui variabel lingkungan, apakah saya masih memerlukan os.environ.setdefault ("DJANGO_SETTINGS_MODULE", "projectname.settings.production") di file wsgi.py saya? Juga, saya telah mengatur var lingkungan menggunakan: export DJANGO_SETTINGS_MODULE = projectname.settings.local, tetapi kemudian hilang ketika saya menutup terminal. Apa yang bisa saya lakukan untuk memastikan itu disimpan? Haruskah saya menambahkan baris itu ke file bashrc?
Kritz
71

Alih-alih settings.py, gunakan tata letak ini:

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

common.py adalah tempat sebagian besar konfigurasi Anda.

prod.py mengimpor semuanya dari yang umum, dan menimpa apa pun yang perlu ditimpa:

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

# Production overrides
DEBUG = False
#...

Demikian pula, dev.pymengimpor segala sesuatu dari common.pydan menimpa apa pun yang perlu ditimpa.

Terakhir, __init__.pyadalah tempat Anda memutuskan pengaturan mana yang akan dimuat, dan itu juga tempat Anda menyimpan rahasia (karena itu file ini tidak boleh diversi):

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..."

Apa yang saya sukai dari solusi ini adalah:

  1. Semuanya ada di sistem versi Anda, kecuali rahasia
  2. Kebanyakan konfigurasi di satu tempat: common.py.
  3. Hal-hal khusus prod masuk prod.py, hal-hal khusus dev masuk dev.py. Itu mudah.
  4. Anda dapat menimpa barang dari common.pydalam prod.pyatau dev.py, dan Anda dapat menimpa apa pun di __init__.py.
  5. Ini python langsung. Tidak perlu mengimpor kembali peretasan.
MiniQuark
sumber
2
Saya masih mencoba mencari tahu apa yang harus ditetapkan dalam project.wsgi saya dan mengelola file .py untuk file pengaturan. Apakah Anda akan menjelaskan ini? Secara khusus, dalam file manage.py saya, saya memiliki os.environ.setdefault("DJANGO_SETTINGS_MODULE", "foobar.settings")foobar adalah folder dengan __init__.pyfile dan pengaturan adalah folder dengan __init__.pyfile yang berisi rahasia saya dan impor dev.py, yang kemudian mengimpor common.py. EDIT Nevermind, saya tidak memiliki modul yang diinstal yang diperlukan. Salahku! Ini sangat bagus !!
Teewuane
5
Dua hal: 1) lebih baik untuk menetapkan Debug = Benar di dev.py Anda daripada = Salah di prod.py Anda 2) Daripada beralih init .py, beralih menggunakan var lingkungan DJANGO_SETTINGS_MODULE. Ini akan membantu penyebaran PAAS (mis. Heroku).
Rob Grant
Ketika saya menggunakan pengaturan ini di django 1.8.4 dan mencoba runserver saya mendapatkan "django.core.exceptions.ImproperlyConfigured: Pengaturan SECRET_KEY tidak boleh kosong.", Bahkan apakah saya punya SECRET_KEY pada file init .py saya . Apakah saya melewatkan sesuatu?
polarcare
bukankah penggunaan sesuatu seperti AWS_SECRET_ACCESS_KEY = os.getenv ("AWS_SECRET_ACCESS_KEY") lebih aman? Pertanyaan jujur ​​- Saya tahu mengapa Anda tidak menginginkannya berversi, tetapi alternatif lain adalah mendapatkannya dari lingkungan. Yang menimbulkan pertanyaan pengaturan variabel lingkungan, tentu saja, tetapi itu bisa diserahkan pada mekanisme penyebaran Anda, bukan?
JL Peyret
20

Saya menggunakan versi sedikit modifikasi dari gaya "jika DEBUG" pengaturan yang diposting Harper Shelby. Jelas tergantung pada lingkungan (win / linux / dll.) Kode mungkin perlu sedikit di-tweak.

Saya dulu menggunakan "jika DEBUG" tetapi saya menemukan bahwa kadang-kadang saya perlu melakukan pengujian dengan DEUBG diatur ke False. Apa yang benar-benar ingin saya bedakan jika lingkungannya adalah produksi atau pengembangan, yang memberi saya kebebasan untuk memilih level 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'

Saya masih menganggap cara pengaturan ini sedang berjalan. Saya belum melihat satu cara untuk menangani pengaturan Django yang mencakup semua pangkalan dan pada saat yang sama tidak merepotkan total untuk setup (saya tidak kecewa dengan metode file pengaturan 5x).

T. Stone
sumber
Ini adalah hal yang pengaturan Django menjadi file kode aktual memungkinkan, dan saya mengisyaratkan. Saya sendiri belum pernah melakukan hal seperti ini, tetapi ini jelas merupakan solusi yang mungkin merupakan jawaban umum yang lebih baik daripada jawaban saya.
Harper Shelby
3
Saya baru saja menemukan ini untuk pertama kalinya dan memilih untuk (berhasil!) Menggunakan solusi Anda, dengan sedikit perbedaan: Saya menggunakan uuid.getnode () untuk menemukan uuid dari sistem saya. Jadi saya sedang menguji apakah uuid.getnode () == 12345678901 (sebenarnya nomor yang berbeda) daripada tes os.environ yang Anda gunakan. Saya tidak dapat menemukan dokumentasi untuk meyakinkan saya bahwa os.environ ['COMPUTERNAME'] unik untuk setiap komputer.
Joe Golton
os.environ ['COMPUTERNAME'] tidak berfungsi di Amazon AWS Ubuntu. Saya mendapatkan KeyError.
nu everest
Saat menggunakan UUID, solusi ini telah terbukti sebagai yang terbaik dan paling sederhana bagi saya. Itu tidak membutuhkan banyak tambal sulam rumit dan over-modularized Dalam lingkungan produksi, Anda masih perlu menempatkan kata sandi basis data dan SECRET_KEY dalam file terpisah yang berada di luar kendali versi.
nu everest
os.environ['COMPUTERNAME']sayangnya tidak berfungsi di PythonAnywhere. Anda mendapatkan KeyError.
nbeuchat
14

Saya menggunakan settings_local.py dan a settings_production.py. Setelah mencoba beberapa opsi, saya menemukan bahwa membuang-buang waktu dengan solusi yang kompleks menjadi mudah ketika memiliki dua file pengaturan terasa mudah dan cepat.

Ketika Anda menggunakan mod_python / mod_wsgi untuk proyek Django Anda, Anda perlu mengarahkannya ke file pengaturan Anda. Jika Anda mengarahkannya ke app / settings_local.py di server lokal Anda dan app / settings_production.py di server produksi Anda maka hidup menjadi mudah. Cukup edit file pengaturan yang sesuai dan mulai ulang server (server pengembangan Django akan memulai ulang secara otomatis).

Kai
sumber
2
Dan bagaimana dengan server pengembangan lokal? apakah ada cara untuk memberitahu server web Django (jalankan menggunakan python manage.py runserver), file pengaturan mana yang digunakan?
akv
2
@ akv jika Anda menambahkan --settings = [nama modul] (tanpa ekstensi .py) ke akhir perintah runserver, Anda dapat menentukan file pengaturan mana yang akan digunakan. Jika Anda akan melakukannya, bantulah diri Anda sendiri dan buat skrip shell / file batch dengan pengaturan pengembangan yang dikonfigurasikan. Percayalah, jari-jari Anda akan berterima kasih.
T. Stone
ini solusi yang saya gunakan. meretas file pengaturan yang akan digunakan untuk produksi atau pengembangan berantakan
27880 George Godik
4
Saya pikir lebih baik menggunakan settings.py dalam pengembangan, karena Anda tidak harus menentukannya setiap saat.
Andre Bossard
Apakah saya benar mengasumsikan metode ini memerlukan impor modul pengaturan melalui proxy, django.conf.settings? Kalau tidak, Anda perlu mengedit deklarasi impor untuk menunjuk pada file pengaturan yang benar ketika mendorong langsung.
Groady
8

TL; DR: Kuncinya adalah memodifikasi os.environmentsebelum Anda mengimpor settings/base.pyapa pun settings/<purpose>.py, ini akan sangat menyederhanakan hal-hal.


Hanya memikirkan semua file yang saling terkait ini membuat saya sakit kepala. Menggabungkan, mengimpor (kadang-kadang kondisional), mengesampingkan, menambal apa yang sudah diatur dalam DEBUGpengaturan kasus berubah nanti. Sungguh mimpi buruk!

Selama bertahun-tahun saya mengalami semua solusi yang berbeda. Mereka semua agak pekerjaan, tapi begitu menyakitkan untuk mengelola. WTF! Apakah kita benar-benar membutuhkan semua kerumitan itu? Kami mulai dengan hanya satu settings.pyfile. Sekarang kita membutuhkan dokumentasi hanya untuk menggabungkan semua ini dengan benar dalam urutan yang benar!

Saya harap saya akhirnya mencapai sweet spot (saya) dengan solusi di bawah ini.

Mari kita rekap tujuan (beberapa umum, beberapa milikku)

  1. Jaga kerahasiaan rahasia - jangan menyimpannya di repo!

  2. Atur / baca kunci dan rahasia melalui pengaturan lingkungan, gaya 12 faktor .

  3. Memiliki default fallback yang masuk akal. Idealnya untuk pengembangan lokal Anda tidak perlu apa-apa selain standar.

  4. ... tetapi cobalah untuk menjaga produksi default tetap aman. Lebih baik melewatkan pengaturan yang ditimpa secara lokal, daripada harus mengingat untuk menyesuaikan pengaturan default yang aman untuk produksi.

  5. Memiliki kemampuan untuk menghidupkan DEBUG/ mematikan dengan cara yang dapat memiliki efek pada pengaturan lain (mis. Menggunakan javascript terkompresi atau tidak).

  6. Beralih antara pengaturan tujuan, seperti lokal / pengujian / pementasan / produksi, harus didasarkan hanya pada DJANGO_SETTINGS_MODULE, tidak lebih.

  7. ... tetapi memungkinkan parameterisasi lebih lanjut melalui pengaturan lingkungan seperti DATABASE_URL.

  8. ... juga memungkinkan mereka untuk menggunakan pengaturan tujuan yang berbeda dan menjalankannya secara lokal berdampingan, misalnya. pengaturan produksi pada mesin pengembang lokal, untuk mengakses basis data produksi atau menguji style sheet uji asap.

  9. Gagal jika variabel lingkungan tidak ditetapkan secara eksplisit (membutuhkan nilai kosong minimum), terutama dalam produksi, misalnya. EMAIL_HOST_PASSWORD.

  10. Menanggapi pengaturan default DJANGO_SETTINGS_MODULEdi manage.py selama django-admin startproject

  11. Jauhkan conditional untuk minimum, jika kondisi yang jenis lingkungan bertujuan (misalnya. Untuk produksi set file log dan rotasi itu), menggantikan pengaturan dalam terkait pengaturan file bertujuan.

Jangan lakukan itu

  1. Jangan biarkan Django membaca pengaturan DJANGO_SETTINGS_MODULE membentuk file.
    Ugh! Pikirkan bagaimana meta ini. Jika Anda perlu memiliki file (seperti docker env) baca itu ke lingkungan sebelum menatap proses Django.

  2. Jangan menimpa DJANGO_SETTINGS_MODULE dalam kode proyek / aplikasi Anda, mis. berdasarkan nama host atau nama proses.
    Jika Anda malas untuk mengatur variabel lingkungan (seperti untuk setup.py test) lakukan di tooling sebelum Anda menjalankan kode proyek Anda.

  3. Hindari sihir dan menambal tentang bagaimana Django membaca pengaturan itu, preprocess pengaturan tetapi tidak mengganggu sesudahnya.

  4. Tidak ada omong kosong berbasis logika yang rumit. Konfigurasi harus diperbaiki dan terwujud tidak dihitung dengan cepat. Memberikan default mundur adalah logika yang cukup di sini.
    Apakah Anda benar-benar ingin melakukan debug, mengapa secara lokal Anda memiliki set pengaturan yang benar tetapi dalam produksi pada server jauh, pada satu dari seratus mesin, sesuatu dikomputasi secara berbeda? Oh! Tes unit? Untuk pengaturan? Serius?

Larutan

Strategi saya terdiri dari django-environment luar biasa yang digunakan dengan inifile gaya, memberikan os.environmentstandar untuk pengembangan lokal, beberapa settings/<purpose>.pyfile minimal dan pendek yang memiliki import settings/base.py SETELAH yang os.environmentditetapkan dari INIfile. Ini secara efektif memberi kita semacam pengaturan injeksi.

Kuncinya di sini adalah memodifikasi os.environmentsebelum Anda mengimpor settings/base.py.

Untuk melihat contoh lengkapnya, lakukan repo: 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

pengaturan / .env

Default untuk pengembangan lokal. File rahasia, sebagian besar untuk mengatur variabel lingkungan yang diperlukan. Tetapkan nilai kosong jika tidak diperlukan dalam pengembangan lokal. Kami memberikan standar di sini dan tidak settings/base.pygagal pada mesin lain jika mereka hilang dari lingkungan.

pengaturan / local.py

Apa yang terjadi di sini, adalah memuat lingkungan dari settings/.env, lalu mengimpor pengaturan umum dari settings/base.py. Setelah itu kita dapat mengganti beberapa untuk memudahkan pembangunan lokal.

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

pengaturan / production.py

Untuk produksi, kita seharusnya tidak mengharapkan file lingkungan, tetapi lebih mudah memilikinya jika kita menguji sesuatu. Tapi bagaimanapun, jangan sampai memberikan beberapa default inline, sehingga settings/base.pydapat merespons sesuai.

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

Poin utama yang menarik di sini adalah DEBUGdan ASSETS_DEBUGmenimpa, mereka akan diterapkan pada python os.environSAJA jika mereka HILANG dari lingkungan dan file.

Ini akan menjadi default produksi kami, tidak perlu menempatkannya di lingkungan atau file, tetapi mereka dapat diganti jika diperlukan. Rapi!

pengaturan / base.py

Ini sebagian besar pengaturan vanilla django Anda, dengan beberapa persyaratan dan banyak membacanya dari lingkungan. Hampir semuanya ada di sini, menjaga semua lingkungan yang dituju konsisten dan semirip mungkin.

Perbedaan utama di bawah ini (saya harap ini cukup jelas):

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',)

Bit terakhir menunjukkan kekuatan di sini. ASSETS_DEBUGmemiliki default yang masuk akal, yang dapat ditimpa settings/production.pydan bahkan yang dapat ditimpa oleh pengaturan lingkungan! Yay!

Akibatnya, kami memiliki hierarki kepentingan campuran:

  1. settings / .py - menetapkan standar berdasarkan tujuan, tidak menyimpan rahasia
  2. settings / base.py - sebagian besar dikendalikan oleh lingkungan
  3. pengaturan lingkungan proses - 12 faktor bayi!
  4. settings / .env - default lokal untuk startup yang mudah
Janusz Skonieczny
sumber
Hai Janusz ... jadi dalam file .env akan pergi semua kunci API dan kunci auth dan kata sandi dll? Sama seperti TWILLIO_API = "abc123"? Atau TWILLIO_API = env ("TWILLIO_API")?
dbinott
Ya, tapi ini hanya mundur untuk pengaturan lingkungan. File ini sangat berguna untuk pengembangan tetapi tidak disimpan dalam repo atau didorong ke produksi di mana Anda harus menggunakan pengaturan lingkungan atau platform Anda yang ketat yang pada gilirannya akan menetapkan pengaturan lingkungan untuk proses server.
Janusz Skonieczny
7

Saya mengelola konfigurasi saya dengan bantuan pengaturan-split-Django .

Ini adalah pengganti drop-in untuk pengaturan default. Ini sederhana, namun dapat dikonfigurasi. Dan refactoring dari pengaturan Anda yang ada tidak diperlukan.

Berikut ini contoh kecil (file 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()
    )

Itu dia.

Memperbarui

Saya menulis posting blog tentang mengelola djangopengaturan dengan django-split-sttings. Silahkan lihat!

sobolevn
sumber
1
Saya mencoba itu .. berlari ke dinding setelah saya mencoba menjalankan tes unit Django saya .. saya hanya tidak tahu bagaimana menentukan file pengaturan mana yang harus dibaca
abbood
Saya telah membuat intisari untuk Anda: gist.github.com/sobolevn/006c734f0520439a4b6c16891d65406c
sobolevn
saya mendapatkan sesuatu seperti ini dalam kode saya, jadi saya memeriksa pengaturan. Flag DUG untuk tahu apakah saya ingin mengimpor barang .. bendera itu selalu disetel ke false dalam tes unit Django (lihat di sini ) sehingga pekerjaan saya adalah menimpanya di setiap tes seperti begitu
abbood
berikut adalah pertanyaan lain: uwsgi.inifile saya memiliki pengaturan berbeda di dev / prod .. ada ide bagaimana membuatnya mengambil nilai dari file pengaturan saya?
abbood
maaf, saya tidak mendapatkan pengaturan. Anda dapat mengajukan pertanyaan terpisah dengan lebih detail dan saya akan mencoba membantu Anda.
sobolevn
6

Masalah dengan sebagian besar solusi ini adalah Anda menerapkan pengaturan lokal sebelum yang umum, atau setelahnya .

Jadi tidak mungkin menimpa hal-hal seperti

  • pengaturan env-spesifik menentukan alamat untuk kumpulan memcached, dan dalam file pengaturan utama nilai ini digunakan untuk mengkonfigurasi backend cache
  • pengaturan khusus env menambah atau menghapus aplikasi / middleware ke yang default

pada waktu bersamaan.

Salah satu solusi dapat diimplementasikan menggunakan file konfigurasi gaya "ini" dengan kelas ConfigParser. Ini mendukung banyak file, interpolasi string malas, nilai default dan banyak barang lainnya. Setelah sejumlah file dimuat, lebih banyak file dapat dimuat dan nilainya akan menimpa yang sebelumnya, jika ada.

Anda memuat satu atau lebih file konfigurasi, tergantung pada alamat mesin, variabel lingkungan dan bahkan nilai dalam file konfigurasi yang dimuat sebelumnya. Kemudian Anda hanya menggunakan nilai yang diurai untuk mengisi pengaturan.

Salah satu strategi yang berhasil saya gunakan adalah:

  • Muat defaults.inifile default
  • Periksa nama mesin, dan muat semua file yang cocok dengan FQDN terbalik, dari kecocokan terpendek ke kecocokan terpanjang (jadi, saya memuat net.ini, lalu net.domain.ini, kemudian net.domain.webserver01.ini, masing-masing mungkin mengesampingkan nilai-nilai sebelumnya). Akun ini juga untuk mesin pengembang, sehingga masing-masing dapat mengatur driver basis data yang disukai, dll untuk pengembangan lokal
  • Periksa apakah ada "nama cluster" yang dideklarasikan, dan dalam hal ini memuat cluster.cluster_name.ini, yang dapat menentukan hal-hal seperti basis data dan IP cache

Sebagai contoh dari sesuatu yang dapat Anda capai dengan ini, Anda dapat mendefinisikan nilai "subdomain" per-env, yang kemudian digunakan dalam pengaturan default (as hostname: %(subdomain).whatever.net) untuk mendefinisikan semua nama host dan cookie yang diperlukan hal-hal yang perlu dilakukan oleh Django.

Ini seperti KERING yang bisa saya dapatkan, kebanyakan file (yang sudah ada) hanya memiliki 3 atau 4 pengaturan. Selain itu, saya harus mengelola konfigurasi pelanggan, jadi ada satu set file konfigurasi tambahan (dengan hal-hal seperti nama basis data, pengguna dan kata sandi, subdomain yang ditugaskan, dll.) Ada, satu atau lebih per pelanggan.

Satu dapat mengatur skala ini sebagai rendah atau setinggi yang diperlukan, Anda hanya memasukkan file konfigurasi kunci yang ingin Anda konfigurasi per-lingkungan, dan begitu ada kebutuhan untuk konfigurasi baru, masukkan nilai sebelumnya dalam konfigurasi default, dan menimpanya jika perlu.

Sistem ini telah terbukti andal dan berfungsi baik dengan kontrol versi. Ini telah digunakan untuk waktu yang lama mengelola dua kelompok aplikasi yang terpisah (15 contoh situs django atau lebih yang terpisah per mesin), dengan lebih dari 50 pelanggan, di mana kelompok-kelompok itu berubah ukuran dan anggota tergantung pada suasana sysadmin. .

ditulis ulang
sumber
1
Apakah Anda memiliki contoh bagaimana Anda memuat pengaturan dari ini ke pengaturan Django?
kaleissin
Lihat docs.python.org/2/library/configparser.html . Anda dapat memuat parser dengan config = ConfigParser.ConfigParser() lalu membaca file Anda config.read(array_of_filenames)dan mendapatkan nilai menggunakan config.get(section, option). Jadi pertama-tama Anda memuat konfigurasi Anda, dan kemudian Anda menggunakannya untuk membaca nilai pengaturan.
ditulis ulang
5

Saya juga bekerja dengan Laravel dan saya suka implementasinya di sana. Saya mencoba meniru dan menggabungkannya dengan solusi yang diajukan oleh T. Stone (lihat di atas):

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

Mungkin sesuatu seperti ini akan membantu Anda.

Robert Kuzma
sumber
4

Ingat bahwa settings.py adalah file kode langsung. Dengan asumsi bahwa Anda tidak memiliki DEBUG ditetapkan pada produksi (yang merupakan praktik terbaik), Anda dapat melakukan sesuatu seperti:

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

Cukup mendasar, tetapi secara teori, Anda bisa naik ke tingkat kompleksitas apa pun hanya berdasarkan nilai DEBUG - atau variabel atau kode lain yang ingin Anda gunakan.

Harper Shelby
sumber
4

Untuk sebagian besar proyek saya, saya menggunakan pola berikut:

  1. Buat settings_base.py di mana saya menyimpan pengaturan yang umum untuk semua lingkungan
  2. Setiap kali saya perlu menggunakan lingkungan baru dengan persyaratan khusus saya membuat file pengaturan baru (mis. Settings_local.py) yang mewarisi konten settings_base.py dan menimpa / menambahkan variabel pengaturan yang tepat ( from settings_base import *)

(Untuk menjalankan manage.py dengan pengaturan kustom file yang Anda hanya menggunakan opsi perintah --settings: manage.py <command> --settings=settings_you_wish_to_use.py)

dzida
sumber
3

Solusi saya untuk masalah itu juga agak campuran dari beberapa solusi yang telah dinyatakan di sini:

  • Saya menyimpan file bernama local_settings.pyyang memiliki konten USING_LOCAL = Truedi dev dan USING_LOCAL = Falsedi prod
  • Di settings.pysaya lakukan impor pada file itu untuk mendapatkan USING_LOCALpengaturan

Saya kemudian mendasarkan semua pengaturan yang bergantung pada lingkungan saya pada yang itu:

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

Saya lebih suka ini memiliki dua file settings.py terpisah yang perlu saya pertahankan karena saya dapat membuat pengaturan saya terstruktur dalam satu file lebih mudah daripada memiliki mereka menyebar di beberapa file. Seperti ini, ketika saya memperbarui pengaturan saya tidak lupa melakukannya untuk kedua lingkungan.

Tentu saja setiap metode memiliki kekurangan dan yang satu ini tidak terkecuali. Masalahnya di sini adalah bahwa saya tidak bisa menimpa local_settings.pyfile setiap kali saya mendorong perubahan saya menjadi produksi, yang berarti saya tidak bisa hanya menyalin semua file secara membabi buta, tetapi itu adalah sesuatu yang bisa saya jalani.

Miguel Ventura
sumber
3

Saya menggunakan variasi dari apa yang disebutkan jpartogi di atas, yang saya temukan sedikit lebih pendek:

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)

Pada dasarnya di setiap komputer (pengembangan atau produksi) saya memiliki file hostname_settings.py yang sesuai yang akan dimuat secara dinamis.

Stratosgear
sumber
3

Ada juga Pengaturan Classy Django. Saya pribadi penggemar beratnya. Ini dibangun oleh salah satu orang paling aktif di Django IRC. Anda akan menggunakan vars lingkungan untuk mengatur hal-hal.

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

SudoKid
sumber
3

1 - Buat folder baru di dalam aplikasi Anda dan pengaturan nama untuk itu.

2 - Sekarang buat __init__.pyfile baru di dalamnya dan tulis di dalamnya

from .base import *

try:
    from .local import *
except:
    pass

try:
    from .production import *
except:
    pass

3 - Buat tiga file baru dalam nama folder pengaturan local.pydan production.pydan base.py.

4 - Di dalam base.py, salin semua konten settings.pyfolder sebelumnya dan ganti namanya dengan sesuatu yang berbeda, katakanlah old_settings.py.

5 - Di base.py ubah jalur BASE_DIR Anda untuk mengarahkan ke jalur pengaturan baru Anda

Jalan lama-> BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

Jalur baru -> BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

Dengan cara ini, dir proyek dapat terstruktur dan dapat dikelola di antara produksi dan pengembangan lokal.

Jack Ryan
sumber
2

Untuk menggunakan settingskonfigurasi yang berbeda pada lingkungan yang berbeda, buat file pengaturan yang berbeda. Dan dalam skrip penerapan Anda, mulai server menggunakan --settings=<my-settings.py>parameter, di mana Anda dapat menggunakan pengaturan berbeda di lingkungan yang berbeda.

Manfaat menggunakan pendekatan ini :

  1. Pengaturan Anda akan modular berdasarkan pada setiap lingkungan

  2. Anda dapat mengimpor yang master_settings.pyberisi konfigurasi basis di environmnet_configuration.pydan menimpa nilai yang ingin Anda ubah di lingkungan itu.

  3. Jika Anda memiliki tim yang sangat besar, setiap pengembang dapat memiliki timnya sendiri local_settings.pyyang dapat mereka tambahkan ke repositori kode tanpa risiko memodifikasi konfigurasi server. Anda dapat menambahkan pengaturan lokal ini ke .gitnorejika Anda menggunakan git atau .hginorejika Anda Mercurial untuk Kontrol Versi (atau yang lain). Dengan begitu pengaturan lokal bahkan tidak akan menjadi bagian dari basis kode aktual yang menjaganya tetap bersih.

Moinuddin Quadri
sumber
2

Pengaturan saya terpecah sebagai berikut

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

Kami memiliki 3 lingkungan

  • dev
  • pementasan
  • produksi

Sekarang jelas pementasan dan produksi harus memiliki lingkungan serupa yang semaksimal mungkin. Jadi kami menyimpan prod.pykeduanya.

Tetapi ada kasus di mana saya harus mengidentifikasi server yang sedang berjalan adalah server produksi. @T. Jawaban Stone membantu saya menulis cek sebagai berikut.

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
sumber
1

Saya membedakannya di manage.py dan membuat dua file pengaturan terpisah: local_settings.py dan prod_settings.py.

Di manage.py saya memeriksa apakah servernya adalah server lokal atau server produksi. Jika ini adalah server lokal, ia akan memuat local_settings.py dan itu adalah server produksi, ia akan memuat prod_settings.py. Pada dasarnya ini akan terlihat seperti:

#!/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)

Saya menemukan lebih mudah untuk memisahkan file pengaturan menjadi dua file terpisah daripada melakukan banyak seandainya di dalam file pengaturan.

Joshua Partogi
sumber
1

Sebagai alternatif untuk mempertahankan file yang berbeda jika Anda mau: Jika Anda menggunakan git atau VCS lainnya untuk mendorong kode dari lokal ke server, yang dapat Anda lakukan adalah menambahkan file pengaturan ke .gitignore.

Ini akan memungkinkan Anda untuk memiliki konten yang berbeda di kedua tempat tanpa masalah. SO pada server, Anda dapat mengonfigurasi versi setting.py independen dan perubahan apa pun yang dibuat pada lokal tidak akan tercermin di server dan sebaliknya.

Selain itu, ia akan menghapus file settings.py dari github juga, kesalahan besar, yang telah saya lihat banyak pemula lakukan.

sprksh
sumber
0

Saya pikir solusi terbaik disarankan oleh @T. Stone, tapi aku tidak tahu mengapa tidak menggunakan bendera DEBUG di Django. Saya Tulis kode di bawah ini untuk situs web saya:

if DEBUG:
    from .local_settings import *

Selalu solusi sederhana lebih baik daripada solusi yang kompleks.

seyedrezafar
sumber
-2

Saya menemukan tanggapan di sini sangat membantu. (Apakah ini telah diselesaikan secara lebih definitif? Tanggapan terakhir adalah setahun yang lalu.) Setelah mempertimbangkan semua pendekatan yang terdaftar, saya datang dengan solusi yang saya tidak lihat tercantum di sini.

Kriteria saya adalah:

  • Semuanya harus di kontrol sumber. Saya tidak suka potongan-potongan kecil tergeletak di sekitar.
  • Idealnya, simpan pengaturan dalam satu file. Saya lupa hal-hal jika saya tidak memandang mereka :)
  • Tidak ada pengeditan manual untuk digunakan. Harus dapat menguji / mendorong / menyebarkan dengan perintah fabric tunggal.
  • Hindari membocorkan pengaturan pengembangan ke dalam produksi.
  • Tetap sedekat mungkin dengan tata letak Django "standar" (* batuk *).

Saya pikir menyalakan mesin host masuk akal, tetapi kemudian menemukan masalah sebenarnya di sini adalah pengaturan yang berbeda untuk lingkungan yang berbeda , dan memiliki momen aha. Saya meletakkan kode ini di akhir file settings.py saya:

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'

Dengan cara ini, aplikasi secara default ke pengaturan produksi, yang berarti Anda secara eksplisit "memasukkan" lingkungan pengembangan Anda. Jauh lebih aman untuk lupa mengatur variabel lingkungan secara lokal daripada jika itu sebaliknya dan Anda lupa untuk mengatur sesuatu dalam produksi dan membiarkan beberapa pengaturan dev digunakan.

Saat berkembang secara lokal, baik dari shell atau di .bash_profile atau di mana pun:

$ export DJANGO_DEVELOPMENT_SERVER=yep

(Atau jika Anda mengembangkan pada Windows, atur melalui Control Panel atau apa pun namanya hari ini ... Windows selalu membuatnya begitu tidak jelas sehingga Anda dapat mengatur variabel lingkungan.)

Dengan pendekatan ini, pengaturan dev semua berada di satu tempat (standar), dan cukup mengesampingkan yang produksi di mana diperlukan. Setiap penyia-nyiaan dengan pengaturan pengembangan harus benar-benar aman untuk berkomitmen pada kontrol sumber tanpa dampak pada produksi.

Jason Boyd
sumber
Lebih baik hanya mempertahankan file konfigurasi yang berbeda, dan memilih menggunakan variabel enang standar DJango DJANGO_SETTINGS_MODULE
Rob Grant