Part 2: Adding a Database¶
Connect your Django app to a managed PostgreSQL database, create models, and build a REST API.
What you'll learn¶
- Enabling a managed PostgreSQL database for your application
- Creating Django models and DRF serializers for a Todo API
- Writing an entrypoint script that waits for the database and runs migrations
- Creating, backing up, restoring, and downloading database backups
What you'll need¶
- A deployed hello-world app from Part 1: Your First Deployment
djangorestframeworkandpsycopg2-binaryadded to your requirements
Step 1: Understand how the database appears¶
There is no create command. Databases are provisioned implicitly: set database: enabled: true in appmecca.yaml and deploy. On the first deploy AppMecca creates the database and its user on the managed PostgreSQL server, stores the generated password in the platform secret store, and injects the connection details into every container of the app. Re-deploying is idempotent.
Database and user names are opaque
AppMecca derives the database and user names as a deterministic hash — they look like am_9f3a1c4e88b2_db and am_9f3a1c4e88b2_user, never todo_app_db. Never hard-code them. Read DATABASE_URL (or the individual DB_* variables) from the environment.
The names are also derived per branch, so main and a feature branch each get their own separate database. Data does not cross branches.
There are no mecca db commands
The database lifecycle is entirely implicit: created on first deploy, dropped by mecca app delete. Backups and restores go through mecca app backup / mecca app restore, covered in Step 10; uploading a pg_dump you produced elsewhere is the dashboard's Database tab.
Step 2: Update dependencies¶
Edit requirements.txt:
Step 3: Add the Todo model¶
Edit core/models.py:
from django.db import models
class Todo(models.Model):
title = models.CharField(max_length=255)
done = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ["-created_at"]
def __str__(self):
return self.title
Step 4: Add DRF serializers and views¶
Create core/serializers.py:
from rest_framework import serializers
from .models import Todo
class TodoSerializer(serializers.ModelSerializer):
class Meta:
model = Todo
fields = ["id", "title", "done", "created_at"]
read_only_fields = ["id", "created_at"]
Replace core/views.py:
from django.http import JsonResponse
from rest_framework import viewsets
from .models import Todo
from .serializers import TodoSerializer
def health(request):
return JsonResponse({"status": "healthy"})
class TodoViewSet(viewsets.ModelViewSet):
queryset = Todo.objects.all()
serializer_class = TodoSerializer
Update config/urls.py:
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from core.views import health, TodoViewSet
router = DefaultRouter()
router.register(r"todos", TodoViewSet)
urlpatterns = [
path("health", health),
path("api/", include(router.urls)),
]
Add "rest_framework" to INSTALLED_APPS in config/settings.py.
Step 5: Configure database settings¶
Update config/settings.py to read the connection string from the environment. Parsing DATABASE_URL is the safest route because it works unchanged no matter what the platform names the database:
import os
from urllib.parse import urlparse
_url = urlparse(os.environ["DATABASE_URL"])
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": _url.path.lstrip("/"),
"USER": _url.username,
"PASSWORD": _url.password,
"HOST": _url.hostname,
"PORT": str(_url.port or 5432),
}
}
Automatic credentials
When database: enabled: true is set in appmecca.yaml, AppMecca injects DATABASE_URL plus the individual DB_HOST, DB_PORT, DB_NAME, DB_USER and DB_PASSWORD variables into every container of the app. Use whichever shape your framework prefers — but never assume the values: DB_NAME and DB_USER are the opaque hashed names described in Step 1.
Step 6: Write the entrypoint script¶
Create entrypoint.sh at the repository root. It waits for the database, runs migrations, then starts gunicorn:
#!/bin/bash
set -e
echo "Waiting for database..."
while ! python -c "import socket; s=socket.socket(); s.connect(('${DB_HOST}',int('${DB_PORT}'))); s.close()" 2>/dev/null; do sleep 1; done
echo "Running migrations..."
python manage.py migrate --noinput
echo "Starting server..."
exec gunicorn config.wsgi:application --bind 0.0.0.0:8000
Make it executable: chmod +x entrypoint.sh.
Step 7: Update the Dockerfile¶
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN python manage.py collectstatic --noinput 2>/dev/null || true
RUN chmod +x entrypoint.sh
EXPOSE 8000
CMD ["./entrypoint.sh"]
Step 8: Update appmecca.yaml¶
database:
enabled: true
containers:
- name: todo-app
dockerfile: Dockerfile
context: .
deploy:
- name: web
container: todo-app
port: 8000
health_check:
path: /health
Declare your connection pool size
database.max_connections (default 5, range 1-50) tells AppMecca how many connections each replica of this app may open. The platform sums that across every database-enabled app and refuses deploys that would exhaust the shared PostgreSQL server. Django's default is one connection per worker process, so a 4-worker gunicorn wants:
Commit and push, then build and deploy:
Step 9: Test the API¶
Once deployed, test the endpoints against your app URL (shown by mecca app show). The hostname always includes the service name:
APP_URL=https://todo-app-web-yourgroup.dev.yourdomain.com
# Create a todo
curl -X POST "$APP_URL/api/todos/" \
-H "Content-Type: application/json" -d '{"title": "Learn AppMecca"}'
# List, update, delete
curl "$APP_URL/api/todos/"
curl -X PUT "$APP_URL/api/todos/1/" \
-H "Content-Type: application/json" -d '{"title": "Learn AppMecca", "done": true}'
curl -X DELETE "$APP_URL/api/todos/1/"
Step 10: Manage database backups¶
A backup bundle captures the database and every declared drive in one manifest-verified archive. Create one before making further changes:
List backups (newest first) and note the bundle prefix of the one you want:
mecca app backups todo-app -e dev
mecca app backups todo-app -e dev --json
mecca app backups todo-app -e dev -n 50 # show more than the default 20
Download a bundle locally — this prints time-limited URLs for each object in it:
Restore from a bundle:
mecca app restore todo-app BUNDLE_PREFIX -e dev --force
mecca app restore todo-app BUNDLE_PREFIX -e dev --db-only # leave drives alone
Restore is destructive
Restoring replaces the current database contents entirely, and prompts for confirmation unless --force is passed. Always create a fresh backup before restoring.
Importing an existing database
To bring an existing PostgreSQL database onto AppMecca, upload a pg_dump file directly:
# Dashboard -> todo-app -> Database tab -> choose the file -> confirm
mecca queue status ACTION_ID -e dev # follow the queue row
A custom-format dump (pg_dump -Fc) is recommended. This is destructive and prompts for your password.
What's next¶
Your backend is complete. In Part 3: Going Full-Stack, you will add a React frontend and deploy both containers from a single repository.