حول المحتوى:
كل ما تحتاجه لربط Django مع PostgreSQL بشكل صحيح، تحسين الاتصال، الفهرسة، وإعدادات الأداء.
إذا كنت تريد تشغيل مشروع Django في بيئة إنتاج (Production) باحترافية، فالانتقال من قاعدة البيانات الافتراضية SQLite إلى PostgreSQL خطوة أساسية. لكن مجرد “الربط” ليس كافيًا؛ تحتاج إلى إعداد صحيح، ضبط إعدادات الأداء، إدارة الاتصال، وفهم الفهرسة (Indexing) حتى تستفيد بالكامل من قوة PostgreSQL.
في هذا الدليل سنتناول Django PostgreSQL Optimization من الصفر: من الإعداد الأولي، مرورًا بتحسين الاتصال، وصولًا إلى الفهرسة المناسبة وأفضل الممارسات في بيئة الإنتاج.
يدعم Django عدة محركات لقواعد البيانات، لكن PostgreSQL يُعتبر الخيار الموصى به لمعظم تطبيقات الويب الاحترافية، لعدة أسباب:
إذا كنت مهتمًا بفهم اختلاف أداء PostgreSQL مقارنة بـ MySQL وكيفية تأثير الفهرسة، يمكنك الاطلاع على: PostgreSQL مقابل MySQL: مقارنة عملية في الأداء والفهرسة.
من أجل ربط Django مع PostgreSQL تحتاج إلى:
psycopg2 أو psycopg[binary] (في الإصدارات الأحدث).تثبيت psycopg:
pip install psycopg[binary]
# أو للإصدارات الأقدم:
# pip install psycopg2-binary
يفضل في بيئات الإنتاج استخدام النسخة العادية مع الاعتماد على نظام التشغيل في بناء المكتبة، لكن النسخة binary مناسبة لبدايات التطوير السريعة.
بعد تثبيت PostgreSQL، أنشئ قاعدة بيانات ومستخدم مخصص لتطبيق Django:
sudo -u postgres psql
CREATE DATABASE myproject_db;
CREATE USER myproject_user WITH PASSWORD 'strong_password_here';
ALTER ROLE myproject_user SET client_encoding TO 'UTF8';
ALTER ROLE myproject_user SET default_transaction_isolation TO 'read committed';
ALTER ROLE myproject_user SET timezone TO 'UTC';
GRANT ALL PRIVILEGES ON DATABASE myproject_db TO myproject_user;
استخدام مستخدم مخصص للتطبيق مع صلاحيات محددة يساعد في الأمان وتنظيم الوصول.
في ملف settings.py، غيّر إعدادات DATABASES للاستخدام مع PostgreSQL:
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "myproject_db",
"USER": "myproject_user",
"PASSWORD": "strong_password_here",
"HOST": "127.0.0.1", # أو اسم/عنوان خادم قاعدة البيانات
"PORT": "5432",
}
}
في بيئات الإنتاج يفضل تخزين هذه القيم في متغيرات بيئية (Environment Variables) وعدم وضعها مباشرة في الكود.
في مشاريع ذات حمل عالٍ، فتح اتصال جديد بقاعدة البيانات على كل طلب (Request) مكلف من ناحية الأداء. هنا تأتي أهمية Connection Pooling عبر أدوات مثل:
CONN_MAX_AGE.يمكنك ضبط عمر الاتصال في إعدادات قاعدة البيانات:
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "myproject_db",
"USER": "myproject_user",
"PASSWORD": "strong_password_here",
"HOST": "127.0.0.1",
"PORT": "5432",
"CONN_MAX_AGE": 60, # ثواني - 0 يعني إغلاق بعد كل طلب، None يعني دائم
}
}
قيمة مثل 60 أو 300 غالبًا تكون مناسبة لمعظم التطبيقات. الهدف هو تقليل تكلفة فتح/إغلاق الاتصالات مع المحافظة على عدد اتصالات متحكم به.
لمنع المشاكل المتعلقة بالوقت والترميز، تأكد من:
في ملف settings.py:
TIME_ZONE = "UTC"
USE_TZ = True
وفي PostgreSQL تأكد من إعداد:
SHOW TIMEZONE; -- يفضل أن تكون UTC
SHOW SERVER_ENCODING; -- يفضل أن تكون UTF8
يمكنك تمرير بعض الخيارات الخاصة عبر حقل OPTIONS:
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "myproject_db",
"USER": "myproject_user",
"PASSWORD": "strong_password_here",
"HOST": "127.0.0.1",
"PORT": "5432",
"CONN_MAX_AGE": 60,
"OPTIONS": {
"options": "-c statement_timeout=5000ms -c lock_timeout=2000ms",
},
}
}
يمكن أيضًا ضبط هذه الإعدادات على مستوى قاعدة البيانات أو المستخدم مباشرة في PostgreSQL.
Django يستخدم Prepared Statements مع PostgreSQL في بعض السيناريوهات لتحسين الأداء، لكن في بعض البيئات (خاصة مع PgBouncer في وضع transaction) قد تحتاج لتعطيلها.
يمكن التحكم بها عبر:
DATABASES = {
"default": {
...
"DISABLE_SERVER_SIDE_CURSORS": True, # يقلل من استخدام cursors وprepared statements
}
}
استخدم هذا الخيار إذا لاحظت مشاكل أو سلوك غير متوقع عند دمج PgBouncer مع Django.
الفهرسة (Indexing) من أهم النقاط في Django PostgreSQL Optimization. استعلام بدون فهرس على عمود تُستخدم عليه الشروط أو عمليات الفرز (ORDER BY) باستمرار يمكن أن ينهار الأداء معه مع ازدياد حجم البيانات.
إذا أردت فهمًا أعمق لخوارزميات الفهرسة عالميًا، يمكنك مراجعة: أهم خوارزميات الفهرسة المستخدمة في قواعد البيانات.
Django ينشئ تلقائيًا:
primary_key=True.unique=True.ForeignKey.لكن في الكثير من التطبيقات، هذا غير كافٍ، خصوصًا مع الحقول المستخدمة في البحث أو التصفية أو الفرز المتكرر.
يمكنك إضافة فهرس لحقل معين عبر:
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=255, db_index=True) # فهرس بسيط
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
indexes = [
models.Index(fields=["created_at"]),
]
حقل db_index=True ينشئ فهرسًا بسيطًا، بينما حقل indexes في Meta يتيح إنشاء فهارس أكثر تعقيدًا.
إذا كنت تستخدم استعلامات كثيرًا بهذا الشكل:
Article.objects.filter(status="published", created_at__gte=some_date)
فمن الأفضل إنشاء فهرس مركب على (status, created_at):
class Article(models.Model):
status = models.CharField(max_length=32)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
indexes = [
models.Index(fields=["status", "created_at"]),
]
PostgreSQL يستطيع استخدام هذا الفهرس بكفاءة عند التصفية على status والفرز/التصفية بـ created_at.
في بعض الأحيان تحتاج لفهرسة جزء فقط من البيانات، مثلاً السجلات ذات status='published'. هنا تأتي قوة الفهارس الجزئية:
from django.db import models
from django.contrib.postgres.indexes import Index
class Article(models.Model):
status = models.CharField(max_length=32)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
indexes = [
Index(
fields=["created_at"],
name="idx_published_articles_created_at",
condition=models.Q(status="published"),
),
]
بهذا الفهرس، تستفيد استعلاماتك على المقالات المنشورة فقط دون تضخيم حجم الفهرس بباقي الحالات (draft, archived, ...).
إذا كان عندك حقول نصية كبيرة وتحتاج لبحث نصي سريع (Full Text Search)، PostgreSQL يوفر ذلك عبر:
tsvector.GIN. يمكنك استغلال ذلك عبر Django باستخدام SearchVectorField أو SearchVector من django.contrib.postgres.search، ثم إنشاء فهرس GIN:
from django.contrib.postgres.search import SearchVectorField
from django.contrib.postgres.indexes import GinIndex
class Article(models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
search_vector = SearchVectorField(null=True)
class Meta:
indexes = [
GinIndex(fields=["search_vector"]),
]
بعدها يمكنك تحديث search_vector في Signals أو Management Commands ليحتوي القيم المناسبة للبحث.
تحسين الفهرسة وحده لا يكفي؛ يجب أن تكون استعلامات Django ORM مكتوبة بعناية. استعلام سيء يمكن أن يفشل حتى مع أفضل فهارس.
لمزيد من التفاصيل حول تحسين ORM، راجع: تحسين استعلامات Django ORM لأداء أعلى.
لتقليل عدد الاستعلامات (N+1 Problem)، استخدم:
select_related للعلاقات من نوع ForeignKey/OneToOne.prefetch_related للعلاقات ManyToMany أو العكسية.# سيئ: استعلام لكل تعليق لجلب المستخدم
comments = Comment.objects.all()
for c in comments:
print(c.user.username)
# أفضل:
comments = Comment.objects.select_related("user")
for c in comments:
print(c.user.username)
أي استعلام يوضع داخل حلقة غالبًا يمكن تحويله لاستعلام واحد مع تجميع أو Prefetch أو Annotate. هذا ليس تحسينًا لقاعدة البيانات فقط، بل جزء أساسي من Django PostgreSQL Optimization.
JSONField وArrayField عند الحاجة لمزيد من المرونة.Func وF وExpressionWrapper لتنفيذ عمليات على مستوى قاعدة البيانات بدلًا من البايثون.EXPLAIN ANALYZE لتحليل الاستعلامات البطيئة (عن طريق تنفيذ SQL مباشرة في PostgreSQL على الاستعلام الناتج من ORM).حتى لو كان كود Django مثاليًا، الإعداد الافتراضي لـ PostgreSQL غالبًا ليس الأفضل للإنتاج. من أهم الإعدادات التي تستحق النظر:
إذا أردت دليلاً أعمق لضبط PostgreSQL من وجهة نظر مطور، يمكنك الاطلاع على: تحسين أداء قواعد بيانات PostgreSQL للمطورين.
أي تغيير في النماذج (Models) ينعكس مباشرة على PostgreSQL عن طريق migrations. التعامل العشوائي معها قد يؤثر على الأداء أو يؤدي لتوقف الخدمة أثناء التحديثات.
RunSQL عند الحاجة لأوامر PostgreSQL مخصصة غير مدعومة مباشرة من ORM.لإدارة أفضل للمهاجرات في Django راجع: التحكم بالإصدارات في Django: إدارة migrations بشكل صحيح.
أداء التطبيق لا يعتمد على قاعدة البيانات فقط، بل يتأثر أيضًا بالراوتر، DNS، وطبقة الشبكة. تأخير في الـ DNS أو إعداد شبكة ضعيف يمكن أن يظهر وكأنه “بطء في PostgreSQL” بينما المشكلة في مكان آخر.
يمكنك قراءة المزيد عن ذلك في: تأثير إعدادات الراوتر والـ DNS على أداء تطبيقات الويب (ربط شبكات مع Django).
لربط Django مع PostgreSQL بشكل احترافي وتحقيق أفضل أداء، اتبع هذه الاستراتيجية المختصرة:
DATABASES في settings.py مع CONN_MAX_AGE مناسب.select_related وprefetch_related، واستخدام التعابير (Expressions).بتطبيق هذه الخطوات ستتمكن من بناء طبقة بيانات قوية ومرنة لتطبيقات Django، تستفيد من إمكانيات PostgreSQL الكاملة وتوفر أداءً مستقرًا حتى مع ازدياد عدد المستخدمين وحجم البيانات.
كل ما تحتاجه لربط Django مع PostgreSQL بشكل صحيح، تحسين الاتصال، الفهرسة، وإعدادات الأداء.
مساحة اعلانية