Django
Django connects to DB9 through the standard PostgreSQL backend using psycopg2. No special adapter or driver is needed — Django’s ORM, migrations, and management commands work as expected.
Prerequisites
Section titled “Prerequisites”- A DB9 database (create one)
- Python 3.10+
- Django 4.2+
Create a DB9 Database
Section titled “Create a DB9 Database”db9 create --name django-appGet the connection string:
db9 db status django-appSet the connection string as an environment variable:
DATABASE_URL="postgresql://django-app.admin:YOUR_PASSWORD@pg.db9.io:5433/postgres?sslmode=require"Configure Database Connection
Section titled “Configure Database Connection”Install dependencies:
pip install django psycopg2-binary dj-database-url python-dotenvimport dj_database_urlfrom dotenv import load_dotenv
load_dotenv(".env.local")
DATABASES = { "default": dj_database_url.config( default="postgresql://django-app.admin:YOUR_PASSWORD@pg.db9.io:5433/postgres?sslmode=require", conn_max_age=600, )}Install dependencies:
pip install django psycopg2-binaryDATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", "NAME": "postgres", "USER": "django-app.admin", "PASSWORD": "YOUR_PASSWORD", "HOST": "pg.db9.io", "PORT": "5433", "OPTIONS": { "sslmode": "require", }, }}Define Models
Section titled “Define Models”from django.db import models
class User(models.Model): email = models.EmailField(unique=True) name = models.CharField(max_length=100) created_at = models.DateTimeField(auto_now_add=True)
class Meta: db_table = "users"
def __str__(self): return self.name
class Post(models.Model): title = models.CharField(max_length=500) content = models.TextField(blank=True) published = models.BooleanField(default=False) author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="posts") created_at = models.DateTimeField(auto_now_add=True)
class Meta: db_table = "posts"
def __str__(self): return self.titleRun Migrations
Section titled “Run Migrations”Generate and apply migrations:
python manage.py makemigrations blogpython manage.py migrateVerify the tables exist:
python manage.py dbshell\dtViews and URLs
Section titled “Views and URLs”List view
Section titled “List view”from django.http import JsonResponsefrom .models import User, Post
def user_list(request): users = User.objects.all().values("id", "name", "email", "created_at") return JsonResponse(list(users), safe=False)
def create_user(request): if request.method == "POST": import json data = json.loads(request.body) user = User.objects.create( email=data["email"], name=data["name"], ) return JsonResponse({"id": user.id, "name": user.name}, status=201) return JsonResponse({"error": "POST required"}, status=405)
def post_list(request): posts = Post.objects.select_related("author").values( "id", "title", "content", "author__name", "created_at" ) return JsonResponse(list(posts), safe=False)URL configuration
Section titled “URL configuration”from django.urls import pathfrom . import views
urlpatterns = [ path("users/", views.user_list, name="user-list"), path("users/create/", views.create_user, name="create-user"), path("posts/", views.post_list, name="post-list"),]from django.urls import path, include
urlpatterns = [ path("api/", include("blog.urls")),]Django REST Framework
Section titled “Django REST Framework”For a full API layer, add Django REST Framework:
pip install djangorestframeworkfrom rest_framework import serializersfrom .models import User, Post
class PostSerializer(serializers.ModelSerializer): class Meta: model = Post fields = ["id", "title", "content", "published", "author", "created_at"]
class UserSerializer(serializers.ModelSerializer): posts = PostSerializer(many=True, read_only=True)
class Meta: model = User fields = ["id", "email", "name", "posts", "created_at"]from rest_framework import viewsetsfrom .models import User, Postfrom .serializers import UserSerializer, PostSerializer
class UserViewSet(viewsets.ModelViewSet): queryset = User.objects.prefetch_related("posts").all() serializer_class = UserSerializer
class PostViewSet(viewsets.ModelViewSet): queryset = Post.objects.select_related("author").all() serializer_class = PostSerializerfrom django.urls import path, includefrom rest_framework.routers import DefaultRouterfrom .views_api import UserViewSet, PostViewSet
router = DefaultRouter()router.register("users", UserViewSet)router.register("posts", PostViewSet)
urlpatterns = [ path("api/", include(router.urls)),]Production Notes
Section titled “Production Notes”- Server-side only: Django runs on the server by default, so all DB queries go through the backend. Never log or render the connection string.
- Connection pooling: Set
CONN_MAX_AGEto reuse connections across requests. For high-traffic apps, considerdjango-db-connection-poolor PgBouncer. - Port 5433: DB9 uses port 5433, not the default PostgreSQL port 5432. Double-check your
DATABASESconfig. - TLS required: Always include
sslmode=requirein the connection string or set it inOPTIONSfor DB9’s hosted service. - WSGI server: Use Gunicorn or uWSGI in production. Django’s development server (
runserver) is not designed for production traffic.
Troubleshooting
Section titled “Troubleshooting”Connection refused on port 5432
Section titled “Connection refused on port 5432”DB9 uses port 5433, not 5432. Update your DATABASES config or DATABASE_URL to use the correct port.
OperationalError: FATAL: authentication failed
Section titled “OperationalError: FATAL: authentication failed”Verify the username format. DB9 expects {database-name}.admin as the username (e.g., django-app.admin).
Migrations fail with relation already exists
Section titled “Migrations fail with relation already exists”If you created tables manually before running migrations, use python manage.py migrate --fake to mark existing migrations as applied. Then run future migrations normally.
SSL connection errors
Section titled “SSL connection errors”Ensure sslmode=require is set. If using the direct config, add it under OPTIONS:
"OPTIONS": { "sslmode": "require",}psycopg2 not found
Section titled “psycopg2 not found”Install the binary package: pip install psycopg2-binary. For production, compile from source with pip install psycopg2 (requires libpq-dev).
Next Pages
Section titled “Next Pages”- SQLAlchemy — Python ORM alternative with SQLAlchemy 2.0
- Connect — connection strings and authentication
- Production Checklist — deployment readiness