Configuration Concepts¶
Understand how DBWarden configuration works under the hood.
What is Configuration?¶
Configuration tells DBWarden: - Where your databases are (connection URLs) - What kind of databases they are (PostgreSQL, SQLite, etc.) - Where your SQLAlchemy models live (for migration generation) - Where to store migrations (directories)
Why Python Configuration?¶
Type Safety¶
Your IDE can help you:
database_config(
database_name="primary", # ← IDE suggests parameter names
default=True, # ← IDE knows this is boolean
database_type="sqlite", # ← IDE can validate enum values
database_url="...",
)
Dynamic Configuration¶
You can use Python logic:
import os
# Different config per environment
environment = os.getenv("ENV", "dev")
if environment == "production":
database_url = "postgresql://prod-host/myapp"
else:
database_url = "sqlite:///./dev.db"
database_config(
database_name="primary",
default=True,
database_type="postgresql" if environment == "production" else "sqlite",
database_url=database_url,
)
Multiple Databases¶
Easy to configure multiple databases:
DATABASES = {
"primary": "postgresql://localhost/main",
"analytics": "postgresql://localhost/analytics",
"logging": "postgresql://localhost/logs",
}
for name, url in DATABASES.items():
database_config(
database_name=name,
default=(name == "primary"),
database_type="postgresql",
database_url=url,
model_paths=[f"app.models.{name}"],
)
Configuration Loading¶
Discovery Order¶
DBWarden searches for configuration in this order:
1. dbwarden.py in current directory
↓ not found
2. dbwarden.py in parent directories
↓ not found
3. Full scan for files with database_config()
↓ not found
4. DBWARDEN_CONFIG_MODULE environment variable
↓ not found
Error: No configuration found
When Configuration Loads¶
Configuration loads when you run any DBWarden command:
dbwarden migrate # ← Config loads here
dbwarden status # ← Config loads here
dbwarden history # ← Config loads here
Load process:
1. Python imports your config module
2. database_config() calls execute
3. Databases register in internal registry
4. Validation runs
5. Command executes with loaded config
Validation Rules¶
DBWarden validates configuration at load time:
| Rule | Why It Matters |
|---|---|
Exactly one default=True |
CLI needs to know which DB to use when --database is omitted |
Unique database_name |
Commands target databases by name |
Unique database_url |
Prevents accidental duplicate configurations |
| Unique physical targets | Prevents two configs pointing to same DB with different credentials |
Required model_paths in multi-DB |
Keeps model discovery boundaries clear |
No overlapping model_paths |
Prevents ambiguous model ownership (unless overlap_models=True) |
Validation Timing¶
# dbwarden.py
database_config(
database_name="primary",
default=True,
database_type="postgresql",
database_url="postgresql://localhost/myapp",
)
database_config(
database_name="primary", # ← Duplicate!
default=True,
database_type="postgresql",
database_url="postgresql://localhost/other",
)
When you run dbwarden migrate:
Error: Duplicate database_name 'primary'
Validation happens before any commands execute.
The default Database¶
Why default=True Exists¶
Consider these commands:
# Explicit database
dbwarden migrate --database primary
# Implicit database (uses default)
dbwarden migrate
Without default=True, DBWarden wouldn't know which database to use for the second command.
Only One Default¶
# ✅ Good
database_config(database_name="primary", default=True, ...)
database_config(database_name="analytics", default=False, ...) # or omit default
# ❌ Bad - two defaults
database_config(database_name="primary", default=True, ...)
database_config(database_name="analytics", default=True, ...) # Error!
Default Affects CLI Behavior¶
# These are equivalent when primary is default:
dbwarden migrate
dbwarden migrate --database primary
# These are NOT equivalent:
dbwarden migrate
dbwarden migrate --database analytics # Targets analytics, not primary
Model Discovery¶
What Are model_paths?¶
model_paths tells DBWarden where your SQLAlchemy models live:
database_config(
database_name="primary",
default=True,
database_type="postgresql",
database_url="postgresql://localhost/myapp",
model_paths=["app.models"], # ← Look here for models
)
How Discovery Works¶
1. Import each module in model_paths
↓
2. Find all classes inheriting from DeclarativeBase
↓
3. Extract table metadata (__tablename__, columns, etc.)
↓
4. Build internal representation for migration generation
When Is It Required?¶
Single database: Optional (DBWarden scans entire codebase)
# This works
database_config(
database_name="primary",
default=True,
database_type="sqlite",
database_url="sqlite:///./app.db",
# No model_paths needed
)
Multiple databases: Required for each database
# This is required
database_config(
database_name="primary",
default=True,
database_type="postgresql",
database_url="postgresql://localhost/main",
model_paths=["app.models.primary"], # ← Required
)
database_config(
database_name="analytics",
database_type="postgresql",
database_url="postgresql://localhost/analytics",
model_paths=["app.models.analytics"], # ← Required
)
Why? To prevent ambiguity about which models belong to which database.
Dev Mode¶
What Is Dev Mode?¶
Dev mode lets you use a different database for local development:
database_config(
database_name="primary",
default=True,
database_type="postgresql", # Production
database_url="postgresql://prod/myapp",
dev_database_type="sqlite", # Development
dev_database_url="sqlite:///./dev.db",
)
Run commands with --dev:
dbwarden --dev migrate # Uses SQLite
dbwarden migrate # Uses PostgreSQL
How It Works¶
Command: dbwarden --dev migrate
↓
Check for --dev flag
↓
Swap database_type → dev_database_type
Swap database_url → dev_database_url
↓
Connect to dev database
↓
Execute command
Why Use It?¶
Speed: - SQLite is faster than PostgreSQL for local iteration - No network latency - No server setup
Safety: - Can't accidentally affect production - Each developer has their own isolated database - Easy to reset (just delete the file)
Simplicity: - No Docker containers needed - No database server installation - Works on all platforms
Multi-Database Configuration¶
Why Multiple Databases?¶
Common scenarios: - Separation of concerns - Transactions vs analytics - Performance - Offload reporting to separate database - Compliance - Audit logs in separate database - Legacy systems - New and old databases coexist
How It Works¶
Each database_config() call registers an independent database:
database_config(
database_name="primary",
default=True,
database_type="postgresql",
database_url="postgresql://localhost/main",
model_paths=["app.models.primary"],
)
database_config(
database_name="analytics",
database_type="postgresql",
database_url="postgresql://localhost/analytics",
model_paths=["app.models.analytics"],
)
They're completely independent: - Separate migration histories - Separate migration directories - Separate model sets - Can use different database types
Model Path Boundaries¶
app/
models/
primary/
user.py # ← Goes to primary database
order.py
analytics/
event.py # ← Goes to analytics database
metric.py
Configuration:
database_config(
database_name="primary",
model_paths=["app.models.primary"], # ← Only primary models
...
)
database_config(
database_name="analytics",
model_paths=["app.models.analytics"], # ← Only analytics models
...
)
Secure Values¶
What Is secure_values?¶
Prevents credentials from appearing in terminal output:
import os
DATABASE_URL = os.getenv("DATABASE_URL")
database_config(
database_name="primary",
default=True,
database_type="postgresql",
database_url=DATABASE_URL,
secure_values=True, # ← Hide credentials
)
Without secure_values:¶
$ dbwarden database
Database: primary
URL: postgresql://user:SECRET_PASSWORD@prod-host/myapp
With secure_values:¶
$ dbwarden database
Database: primary
URL: DATABASE_URL (expression)
Shows the variable name instead of resolved value.
Configuration vs Runtime¶
Configuration Time¶
When config loads:
- database_config() calls execute
- Validation runs
- Internal registry populates
- No database connections made
Runtime¶
When commands run: - DBWarden reads from registry - Connects to database - Executes command logic
Key point: Configuration errors are caught early, before any database operations.
Recap¶
You learned:
✅ Why Python configuration is powerful
✅ How configuration discovery works
✅ When validation runs
✅ Why default=True is required
✅ How model discovery works
✅ What dev mode does
✅ How multi-database configuration works
✅ When to use secure_values
What's Next?¶
- Connection URLs - URL format reference
- Model Discovery - Deep dive into model paths
- Dev Mode - Local development workflows
- Multi-Database - Multi-database patterns