Optimierung der Konfiguration über Umgebungen hinweg in Django und Flask
Emily Parker
Product Engineer · Leapcell

Einleitung
die Entwicklung robuster Webanwendungen beinhaltet oft die Bewältigung einer entscheidenden Herausforderung: die Verwaltung unterschiedlicher Konfigurationen über verschiedene Phasen des Softwarelebenszyklus hinweg. Was in der lokalen Umgebung eines Entwicklers perfekt funktioniert – zum Beispiel die Verbindung zu einer SQLite-Datenbank oder die Aktivierung von ausführlicher Fehlersuche –, wäre in einer Produktionsumgebung katastrophal. In der Produktion benötigen wir hocheffiziente Datenbankverbindungen, robuste Protokollierung, strenge Sicherheitseinstellungen und minimale Fehlersuchinformationen, die der Öffentlichkeit zugänglich gemacht werden. Ebenso erfordern Testumgebungen eine spezifische Konfiguration, um isolierte und wiederholbare Tests zu gewährleisten. Das Ignorieren dieser Unterschiede führt zu frustrierenden Fehlern, Sicherheitslücken und Problemen bei der Bereitstellung. Dieser Artikel untersucht praktische und effektive Strategien für den reibungslosen Umgang mit diesen unterschiedlichen Konfigurationen in den beliebten Python-Webframeworks Django und Flask, um einen reibungslosen Übergang von der Entwicklung zur Produktion zu gewährleisten.
Kernkonzepte und Implementierungsstrategien
Bevor wir uns mit den Besonderheiten von Django und Flask beschäftigen, wollen wir einige Kernkonzepte festlegen, die für eine effektive Konfigurationsverwaltung von grundlegender Bedeutung sind.
Kernterminologie
- Umgebungsvariablen: Dies sind dynamisch benannte Werte, die das Verhalten laufender Prozesse auf einem Computer beeinflussen können. Sie werden häufig verwendet, um sensible Informationen (wie API-Schlüssel, Datenbankanmeldeinformationen) oder umgebungsspezifische Einstellungen zu speichern, ohne sie in den Code der Anwendung zu integrieren.
- Einstellungen/Konfigurationsdateien: Diese Dateien (häufig
settings.py
in Django oder benutzerdefinierte.py
- oder.ini
-Dateien in Flask) enthalten Schlüssel-Wert-Paare, die das Verhalten einer Anwendung bestimmen, wie z. B. Datenbankverbindungen, aktivierte Apps, Middleware und Protokollierungsstufen. SECRET_KEY
: Eine kritische Sicherheitseinstellung in Django (und oft auch in Flask für die Sitzungsverwaltung verwendet), die für kryptografisches Signieren verwendet wird. Sie muss geheim und für jede Umgebung, insbesondere für die Produktion, eindeutig sein.- Debug-Status: Ein boolesches Flag (
DEBUG
in Django,DEBUG
in Flask), das steuert, ob Debugging-Informationen angezeigt werden. Es sollte in der Produktion immerFalse
sein.
Allgemeine Grundsätze für die Konfiguration
- Keine Geheimnisse in die Versionskontrolle einchecken: Sensible Informationen wie Datenbankpasswörter, API-Schlüssel und
SECRET_KEY
sollten niemals fest codiert oder in Git eingecheckt werden. - Umgebungsspezifische Einstellungen trennen: Konfigurationen, die sich zwischen Entwicklung, Test und Produktion unterscheiden, sollten von der Kernanwendungslogik entkoppelt werden.
- Umgebungsvariablen für sensible Daten priorisieren: Umgebungsvariablen bieten eine sichere und flexible Möglichkeit, sensible Daten zur Laufzeit in Ihre Anwendung einzuspeisen.
- Sinnvolle Standardwerte verwenden: Stellen Sie Standardkonfigurationen bereit, die für die Entwicklung gut funktionieren, und überschreiben Sie diese dann für andere Umgebungen.
Django-Konfigurationsstrategie
Das integrierte Einstellungsmanagement von Django ist leistungsstark, erfordert aber eine sorgfältige Strukturierung für Bereitstellungen mit mehreren Umgebungen. Ein üblicher und sehr empfehlenswerter Ansatz ist die Trennung der Einstellungen in ein Paket.
Nehmen wir eine Projektstruktur wie diese an:
myproject/
├── manage.py
├── myproject/
│ ├── __init__.py
│ ├── urls.py
│ ├── wsgi.py
│ └── settings/
│ ├── __init__.py
│ ├── base.py
│ ├── development.py
│ ├── production.py
│ └── test.py
└── myapp/
└── ...
1. myproject/settings/base.py
:
Diese Datei enthält alle gemeinsamen Einstellungen, die für alle Umgebungen gelten.
# myproject/settings/base.py import os from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent.parent # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! # Use environment variable for SECRET_KEY SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'default-insecure-key-for-dev-only') # SECURITY WARNING: don't run with debug turned on in production! DEBUG = False # Default to False, overridden in development.py ALLOWED_HOSTS = [] # Overridden in specific environments # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'myapp', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'myproject.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'myproject.wsgi.application' DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } } AUTH_PASSWORD_VALIDATORS = [ # ... default validators ... ] LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_TZ = True STATIC_URL = '/static/' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
2. myproject/settings/development.py
:
Diese Datei importiert base.py
und überschreibt Einstellungen für die lokale Entwicklung.
# myproject/settings/development.py from .base import * DEBUG = True ALLOWED_HOSTS = ['127.0.0.1', 'localhost'] # Use a simpler database for development DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } } # Add any development-specific apps or tools INSTALLED_APPS += [ 'debug_toolbar', ] MIDDLEWARE += [ 'debug_toolbar.middleware.DebugToolbarMiddleware', ] # Django Debug Toolbar configuration INTERNAL_IPS = [ '127.0.0.1', ] LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console': { 'class': 'logging.StreamHandler', }, }, 'loggers': { 'django': { 'handlers': ['console'], 'level': 'INFO', }, 'myapp': { 'handlers': ['console'], 'level': 'DEBUG', # More verbose logging for your app in dev }, }, }
3. myproject/settings/production.py
:
Diese Datei importiert base.py
und konfiguriert produktionsbereite Einstellungen.
# myproject/settings/production.py from .base import * DEBUG = False # Load SECRET_KEY from environment variable for security SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY') if not SECRET_KEY: raise ImproperlyConfigured('DJANGO_SECRET_KEY environment variable not set') ALLOWED_HOSTS = os.environ.get('DJANGO_ALLOWED_HOSTS', '').split(',') if not ALLOWED_HOSTS: raise ImproperlyConfigured('DJANGO_ALLOWED_HOSTS environment variable not set') # Production database settings (e.g., PostgreSQL) DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': os.environ.get('POSTGRES_DB_NAME'), 'USER': os.environ.get('POSTGRES_DB_USER'), 'PASSWORD': os.environ.get('POSTGRES_DB_PASSWORD'), 'HOST': os.environ.get('POSTGRES_DB_HOST'), 'PORT': os.environ.get('POSTGRES_DB_PORT', '5432'), } } # Static file storage and serving STATIC_ROOT = BASE_DIR / 'staticfiles' STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' # Recommended for production # Security settings CSRF_COOKIE_SECURE = True SESSION_COOKIE_SECURE = True SECURE_SSL_REDIRECT = True SECURE_HSTS_SECONDS = 31536000 # 1 year SECURE_HSTS_INCLUDE_SUBDOMAINS = True SECURE_HSTS_PRELOAD = True SECURE_BROWSER_XSS_FILTER = True X_FRAME_OPTIONS = 'DENY' # Logging for production (e.g., to file or external service) LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'verbose': { 'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}', 'style': '{', }, }, 'handlers': { 'file': { 'level': 'INFO', 'class': 'logging.handlers.RotatingFileHandler', 'filename': '/var/log/myproject/myproject.log', # Adjust path as needed 'maxBytes': 1024*1024*5, # 5MB 'backupCount': 5, 'formatter': 'verbose', }, 'console': { # Still useful for containerized environments 'class': 'logging.StreamHandler', 'formatter': 'verbose', }, }, 'loggers': { 'django': { 'handlers': ['file', 'console'], 'level': 'INFO', 'propagate': True, }, 'myapp': { 'handlers': ['file', 'console'], 'level': 'WARNING', # Less verbose for production unless critical 'propagate': False, }, }, }
4. myproject/settings/test.py
:
Ähnlich wie die Entwicklung, nur mit sehr spezifischen Änderungen für isolierte Tests.
# myproject/settings/test.py from .base import * DEBUG = False # Tests should run as close to production as possible, without debug info # Use an in-memory SQLite database for fast, isolated tests DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:', } } # Speed up password hashing during tests PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.MD5PasswordHasher', ] SECRET_KEY = 'test-insecure-key' # Safe for test
5. So laden Sie die richtigen Einstellungen:
Django lädt Einstellungen über die Umgebungsvariable DJANGO_SETTINGS_MODULE
.
- Entwicklung:
export DJANGO_SETTINGS_MODULE=myproject.settings.development
- Produktion:
export DJANGO_SETTINGS_MODULE=myproject.settings.production
- Testen:
export DJANGO_SETTINGS_MODULE=myproject.settings.test
Sie können dies in Ihrer Webserverkonfiguration (z. B. Gunicorn, uWSGI) festlegen oder direkt beim Ausführen von Django-Befehlen:
# Für die Entwicklung DJANGO_SETTINGS_MODULE=myproject.settings.development python manage.py runserver # Für die Produktion (über Gunicorn Beispiel) DJANGO_SETTINGS_MODULE=myproject.settings.production gunicorn myproject.wsgi:application
Flask-Konfigurationsstrategie
Flask bietet Flexibilität bei der Verwaltung von Konfigurationen. Ein gängiger Ansatz nutzt Python-Klassen und Umgebungsvariablen.
Nehmen wir eine Projektstruktur an:
myflaskapp/
├── run.py
├── config.py
└── myflaskapp/
├── __init__.py
└── views.py
1. config.py
:
Diese Datei definiert Basis-, Entwicklungs-, Test- und Produktionskonfigurationsklassen.
# config.py import os from datetime import timedelta class Config: """Base configuration settings.""" SECRET_KEY = os.environ.get('FLASK_SECRET_KEY', 'dev-secret-key-insecure') SQLALCHEMY_TRACK_MODIFICATIONS = False PERMANENT_SESSION_LIFETIME = timedelta(days=7) LOG_LEVEL = 'INFO' class DevelopmentConfig(Config): """Development configuration.""" DEBUG = True ENV = 'development' SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or \ 'sqlite:///' + os.path.join(os.path.abspath(os.path.dirname(__file__)), 'dev.db') LOG_LEVEL = 'DEBUG' class TestingConfig(Config): """Testing configuration.""" DEBUG = True # Keep debug true for detailed error reporting during tests ENV = 'testing' TESTING = True # Flask-specific flag SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:' # In-memory database for tests LOG_LEVEL = 'WARNING' SECRET_KEY = 'test-secret-key' # Safe for test class ProductionConfig(Config): """Production configuration.""" DEBUG = False ENV = 'production' SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') if SQLALCHEMY_DATABASE_URI is None: raise ValueError("DATABASE_URL environment variable not set for production.") # Override SECRET_KEY from environment for production SECRET_KEY = os.environ.get('FLASK_SECRET_KEY') if SECRET_KEY is None: raise ValueError("FLASK_SECRET_KEY environment variable not set for production.") # Logging: For production, typically send to file or a log management service LOG_LEVEL = 'ERROR' # Only high-severity logs
2. myflaskapp/__init__.py
:
Diese Datei initialisiert die Flask-App und lädt die entsprechende Konfiguration.
# myflaskapp/__init__.py from flask import Flask import os import logging from logging.handlers import RotatingFileHandler def create_app(): app = Flask(__name__) # Determine which config to load based on FLASK_ENV environment variable # It's common to default to DevelopmentConfig if FLASK_ENV is not set config_name = os.environ.get('FLASK_ENV', 'development') if config_name == 'production': app.config.from_object('config.ProductionConfig') elif config_name == 'testing': app.config.from_object('config.TestingConfig') else: # Default to development app.config.from_object('config.DevelopmentConfig') # Example of integrating a database if you're using one like SQLAlchemy # from flask_sqlalchemy import SQLAlchemy # db = SQLAlchemy(app) # Configure logging based on the environment if not app.debug and not app.testing: # Production logging example (to a file) if not os.path.exists('logs'): os.mkdir('logs') file_handler = RotatingFileHandler('logs/myflaskapp.log', maxBytes=10240, backupCount=10) file_handler.setFormatter(logging.Formatter( '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]' )) file_handler.setLevel(logging.ERROR) # Use app.config.get('LOG_LEVEL', 'ERROR') app.logger.addHandler(file_handler) app.logger.setLevel(logging.INFO) # Use app.config.get('LOG_LEVEL', 'INFO') app.logger.info('MyFlaskApp startup') elif app.debug: # Development logging to console app.logger.setLevel(logging.DEBUG) app.logger.info('MyFlaskApp development startup') from . import views app.register_blueprint(views.bp) return app
3. run.py
(oder ähnlicher Einstiegspunkt):
# run.py from myflaskapp import create_app app = create_app() if __name__ == '__main__': app.run()
4. So laden Sie die richtigen Einstellungen:
Flask verwendet konventionell die Umgebungsvariable FLASK_ENV
, die development
, production
oder testing
sein kann.
- Entwicklung:
export FLASK_ENV=development
- Produktion:
export FLASK_ENV=production
- Testen:
export FLASK_ENV=testing
Führen Sie dann Ihre Anwendung aus:
# Für die Entwicklung export FLASK_ENV=development python run.py # Für die Produktion (über Gunicorn Beispiel) export FLASK_ENV=production export DATABASE_URL="postgresql://user:password@host/dbname" export FLASK_SECRET_KEY="a-very-long-and-secure-random-string" gunicorn 'myflaskapp:create_app()'
Verwaltung von Umgebungsvariablen
Unabhängig davon, ob Sie Django oder Flask verwenden, ist die sichere Verwaltung von Umgebungsvariablen von größter Bedeutung.
-
Für die Entwicklung: Verwenden Sie eine
.env
-Datei und eine Bibliothek wiepython-dotenv
.pip install python-dotenv
- Erstellen Sie eine
.env
-Datei im Stammverzeichnis Ihres Projekts (z. B.myproject/.env
odermyflaskapp/.env
):DJANGO_SECRET_KEY='your-dev-secret-key-here' POSTGRES_DB_NAME='myproject_dev' FLASK_SECRET_KEY='your-dev-secret-key-here' DEV_DATABASE_URL='sqlite:///dev.db'
- Fügen Sie
.env
zu Ihrer.gitignore
-Datei hinzu. - Laden Sie es in Ihrem
settings.py
(Django) oderconfig.py
/__init__.py
(Flask):# In Django settings/base.py oder Flask config.py/init.py from dotenv import load_dotenv load_dotenv() # This loads variables from .env into os.environ # Then access them as usual SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
-
Für die Produktion: Umgebungsvariablen werden typischerweise von Ihrem Hosting-Anbieter festgelegt (z. B. Heroku Config Vars, Kubernetes Secrets, AWS EC2-Umgebungsvariablen, Docker Compose
env_file
). Verwenden Sie.env
-Dateien nicht in der Produktion. Sie dienen der lokalen Bequemlichkeit.
Anwendungsszenarien
Die oben beschriebenen Konfigurationen eignen sich für eine Vielzahl von Szenarien:
- Lokale Entwicklung: Schnelle, iterative Änderungen mit ausführlicher Protokollierung und einfach zu verwendenden lokalen Datenbanken.
- Automatisierte Tests: Isolierte, wiederholbare Tests mit In-Memory- oder temporären Datenbanken zur Gewährleistung von Geschwindigkeit und Vermeidung von Nebeneffekten.
- Staging/Vorserienproduktion: Eine nahezu identische Replikation der Produktion, die externe Datenbanken und Dienste nutzt, um abschließende Prüfungen vor der Bereitstellung zu ermöglichen.
- Produktion: Sichere, optimierte und leistungsstarke Einrichtung mit externen Diensten, robuster Überwachung und minimaler Offenlegung von Debugging-Informationen.
Fazit
Eine effektive Verwaltung von Konfigurationen über verschiedene Umgebungen hinweg ist ein Eckpfeiler für die Erstellung zuverlässiger und sicherer Webanwendungen mit Django und Flask. Durch die Trennung umgebungsspezifischer Einstellungen, die Nutzung von Umgebungsvariablen für sensible Daten und die sorgfältige Strukturierung Ihrer Konfigurationsdateien schaffen Sie ein robustes und wartbares System. Eine gut organisierte Konfigurationsstrategie reduziert die Bereitstellungsrisiken erheblich und verbessert die Anwendungsstabilität.