← Zurück zum Blog
PostgreSQLDjango20. April 2026· 15 min Lesezeit

PostgreSQL mit Django: Tipps für datenbankintensive Anwendungen

Inhalt
  1. JSONField für flexible Datenstrukturen
  2. Full-Text Search
  3. Window Functions
  4. ArrayField
  5. Connection Pooling mit PgBouncer
  6. Queries analysieren mit EXPLAIN

PostgreSQL ist weit mehr als eine relationale Datenbank. Mit django.contrib.postgres hast du direkten Zugriff auf viele mächtige Features die externe Dienste oft überflüssig machen.

JSONField für flexible Datenstrukturen

class Product(models.Model):
    name = models.CharField(max_length=200)
    attributes = models.JSONField(default=dict)

# Speichern
Product.objects.create(
    name="Laptop",
    attributes={"cpu": "Intel i7", "ram": 16, "storage": ["512GB SSD"]}
)

# Filtern nach JSON-Inhalt
Product.objects.filter(attributes__ram=16)
Product.objects.filter(attributes__cpu__contains="Intel")

# Felder annotieren
from django.db.models import F
Product.objects.annotate(ram=F("attributes__ram")).filter(ram__gte=8)

Full-Text Search

from django.contrib.postgres.search import SearchVector, SearchQuery, SearchRank

query  = SearchQuery("django performance", config="german")
vector = SearchVector("title", weight="A", config="german") + \
         SearchVector("body",  weight="B", config="german")

results = Article.objects \
    .annotate(rank=SearchRank(vector, query)) \
    .filter(rank__gte=0.1) \
    .order_by("-rank")

Für bessere Performance: SearchVectorField für vorab berechnete Vektoren mit einem GinIndex.

Window Functions

from django.db.models import F, Window
from django.db.models.functions import Rank

# Rang der Artikel nach Aufrufen pro Kategorie
Article.objects.annotate(
    category_rank=Window(
        expression=Rank(),
        partition_by=[F("category")],
        order_by=F("views").desc()
    )
).filter(category_rank=1)  # Meistgesehener pro Kategorie

ArrayField

from django.contrib.postgres.fields import ArrayField

class Article(models.Model):
    tags = ArrayField(models.CharField(max_length=50), default=list)

# Filtern
Article.objects.filter(tags__contains=["python", "django"])
Article.objects.filter(tags__overlap=["python", "flask"])
Article.objects.filter(tags__len__gt=3)

Connection Pooling mit PgBouncer

Jede Django-Request öffnet standardmäßig eine neue Datenbankverbindung. PgBouncer als Connection Pooler löst dieses Problem bei hohem Traffic:

[databases]
mydb = host=localhost dbname=mydb

[pgbouncer]
pool_mode = transaction    # Empfohlen fuer Django
max_client_conn = 1000
default_pool_size = 20
listen_port = 6432
# settings.py: Port auf PgBouncer umleiten
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "HOST":   "localhost",
        "PORT":   "6432",   # PgBouncer statt 5432
        "CONN_MAX_AGE": 0,  # Wichtig bei transaction pooling!
    }
}

Queries analysieren mit EXPLAIN

from django.db import connection

queryset = Article.objects.filter(status="published").select_related("author")

# SQL anzeigen
print(queryset.query)

# EXPLAIN ANALYZE direkt ausfuehren
with connection.cursor() as cursor:
    cursor.execute(f"EXPLAIN ANALYZE {queryset.query}")
    print(cursor.fetchall())
Yevhen Chubchyk
Yevhen Chubchyk
Senior Python / Django Entwickler · Freelancer seit 2016 · 20+ Jahre IT-Erfahrung. Kunden: Siemens Healthineers, Engie, natureOffice u.a.