TL; DR: Thủ thuật là sửa đổi os.environment
trước khi bạn nhập settings/base.py
bất kỳ settings/<purpose>.py
, điều này sẽ đơn giản hóa rất nhiều thứ.
Chỉ cần suy nghĩ về tất cả các tập tin đan xen này làm tôi đau đầu. Kết hợp, nhập (đôi khi có điều kiện), ghi đè, vá những gì đã được đặt trong trường hợp DEBUG
cài đặt thay đổi sau này. Thật là một cơn ác mộng!
Qua nhiều năm tôi đã trải qua tất cả các giải pháp khác nhau. Tất cả họ đều có phần việc, nhưng rất đau đớn để quản lý. WTF! Chúng ta có thực sự cần tất cả những rắc rối đó? Chúng tôi bắt đầu chỉ với một settings.py
tập tin. Bây giờ chúng ta cần một tài liệu chỉ để kết hợp chính xác tất cả những thứ này lại với nhau theo đúng thứ tự!
Tôi hy vọng cuối cùng tôi đã đạt được điểm ngọt ngào (của tôi) với giải pháp dưới đây.
Hãy tóm tắt lại các mục tiêu (một số thông thường, một số của tôi)
Giữ bí mật một bí mật - đừng lưu trữ chúng trong một repo!
Đặt / đọc khóa và bí mật thông qua cài đặt môi trường, kiểu 12 yếu tố .
Có mặc định dự phòng hợp lý. Lý tưởng cho sự phát triển địa phương, bạn không cần thêm gì ngoài mặc định.
Sầu nhưng cố gắng giữ mặc định sản xuất an toàn. Tốt hơn là bỏ lỡ một ghi đè cài đặt cục bộ, hơn là phải nhớ điều chỉnh các cài đặt mặc định an toàn cho sản xuất.
Có khả năng bật DEBUG
/ tắt theo cách có thể có ảnh hưởng đến các cài đặt khác (ví dụ: sử dụng javascript có nén hay không).
Chuyển đổi giữa các cài đặt mục đích, như cục bộ / thử nghiệm / dàn / sản xuất, chỉ nên dựa trên DJANGO_SETTINGS_MODULE
, không có gì hơn.
Khác nhưng cho phép tham số hóa thêm thông qua các cài đặt môi trường như thế nào DATABASE_URL
.
Cẩu cũng cho phép họ sử dụng các cài đặt mục đích khác nhau và chạy chúng cục bộ cạnh nhau, vd. thiết lập sản xuất trên máy phát triển cục bộ, để truy cập cơ sở dữ liệu sản xuất hoặc kiểm tra kiểu bảng nén khói.
Thất bại nếu một biến môi trường không được đặt rõ ràng (yêu cầu giá trị trống tối thiểu), đặc biệt là trong sản xuất, ví dụ. EMAIL_HOST_PASSWORD
.
Trả lời DJANGO_SETTINGS_MODULE
cài đặt mặc định trong Manage.txt trong khi khởi động django-admin
Giữ điều kiện đến mức tối thiểu, nếu điều kiện là các loại môi trường có mục tiêu (ví dụ. Phục vụ sản xuất bộ file log và nó xoay), cài đặt ghi đè trong liên cài đặt tập tin có mục tiêu.
Đừng
Đừng để django đọc cài đặt DJANGO_SETTINGS_MODULE tạo thành một tệp.
Ừ! Hãy nghĩ về cách meta này là. Nếu bạn cần phải có một tệp (như docker env) hãy đọc nó vào môi trường trước khi bắt đầu một quá trình django.
Không ghi đè DJANGO_SETTINGS_MODULE trong mã dự án / ứng dụng của bạn, vd. dựa trên tên máy chủ hoặc tên quá trình.
Nếu bạn lười thiết lập biến môi trường (như for setup.py test
), hãy thực hiện công cụ này ngay trước khi bạn chạy mã dự án.
Tránh ma thuật và vá cách django đọc cài đặt của nó, xử lý trước các cài đặt nhưng không can thiệp sau đó.
Không có logic dựa trên logic phức tạp. Cấu hình nên được cố định và vật chất hóa không được tính toán khi đang bay. Cung cấp một mặc định dự phòng là đủ logic ở đây.
Bạn có thực sự muốn gỡ lỗi, tại sao tại địa phương bạn có bộ cài đặt chính xác nhưng trong sản xuất trên một máy chủ từ xa, trên một trong hàng trăm máy, một cái gì đó được tính toán khác nhau? Oh! Bài kiểm tra đơn vị? Để cài đặt? Nghiêm túc?
Giải pháp
Chiến lược của tôi bao gồm xuất sắc django-environ sử dụng với ini
các file phong cách, cung cấp os.environment
, một số tối thiểu và ngắn giá trị mặc định cho sự phát triển địa phương settings/<purpose>.py
tập tin có một
import settings/base.py
SAU sự os.environment
được thành lập từ một INI
tập tin. Điều này có hiệu quả cung cấp cho chúng tôi một loại tiêm cài đặt.
Mẹo ở đây là sửa đổi os.environment
trước khi bạn nhập settings/base.py
.
Để xem ví dụ đầy đủ, hãy thực hiện repo: https://github.com/wooyek/django-sinstall-strargety
.
│ 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
cài đặt / .env
Một mặc định cho sự phát triển địa phương. Một tập tin bí mật, chủ yếu là thiết lập các biến môi trường cần thiết. Đặt chúng thành các giá trị trống nếu chúng không được yêu cầu trong phát triển cục bộ. Chúng tôi cung cấp mặc định ở đây và không settings/base.py
bị lỗi trên bất kỳ máy nào khác nếu thiếu từ môi trường.
cài đặt / local.py
Điều gì xảy ra ở đây, là tải môi trường từ settings/.env
, sau đó nhập cài đặt chung từ đó settings/base.py
. Sau đó, chúng ta có thể ghi đè một số để giảm bớt sự phát triển cục bộ.
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
cài đặt / sản xuất
Để sản xuất, chúng ta không nên mong đợi một tệp môi trường, nhưng sẽ dễ dàng hơn nếu có một tệp nào đó. Nhưng dù sao đi nữa, e rằng sẽ cung cấp một vài giá trị mặc định nội tuyến, do đó settings/base.py
có thể đáp ứng tương ứng.
environ.Env.read_env(Path(__file__) / "production.env", DEBUG='False', ASSETS_DEBUG='False')
from .base import *
Điểm quan tâm chính ở đây là DEBUG
và ASSETS_DEBUG
ghi đè, chúng sẽ được áp dụng cho con trăn os.environ
CHỈ nếu chúng BỎ L from từ môi trường và tệp.
Đây sẽ là các mặc định sản xuất của chúng tôi, không cần phải đặt chúng vào môi trường hoặc tệp, nhưng chúng có thể bị ghi đè nếu cần. Khéo léo!
cài đặt / cơ sở
Đây là các cài đặt django vanilla chủ yếu của bạn, với một vài điều kiện và rất nhiều đọc chúng từ môi trường. Hầu hết mọi thứ đều ở đây, giữ cho tất cả các môi trường có mục đích nhất quán và giống nhau nhất có thể.
Sự khác biệt chính là dưới đây (tôi hy vọng đây là tự giải thích):
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 cuối cùng cho thấy sức mạnh ở đây. ASSETS_DEBUG
có một mặc định hợp lý, có thể bị ghi đè settings/production.py
và thậm chí có thể bị ghi đè bởi cài đặt môi trường! Yay!
Trong thực tế, chúng tôi có một hệ thống phân cấp quan trọng hỗn hợp:
- settings / .py - đặt mặc định dựa trên mục đích, không lưu trữ bí mật
- settings / base.py - chủ yếu được kiểm soát bởi môi trường
- xử lý cài đặt môi trường - 12 yếu tố bé!
- settings / .env - mặc định cục bộ để dễ dàng khởi động