# 02 | Settings, management and application composition

## Goal

Replace the welcome shell with a small backend and keep one shared configuration.

## What you are building

settings.py reads environment values and gives both the server and management commands the same database location. app.py creates an application from those settings and mounts modules explicitly. management.py delegates to Flaxon; you do not maintain a second command framework. The factory returns early in management mode so migration commands do not compile browser assets or open the staff store. For now, the only mounted feature is the generated welcome module. Later chapters add each module when its code is ready.

## Build this chapter

Stop the server. Work in project_manager. Follow each CREATE, whole-file replacement, or marked-block edit below in order.

## Step 1: EDIT - replace the entire file - settings.py

Read this first: choose paths and environment values in one place. The rest of the app uses these values.

Select all existing contents of this file and replace them with the complete code below.

```python
"""Shared configuration for the server and management commands."""

from pathlib import Path
from urllib.parse import urlsplit
from flaxon.config import env

BASE_DIR = Path(__file__).resolve().parent
ROOT = BASE_DIR
env.load(BASE_DIR / ".env")
PROJECT_NAME = "Project Manager"
DATA_DIR = Path(env.str("DATA_DIR", str(BASE_DIR / "data"))).resolve()
DATABASE_PATH = DATA_DIR / "app.sqlite3"
ADMIN_DATABASE_PATH = DATA_DIR / "admin.sqlite3"
DATABASE_URL = env.str("DATABASE_URL", f"sqlite://{DATABASE_PATH}")
DEBUG = env.bool("FLAXON_DEBUG", default=True)
SECRET_KEY = env.str("FLAXON_SECRET_KEY")
PUBLIC_ORIGIN = env.str("PUBLIC_ORIGIN", "http://127.0.0.1:8000").rstrip("/")
ALLOWED_HOSTS = env.list(
    "FLAXON_ALLOWED_HOSTS", default=[urlsplit(PUBLIC_ORIGIN).hostname, "testserver"]
)
CSRF_TRUSTED_ORIGINS = [PUBLIC_ORIGIN]
TIME_ZONE = "UTC"
ADMIN_ENABLED = True
CMS_ENABLED = True
ADMIN_SERVICES_ENABLED = False
ADMIN_STORE_BACKEND = "sqlite"
ADMIN_STORAGE_PATH = ADMIN_DATABASE_PATH
```

**Say:** "Read this first: choose paths and environment values in one place. The rest of the app uses these values."

**Type:** Follow the action above in `settings.py`; explain each new function or binding as you add it.

**Viewers should see:** The saved file matches this step. After all steps, run the chapter checks below and show the expected result.

## Step 2: EDIT - replace the entire file - management.py

This short entry point delegates commands to Flaxon and fixes the project root.

Select all existing contents of this file and replace them with the complete code below.

```python
"""One entry point for Flaxon's project commands."""

from pathlib import Path
from flaxon.management import execute


def main(argv=None):
    return execute(argv, project_root=Path(__file__).resolve().parent)


if __name__ == "__main__":
    raise SystemExit(main())
```

**Say:** "This short entry point delegates commands to Flaxon and fixes the project root."

**Type:** Follow the action above in `management.py`; explain each new function or binding as you add it.

**Viewers should see:** The saved file matches this step. After all steps, run the chapter checks below and show the expected result.

## Step 3: EDIT - replace the entire file - app.py

The application factory mounts only the features already introduced. Follow the action label: a marked-block edit keeps the surrounding factory intact.

Select all existing contents of this file and replace them with the complete code below.

```python
"""Compose the CLI starter's modules, staff backoffice, and Teloce SPA."""

import settings
from types import SimpleNamespace
from pathlib import Path
from urllib.parse import urlsplit
from flaxon.middleware import BodyLimitMiddleware, TrustedHostsMiddleware
from flaxon import Flaxon, Request
from flaxon.http import JSONResponse
from modules.welcome.module import welcome
from settings import (
    ROOT,
    DATA_DIR,
    DATABASE_PATH,
    ADMIN_DATABASE_PATH,
    DEBUG,
    PUBLIC_ORIGIN,
)


def create_app(database_path=None, admin_path=None, debug=None):
    debug = DEBUG if debug is None else debug
    values = {key: getattr(settings, key) for key in dir(settings) if key.isupper()}
    values.update(DEBUG=debug)
    if database_path is not None:
        values["DATABASE_URL"] = f"sqlite://{Path(database_path).resolve()}"
    if admin_path is not None:
        values["ADMIN_STORAGE_PATH"] = Path(admin_path)
    app = Flaxon.from_settings(SimpleNamespace(__file__=settings.__file__, **values))
    app.add_middleware(BodyLimitMiddleware, max_size=256 * 1024)
    if not debug:
        app.add_middleware(
            TrustedHostsMiddleware, allowed_hosts=[urlsplit(PUBLIC_ORIGIN).hostname]
        )
    app.public_origin = PUBLIC_ORIGIN
    for module, prefix in [
        (welcome, "/api/welcome"),
    ]:
        app.mount_module(module, prefix=prefix)
    if app.is_management:
        return app


    @app.get("/health/course")
    async def health():
        return {"data": {"status": "ok"}}

    @app.get("/")
    async def home():
        return {"data": {"message": "Backend lesson ready. The SPA starts in chapter 8."}}

    return app


app = create_app()
```

**Say:** "The application factory mounts only the features already introduced. Follow the action label: a marked-block edit keeps the surrounding factory intact."

**Type:** Follow the action above in `app.py`; explain each new function or binding as you add it.

**Viewers should see:** The saved file matches this step. After all steps, run the chapter checks below and show the expected result.

## Run and check

```bash
python management.py runserver
# In a second terminal:
curl http://127.0.0.1:8000/api/welcome/status
```

## Expected result

The root returns a Backend lesson ready JSON message. /api/welcome/status returns its timestamp and framework version.

## Common errors

Run commands inside project_manager. Do not place module mounts in settings.py. Keep the generated models.py until chapter 3; the ORM discovers it but does not create tables on startup.

## Short exercise

Change PROJECT_NAME in settings.py and inspect app.settings in a Python session.

## What to say and show

Say: "Replace the welcome shell with a small backend and keep one shared configuration. The server remains responsible for persistence and authorization; the browser presents the result."

Show the expected result above, then point to the file responsible for it. Run the chapter check before the next lesson.

## Save your checkpoint

```bash
git add .
git commit -m "Complete chapter 02: Settings, management and application composition"
```
