skip to content

In Django, what does the DATABASES setting define, and what do you change to move a prototype from SQLite to PostgreSQL?

level: juniorimportance: must knowfreq 60%

answer

  1. a dict of aliases
  2. one alias is mandatory
  3. ENGINE picks the backend module
  4. NAME means a file for one engine
  5. a driver, then migrate

basics

~20 s

DATABASES maps aliases such as default to connection settings: ENGINE picks the backend, NAME the database, plus USER, PASSWORD, HOST, PORT and OPTIONS. Moving to PostgreSQL means installing psycopg, setting ENGINE to django.db.backends.postgresql with credentials, and running migrate.

solid answer

~40 s

`DATABASES` is a dict of **aliases** to connection settings, and it must define `default`. Each entry names an `ENGINE` (a built-in backend module such as `django.db.backends.sqlite3` or `django.db.backends.postgresql`), a `NAME`, which is a file path for SQLite and a database name elsewhere, and for server databases `USER`, `PASSWORD`, `HOST` and `PORT`, plus backend-specific `OPTIONS` and connection settings like `CONN_MAX_AGE`. `startproject` generates SQLite with `NAME` set to `BASE_DIR / 'db.sqlite3'`. To move to PostgreSQL I install a driver (psycopg 3 is recommended), switch `ENGINE`, supply the credentials from the environment, create an empty database and run `migrate`. Data, if any is worth keeping, goes across with `dumpdata`/`loaddata`. On Django 6.1 the server must be PostgreSQL 15 or newer.

code

python · 12 lines
python
import os

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": os.environ.get("DB_NAME", "timetable"),
        "USER": os.environ.get("DB_USER", "timetable_app"),
        "PASSWORD": os.environ["DB_PASSWORD"],
        "HOST": os.environ.get("DB_HOST", "localhost"),
        "PORT": os.environ.get("DB_PORT", "5432"),
    }
}

go deeper

for a junior

Recall the shape of DATABASES: aliases, a required default, ENGINE and NAME, and credentials for server databases.

for a middle

Explain the full switch to PostgreSQL: driver, engine, migrate on an empty database, fixtures with natural keys, and the supported server versions.

for a senior

Treat the switch as a migration project: data transfer order, sequence resets, running the whole suite on PostgreSQL and checking behaviour differences before cut-over.

for a principal

Decide which database the team develops and tests against from day one, weighing setup cost against the bugs a different engine hides.

## What `DATABASES` is `DATABASES` is the setting that tells Django which databases exist and how to connect to them. It is a dictionary whose keys are **aliases** and whose values are dictionaries of connection settings. It must contain a `default` alias; any number of extra aliases (a replica, a reporting database) can sit beside it. A new project from `startproject` starts with SQLite: ```python DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": BASE_DIR / "db.sqlite3", } } ``` ## The keys that matter | Key | Meaning | Default | |---|---|---| | `ENGINE` | Backend module: `django.db.backends.postgresql`, `mysql`, `sqlite3` or `oracle` (or a third-party path) | Empty | | `NAME` | Database name; for SQLite, the full path to the file | Empty | | `USER`, `PASSWORD`, `HOST`, `PORT` | Server credentials and address; unused by SQLite | Empty | | `OPTIONS` | Extra parameters passed to the backend or driver | `{}` | | `CONN_MAX_AGE` | Connection lifetime in seconds; `None` for unlimited | `0` | | `CONN_HEALTH_CHECKS` | Check a reused connection before a request uses it | `False` | | `ATOMIC_REQUESTS` | Wrap each view in a transaction | `False` | Other entries (`AUTOCOMMIT`, `TIME_ZONE`, `TEST`, `DISABLE_SERVER_SIDE_CURSORS`) exist for specific needs. A few facts interviewers like: - **MariaDB has no engine of its own**: it uses `django.db.backends.mysql`. - The old `django.db.backends.postgresql_psycopg2` engine name was deprecated in 2.0 and **removed in 3.0**; the current name is `django.db.backends.postgresql`. - `OPTIONS` is backend-specific: `timeout` and `transaction_mode` for SQLite, `isolation_level` for PostgreSQL and MySQL, `pool` for PostgreSQL and Oracle. ## Moving the prototype to PostgreSQL 1. **Install a driver.** Django supports psycopg 3 (3.1.12+) and psycopg2 (2.9.9+); psycopg 3 is recommended, and connection pooling needs it. 2. **Change the entry**: ```python DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", "NAME": "timetable", "USER": "timetable_app", "PASSWORD": os.environ["DB_PASSWORD"], "HOST": "db.internal", "PORT": "5432", } } ``` 3. **Create the empty database and user** on the server, then run `python manage.py migrate` to build the schema from your migrations. 4. **Move data if needed.** `dumpdata` from the SQLite project and `loaddata` into PostgreSQL. Excluding `contenttypes` and `auth.Permission` (which `migrate` recreates) and using `--natural-foreign` avoids primary-key clashes; `loaddata` resets PostgreSQL sequences afterwards. 5. **Run the test suite against PostgreSQL**, because SQLite hides behaviour differences that will now surface. ## Checking that the switch worked Before pointing real traffic at the new database, a few management commands confirm the wiring: - `python manage.py dbshell` opens the database's own client with the configured credentials; if it connects, `ENGINE`, `NAME`, `HOST` and the password are right. - `python manage.py check --database default` runs the system checks that need a database connection, such as backend-specific warnings. - `python manage.py showmigrations` lists every migration with its applied state on the new database. - A quick count comparison (`Model.objects.count()` per important model, old versus new) catches a fixture that silently skipped rows. ## Supported versions in Django 6.1 Each release drops database versions whose upstream support is ending: - PostgreSQL **15** and higher (14 was dropped in 6.1). - MySQL **8.4** and higher; MariaDB **10.11** and higher. - SQLite **3.37.0** and later (raised from 3.31.0). - Oracle **19** and higher. Django checks the server version when it connects, so an unsupported server fails with an error instead of misbehaving quietly. ## Common mistakes - Treating `NAME` as a file path on PostgreSQL, or as a database name on SQLite. - Hard-coding the password in `settings.py` instead of reading it from the environment. - Copying the SQLite file's data by hand instead of using fixtures or a proper export, and forgetting the sequences.

  • What happens if DATABASES defines only a 'reporting' alias and no 'default'?
    Django requires a `default` entry whenever `DATABASES` is non-empty and raises `ImproperlyConfigured` when it is missing. Code that never names an alias, including most model managers, `connection` and the test runner, uses `default`. If you genuinely want no default database, define `default` as an empty dict and route every model elsewhere.
  • Why exclude contenttypes and auth.Permission when dumping data for a new database?
    `migrate` creates content type and permission rows itself, with their own primary keys. Loading the old rows on top collides with those keys or leaves foreign keys pointing at the wrong ids. Excluding them and dumping with `--natural-foreign` makes other rows refer to content types by app label and model name, which match on the new database.

saying these in an interview costs you the question

  • For PostgreSQL, NAME is the path to the database file
  • MariaDB needs its own django.db.backends.mariadb engine
  • django.db.backends.postgresql_psycopg2 is still the engine name to use
  • DATABASES can omit default if every model is routed
  • Django 6.1 still supports PostgreSQL 13 and 14