Tập này tách file settings duy nhất của ứng dụng quản lý chi tiêu thành ba môi trường dev, staging và production, đồng thời chuyển mọi secret sang biến môi trường bằng django-environ. Kết quả: cùng một codebase chạy ở cả ba môi trường, không còn secret nào trong code. Nối tiếp tập 1 đã lập checklist production-readiness, đây là bước đầu tiên biến checklist đó thành hiện thực.
Vì sao không dùng một settings.py cho mọi môi trường
Khi mọi môi trường dùng chung một file settings, bạn buộc phải viết các điều kiện if DEBUG rải rác khắp nơi, và chỉ một lần quên cũng đủ để debug bật trên production. Tách settings theo môi trường giúp mỗi môi trường khai báo rõ ràng những gì nó cần, còn phần dùng chung nằm ở một file base duy nhất.
Cấu trúc thư mục settings
Thay file settings.py bằng một package settings đặt cạnh manage.py, gồm một file base và một file cho mỗi môi trường:
expense_project/├── manage.py└── expense_project/ ├── __init__.py ├── settings/ │ ├── __init__.py │ ├── base.py │ ├── dev.py │ ├── staging.py │ └── production.py └── urls.py
Cài đặt django-environ và viết settings/base.py
Cài django-environ rồi chuyển cấu hình dùng chung vào base.py. Lưu ý: SECRET_KEY không có giá trị mặc định thật — thiếu biến môi trường thì ứng dụng lỗi ngay khi khởi động, thay vì âm thầm chạy với secret giả.
# settings/base.pyimport environfrom pathlib import PathBASE_DIR = Path(__file__).resolve().parent.parent.parentenv = environ.Env()environ.Env.read_env(BASE_DIR / ".env")SECRET_KEY = env("DJANGO_SECRET_KEY")DEBUG = env.bool("DJANGO_DEBUG", default=False)ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", default=[])INSTALLED_APPS = [ "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", "rest_framework", "expenses",]DATABASES = {"default": env.db("DATABASE_URL")}
Hàm env.db() hiểu chuỗi kết nối dạng postgres://user:pass@host:5432/dbname và trả về đúng dict cho DATABASES, nên đổi cơ sở dữ liệu chỉ là đổi một biến môi trường.
settings cho dev, staging và production
Mỗi file môi trường chỉ import từ base và ghi đè phần khác biệt. Dev bật debug và dùng SQLite cho nhanh, production tắt debug và bật các cờ bảo mật mà tập 11 sẽ nói kỹ hơn.
# settings/dev.pyfrom .base import * # noqa: F401,F403DEBUG = TrueALLOWED_HOSTS = ["localhost", "127.0.0.1"]DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": BASE_DIR / "db.sqlite3", }}# settings/production.pyfrom .base import * # noqa: F401,F403DEBUG = FalseSECURE_SSL_REDIRECT = TrueSESSION_COOKIE_SECURE = TrueCSRF_COOKIE_SECURE = True
File .env và nguyên tắc không commit secret
Mỗi môi trường có một file .env riêng, không bao giờ đưa lên Git. Thêm .env vào .gitignore, và commit file .env.example chỉ chứa tên biến và giá trị mẫu. Khi cần xoay secret, chỉ sửa file .env trên server rồi khởi động lại tiến trình.
# .env.example — commit file này, KHÔNG commit .env thậtDJANGO_SECRET_KEY=thay-bang-secret-thatDJANGO_DEBUG=FalseDJANGO_ALLOWED_HOSTS=example.com,www.example.comDATABASE_URL=postgres://user:pass@localhost:5432/expense_prod
Chạy ứng dụng với từng môi trường
Django chọn settings qua DJANGO_SETTINGS_MODULE, nên không cần sửa manage.py hay wsgi.py cho từng môi trường. Kiểm tra nhanh bằng python manage.py check --deploy với settings production để quét các cấu hình bảo mật trước khi đưa ứng dụng lên server thật.
# DevDJANGO_SETTINGS_MODULE=expense_project.settings.dev python manage.py runserver# ProductionDJANGO_SETTINGS_MODULE=expense_project.settings.production gunicorn expense_project.wsgi:application
Áp dụng vào dự án của bạn
Bắt đầu từ ứng dụng quản lý chi tiêu: tách settings, chuyển SECRET_KEY và DATABASE_URL sang .env, rồi chạy check --deploy để xem còn thiếu gì. Nếu dự án vẫn dùng SQLite trên production, đừng vội đổi cơ sở dữ liệu ngay — tập 4 sẽ lo phần PostgreSQL, còn hôm nay chỉ cần tách xong settings và sạch secret trong Git là đạt mục tiêu.
Bài viết liên quan
- Django vận hành production #1: Giới thiệu series + checklist production-readiness
- Flask: quản lý config theo môi trường (dev/staging/prod), không còn hardcode SECRET_KEY
- FastAPI: quản lý cấu hình tập trung bằng pydantic-settings, khỏi hardcode và đọc os.environ thủ công
Câu hỏi thường gặp
Có nên để file .env trên server production không?
Có, nhưng chỉ trên server, chỉ tài khoản chạy ứng dụng đọc được (quyền file 600). Không commit .env vào Git, không gửi qua chat hay email, không đóng gói vào image Docker — secret được cấp cho container lúc chạy, điều tập 16 sẽ trình bày.
django-environ và python-decouple khác nhau thế nào?
Cả hai đều đọc biến môi trường, nhưng django-environ có thêm env.db() và env.cache() để parse chuỗi kết nối thành dict cấu hình Django ngay lập tức. Chỉ cần vài biến đơn giản thì python-decouple cũng đủ; có DATABASE_URL thì django-environ tiện hơn rõ rệt.
Quên đặt DJANGO_SETTINGS_MODULE thì sao?
Django sẽ dùng settings mặc định trong manage.py và wsgi.py — thường là settings cũ đã xóa — nên ứng dụng lỗi ngay khi khởi động. Đặt biến này trong unit file systemd hoặc lệnh chạy của process manager, rồi kiểm tra bằng python manage.py check trước mỗi lần deploy.
Nguồn tham khảo
- Tài liệu django-environ, tài liệu Django về settings, The Twelve-Factor App (mục Config).
