Django_
Use Django's ORM with an Appwrite native PostgreSQL database. Configure the DATABASES setting with TLS options, run migrations on the direct PostgreSQL port, and tune persistent connections for pooling.
4 min read
A native PostgreSQL database is a standard PostgreSQL engine, so Django's ORM works against it with no Appwrite-specific configuration. Point the DATABASES setting at the credentials from the Connections page, then use migrations, models, and the rest of Django exactly as you would against any PostgreSQL server.
You'll need a native PostgreSQL database in a ready state and its credentials. See native PostgreSQL databases to create one and Connections to retrieve the connection details. The primary user is admin, and the database name is generated for each database.
Install a driver
Django talks to PostgreSQL through a driver. Use psycopg 3:
pip install "psycopg[binary]"Django's PostgreSQL backend uses the ENGINE value django.db.backends.postgresql.
Configure DATABASES
In settings.py, point the default connection at your native PostgreSQL database and require TLS through OPTIONS. Appwrite Cloud terminates TLS at the edge, and the certificate is signed by a public certificate authority. Read every value from the environment so credentials stay out of source control:
import os
DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", "NAME": os.environ["DB_NAME"], "USER": os.environ["DB_USER"], "PASSWORD": os.environ["DB_PASSWORD"], "HOST": os.environ["DB_HOST"], "PORT": os.environ["DB_PORT"], "OPTIONS": { "sslmode": "require", }, }}The PostgreSQL backend forwards everything in OPTIONS to the driver's connection, so sslmode is honored the same way psql honors it in a connection string. For full certificate verification, set "sslmode": "verify-full" with "sslrootcert" pointing at a trusted root store or your OS bundle, such as /etc/ssl/certs/ca-certificates.crt. See Network security for TLS and IP allowlist guidance.
Populate the environment from the Console Credentials dialog or from postgresql.get() in the API credentials flow:
DB_NAME=<database>DB_USER=adminDB_PASSWORD=<password>DB_HOST=db-<hash>.<region>.appwrite.centerDB_PORT=5432Port 5432 is the direct PostgreSQL port. Keep migrations on this port, and see pooling below for when to add the pooler.
Run migrations
Generate migrations from your models, then apply them against the direct PostgreSQL port:
python manage.py makemigrationspython manage.py migratemigrate needs a session connection with DDL privileges. The primary admin user owns the database and can run schema changes. Narrower database roles (readonly or readwrite) are intended for application traffic with reduced privileges. Always run migrate on the direct PostgreSQL port, not through the transaction-mode pooler.
Define a model
Models map to tables in your native PostgreSQL database. Define one in an app's models.py:
from django.db import models
class Article(models.Model): title = models.CharField(max_length=200) body = models.TextField() created_at = models.DateTimeField(auto_now_add=True)
class Meta: ordering = ["-created_at"]Run makemigrations and migrate again to create the table, then query it through the ORM:
from blog.models import Article
Article.objects.create(title="Hello", body="First post")
recent = Article.objects.order_by("-created_at")[:10]Persistent connections and pooling
By default Django opens a new connection per request (CONN_MAX_AGE = 0). On a long-running WSGI server (Gunicorn, uWSGI) you can reuse connections by raising it. Each worker thread keeps its own connection, so the database must allow at least as many connections as you run worker threads:
DATABASES["default"]["CONN_MAX_AGE"] = 60DATABASES["default"]["CONN_HEALTH_CHECKS"] = TrueCONN_HEALTH_CHECKS revalidates a reused connection once per request, avoiding errors after an engine restart. Do not enable persistent connections under the development server, it spawns a thread per request and gains nothing, and disable them under ASGI.
A worker holding a connection across requests behaves like a long-lived session. Connect it to the direct PostgreSQL port or the session-mode pooler. The default transaction-mode pooler can hand each statement a different backend connection, which makes it a better fit for serverless and short-lived runtimes that open and close a connection per invocation.
If you front the database with the transaction-mode connection pooler, point HOST and PORT at the PostgreSQL pooler on port 6432. Also set DISABLE_SERVER_SIDE_CURSORS = True, since server-side cursors can't survive being moved between backend connections:
DATABASES["default"]["DISABLE_SERVER_SIDE_CURSORS"] = TrueFor prepared statements, advisory locks, LISTEN/NOTIFY, or temporary tables, use session mode instead, see the pooler page for the trade-offs.
Use a branch for previews and CI
Branches are instant, isolated copies of a database with their own hostname and connection details. They're ideal for running migrations against throwaway data in a pull-request preview or an integration-test job:
- Create a branch from the API and read its connection details.
- Export them as the
DB_*environment variables your settings read. - Run
python manage.py migrateand your test suite against the branch. - Delete the branch when the job finishes.
Because a branch starts from a storage snapshot, its schema and data match the source database at branch time, so migrations run against realistic data without touching production.
Related
Connections
Retrieve credentials, rotate the password, and create scoped database roles.
Connection pooler
Pool modes, ports, and read/write splitting for serverless workloads.
Branches
Ephemeral database copies for preview environments and CI.
Network
TLS, certificate verification, IP allowlists, and idle timeout settings.
Was this page helpful?
Share what worked or what we should fix. Once approved, our agents automatically apply suggested updates to the docs.