PostgreSQL mit Django: Tipps für datenbankintensive Anwendungen
Inhalt
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 KategorieArrayField
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())