حول المحتوى:
عند العمل مع Django ORM (Object Relational Mapper)، واحدة من أكثر العمليات شيوعًا هي ترتيب البيانات المستخرجة من قاعدة البيانات. هنا يأتي دور الدالة order_by التي توفرها Django كجزء من واجهة QuerySet API.
عند العمل مع Django ORM (Object Relational Mapper)، واحدة من أكثر العمليات شيوعًا هي ترتيب البيانات المستخرجة من قاعدة البيانات. هنا يأتي دور الدالة order_by
التي توفرها Django كجزء من واجهة QuerySet API.
ببساطة، order_by
تسمح لك بترتيب النتائج المسترجعة من قاعدة البيانات وفقًا لحقل أو أكثر، سواء كان الترتيب تصاعديًا أو تنازليًا.
على سبيل المثال، إذا كان لديك جدول للمقالات، يمكنك عرضها مرتبة حسب تاريخ النشر (الأحدث أولًا أو الأقدم أولًا)، أو حسب العنوان أبجديًا.
تحسين تجربة المستخدم: عرض المنتجات أو المقالات بطريقة منطقية يسهل التصفح.
دعم المنطق التجاري: مثل عرض الطلبات الأحدث أولًا في لوحة التحكم.
المرونة: يمكنك التحكم في طريقة ظهور البيانات دون الحاجة إلى كتابة استعلامات SQL معقدة يدويًا.
باختصار، order_by
هي الأداة الأساسية في Django لترتيب البيانات بشكل ديناميكي وسهل.
الأمر order_by
في Django يُستخدم مع أي QuerySet لترتيب النتائج حسب حقل معين أو أكثر. الصياغة الأساسية له كالتالي:
Model.objects.order_by('field_name')
بشكل افتراضي، Django يرتب النتائج بترتيب تصاعدي.
مثال: إذا كان لدينا موديل Student
يحتوي على حقل name
:
students = Student.objects.order_by('name')
هنا سيتم ترتيب الطلاب أبجديًا من A إلى Z.
للحصول على ترتيب تنازلي نضيف علامة السالب (-) قبل اسم الحقل:
students = Student.objects.order_by('-name')
النتيجة: ترتيب الطلاب أبجديًا من Z إلى A.
يمكنك تمرير اسم حقل واحد أو أكثر إلى order_by
.
إذا لم تحدد أي ترتيب، فإن قاعدة البيانات ستعيد النتائج بالترتيب الافتراضي (غالبًا حسب المفتاح الأساسي Primary Key).
بعد ما عرفنا الأساسيات، خلينا نشوف كيف نستخدم order_by
في مواقف يومية داخل مشاريع Django.
إذا عندنا موديل Article
يحتوي على title
و published_date
:
articles = Article.objects.order_by('-published_date')
النتيجة: عرض المقالات الأحدث أولًا (وهو الأكثر شيوعًا في المدونات والمواقع الإخبارية).
في تطبيق تعليمي، نريد عرض الطلاب أبجديًا:
students = Student.objects.order_by('name')
النتيجة: تظهر قائمة الطلاب مرتبة من A إلى Z.
في متجر إلكتروني عندنا موديل Product
:
products = Product.objects.order_by('price')
النتيجة: المنتجات مرتبة من الأرخص إلى الأغلى.
ولو أردنا العكس (الأغلى أولًا):
products = Product.objects.order_by('-price')
لو عندنا موديل Order
:
orders = Order.objects.order_by('-created_at')
النتيجة: تظهر الطلبات الأحدث أولًا، وهو شيء مهم في لوحات التحكم الخاصة بالمشرفين.
أحيانًا نحتاج نرتب النتائج ليس بحقل واحد فقط، بل بمجموعة حقول معًا. في هذه الحالة، order_by
يسمح لنا بتمرير أكثر من اسم حقل بالترتيب الذي نريده.
Model.objects.order_by('field1', '-field2')
يتم الترتيب أولًا حسب field1
.
إذا كانت هناك قيم متساوية في field1
، يتم كسر التعادل باستخدام field2
.
يمكن إضافة أكثر من حقلين.
students = Student.objects.order_by('grade', 'name')
النتيجة:
الطلاب مرتبين حسب الصف (grade) من الأصغر إلى الأكبر.
داخل كل صف، الطلاب مرتبين أبجديًا بالاسم.
employees = Employee.objects.order_by('department', '-salary')
النتيجة:
كل قسم (department) يظهر معًا.
داخل كل قسم، الموظفون مرتبين من الأعلى راتبًا إلى الأقل.
products = Product.objects.order_by('category', '-created_at')
النتيجة:
المنتجات grouped حسب الفئة.
أحدث منتج يظهر أولًا داخل كل فئة.
Meta
داخل الموديلإلى جانب استخدام order_by
مباشرة في الاستعلامات، يوفر Django طريقة أخرى لتعريف ترتيب افتراضي للبيانات من خلال class Meta داخل الموديل.
ordering
في Meta
داخل الموديل نضيف خاصية ordering
، وهي عبارة عن قائمة بالحقول التي نريد ترتيب البيانات عليها افتراضيًا:
from django.db import models
class Student(models.Model):
name = models.CharField(max_length=100)
grade = models.IntegerField()
join_date = models.DateField()
class Meta:
ordering = ['name'] # ترتيب الطلاب أبجديًا بشكل افتراضي
النتيجة:
كل استعلام مثل:
Student.objects.all()
سيُرجع الطلاب مرتبين بالاسم حتى بدون كتابة order_by
.
Meta
لجعل الترتيب تنازليًا نضيف السالب (-) قبل الحقل:
class Meta:
ordering = ['-join_date'] # الأحدث أولًا
ordering
و order_by
ordering
في Meta
: تحدد الترتيب الافتراضي لكل الاستعلامات على هذا الموديل.
order_by
: تستخدم عند الحاجة لتغيير الترتيب في استعلام معين.
الأولوية لـ order_by
: إذا استخدمت order_by
في استعلام، سيتجاهل الترتيب الموجود في Meta
.
# ترتيب افتراضي من Meta (الأحدث أولًا)
articles = Article.objects.all()
# كسر الترتيب الافتراضي وعرض الأقدم أولًا
articles = Article.objects.order_by('published_date')
في Django غالبًا يكون عندنا جداول مرتبطة ببعض (علاقات ForeignKey أو ManyToMany)، ومرات نحتاج نرتب البيانات بناءً على حقل موجود في الجدول المرتبط.
نفترض أن عندنا موديل كتاب Book
مرتبط بكاتب Author
:
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
books = Book.objects.order_by('author__name')
النتيجة:
الكتب تظهر مرتبة حسب اسم الكاتب أبجديًا.
مثال: موديل Student
و Course
:
class Course(models.Model):
name = models.CharField(max_length=100)
class Student(models.Model):
name = models.CharField(max_length=100)
courses = models.ManyToManyField(Course)
students = Student.objects.order_by('courses__name')
النتيجة:
الطلاب يُرتبون بناءً على اسم المقرر الذي يرتبطون به (قد يظهر الطالب أكثر من مرة إذا له أكثر من مقرر).
ممكن نستعمل __
أكثر من مرة إذا عندنا علاقات متداخلة.
مثال: لو أردنا ترتيب الكتب حسب اسم مدينة الكاتب (لو عند الكاتب حقل city
):
books = Book.objects.order_by('author__city')
في بعض الحالات ما نحتاج نرتب فقط على الحقول المخزنة في قاعدة البيانات، بل نريد الترتيب بناءً على قيمة مشتقة أو عملية حسابية.
Django يوفر أدوات قوية لذلك عبر F expressions و database functions.
F
تسمح لنا بالإشارة إلى الحقول داخل قاعدة البيانات نفسها.
مثال: عندنا موديل Order
فيه price
و discount
:
from django.db.models import F
orders = Order.objects.order_by(F('price') - F('discount'))
النتيجة:
الطلبات يتم ترتيبها بناءً على السعر بعد الخصم.
Django يوفر دوال مثل Lower
, Length
, Concat
وغيرها.
from django.db.models.functions import Length
students = Student.objects.order_by(Length('name'))
النتيجة:
الطلاب مرتبين حسب طول الاسم (من الأقصر إلى الأطول).
from django.db.models.functions import Lower
articles = Article.objects.order_by(Lower('title'))
النتيجة:
عناوين المقالات تُرتب بدون التفرقة بين الحروف الكبيرة والصغيرة.
annotate
يمكننا إضافة حقل افتراضي عبر annotate
ثم الترتيب به:
from django.db.models import Count
articles = Article.objects.annotate(num_comments=Count('comments')).order_by('-num_comments')
النتيجة:
المقالات تُرتب بناءً على عدد التعليقات (الأكثر تعليقات يظهر أولًا).
عند استخدام order_by
قد نصادف مشكلة أن بعض الحقول تحتوي على قيم NULL (فارغة). بشكل افتراضي، قواعد البيانات تتعامل مع هذه القيم بطرق مختلفة عند الترتيب:
في بعض قواعد البيانات (مثل PostgreSQL): القيم NULL
تُعتبر أكبر من أي قيمة أخرى وتظهر في آخر القائمة عند الترتيب التصاعدي.
في قواعد أخرى (مثل SQLite و MySQL): القيم NULL
تظهر في البداية عند الترتيب التصاعدي.
وهذا قد يؤدي إلى سلوك غير متوقع إذا لم نتحكم به.
NullsFirst
و NullsLast
Django يوفر إمكانية تحديد مكان ظهور NULL
بوضوح باستخدام F expressions مع nulls_first
أو nulls_last
:
from django.db.models import F
# ترتيب مع وضع القيم الفارغة في النهاية
books = Book.objects.order_by(F('rating').asc(nulls_last=True))
# ترتيب مع وضع القيم الفارغة في البداية
books = Book.objects.order_by(F('rating').desc(nulls_first=True))
نفترض أن عندنا جدول كتب مع حقل تقييم (rating)، وبعض الكتب بدون تقييم:
id | title | rating |
---|---|---|
1 | Book A | 4.5 |
2 | Book B | NULL |
3 | Book C | 3.8 |
4 | Book D | NULL |
Book.objects.order_by(F('rating').asc(nulls_last=True))
النتيجة:
Book C → Book A → Book B → Book D
(القيم الفارغة في النهاية)
يمكننا استبدال القيم الفارغة بقيم افتراضية عند الاستعلام باستخدام Coalesce:
from django.db.models.functions import Coalesce
books = Book.objects.order_by(Coalesce('rating', 0))
النتيجة:
يتم التعامل مع القيم NULL
وكأنها 0
.
annotate
و aggregate
في Django، يمكننا استخدام annotate و aggregate لإضافة حقول محسوبة أو جمع بيانات، ثم ترتيب النتائج بناءً على هذه الحقول.
هذا مفيد جدًا عند الحاجة لعرض بيانات مشتقة مثل عدد التعليقات، متوسط التقييم، أو مجموع المبيعات.
annotate
نفترض أن لدينا موديل Article
و Comment
، ونريد ترتيب المقالات حسب عدد التعليقات:
from django.db.models import Count
articles = Article.objects.annotate(num_comments=Count('comments')).order_by('-num_comments')
annotate(num_comments=Count('comments'))
يحسب عدد التعليقات لكل مقال.
order_by('-num_comments')
يرتب المقالات بحيث يظهر المقال الأكثر تعليقًا أولًا.
aggregate
aggregate
يحسب قيمة واحدة عبر كل الصفوف، لذا لا نستخدمها عادة مع order_by
.
لكن يمكن استخدام annotate
مع aggregate
للحصول على ملخصات ثم ترتيب:
from django.db.models import Avg
# ترتيب المنتجات حسب متوسط تقييم المستخدمين
products = Product.objects.annotate(avg_rating=Avg('reviews__rating')).order_by('-avg_rating')
النتيجة: المنتجات الأكثر تقييمًا تظهر أولًا.
annotate
يضيف حقولًا مؤقتة فقط داخل QuerySet ولا تُخزن في قاعدة البيانات.
يمكن دمج عدة عمليات annotate وترتيبها حسب أكثر من حقل:
articles = Article.objects.annotate(
num_comments=Count('comments'),
num_likes=Count('likes')
).order_by('-num_comments', '-num_likes')
هنا: ترتيب أولًا حسب عدد التعليقات، وإذا كانت متساوية، يتم الترتيب حسب عدد الإعجابات.
Case
و When
في بعض الحالات قد نحتاج ترتيب البيانات بشكل مخصص وغير قياسي، أي ليس بالترتيب الأبجدي أو الرقمي المباشر.
Django يوفر Case و When لتحقيق هذا النوع من الترتيب الشرطي.
Case
يسمح بإنشاء شروط لترتيب الصفوف.
When
يحدد الشرط والقيمة التي تُستخدم للترتيب.
نفترض أن لدينا موديل Article
مع حقل status
:
class Article(models.Model):
title = models.CharField(max_length=200)
status = models.CharField(max_length=20, choices=[('published', 'Published'), ('draft', 'Draft')])
نريد عرض المقالات بحيث تظهر المقالات المنشورة أولًا ثم المسودة:
from django.db.models import Case, When, Value, IntegerField
articles = Article.objects.order_by(
Case(
When(status='published', then=Value(1)),
When(status='draft', then=Value(2)),
output_field=IntegerField(),
)
)
هنا، كل مقال منشور يحصل على القيمة 1، وكل مسودة 2.
ثم يتم ترتيب QuerySet بناءً على هذه القيم، بحيث تظهر المقالات المنشورة أولًا.
يمكن ترتيب البيانات حسب أكثر من شرط في نفس الوقت:
articles = Article.objects.order_by(
Case(
When(status='published', then=Value(1)),
When(status='draft', then=Value(2)),
When(status='archived', then=Value(3)),
output_field=IntegerField(),
),
'-published_date' # داخل كل حالة، الترتيب حسب تاريخ النشر
)
النتيجة:
المقالات المنشورة، مرتبة بالأحدث أولًا.
ثم المسودات، مرتبة بالأحدث أولًا.
ثم المقالات المؤرشفة.
order_by
استخدام order_by
قوي جدًا ومرن، لكنه قد يؤثر على أداء قاعدة البيانات إذا لم نستخدمه بحذر، خصوصًا مع الجداول الكبيرة أو الاستعلامات المعقدة.
كل حقل إضافي في order_by
يزيد من تعقيد الاستعلام ويؤثر على سرعة التنفيذ.
حاول ترتيب الحقول الأساسية فقط، أو استخدام ترتيب افتراضي في Meta لتقليل التكرار.
ترتيب الحقول المفهرسة أسرع بكثير من الحقول غير المفهرسة.
إذا كنت تستخدم ترتيب متكرر على حقل كبير، فكر في إضافة فهرس (index) له:
class Article(models.Model):
title = models.CharField(max_length=200, db_index=True)
?
)استخدام order_by('?')
يؤدي إلى إعادة ترتيب كل الصفوف عشوائيًا في قاعدة البيانات.
هذا مكلف جدًا على الجداول الكبيرة.
الحلول البديلة:
اختيار صفوف عشوائية باستخدام ID عشوائي أو OFFSET عشوائي.
حفظ ترتيب عشوائي مؤقت في حقل عند الحاجة.
select_related
و prefetch_related
مع order_by
عند ترتيب البيانات عبر علاقات ForeignKey أو ManyToMany، يفضل استخدام:
books = Book.objects.select_related('author').order_by('author__name')
هذا يقلل عدد الاستعلامات ويزيد الأداء بشكل كبير.
استخدم Meta.ordering
للترتيب الافتراضي بدلاً من كتابة order_by
في كل استعلام.
لا تنس مراقبة execution plan إذا كان جدولك كبيرًا جدًا.
جرب ترتيب البيانات على SQL مباشرة إذا كان الأداء حرجًا.
في هذا المقال، استعرضنا كل ما يخص order_by
في Django من الأساسيات إلى المستوى المتقدم:
تعلمنا كيفية ترتيب البيانات بشكل تصاعدي وتنازلي باستخدام order_by
.
رأينا كيفية ترتيب أكثر من حقل وكيفية التعامل مع علاقات ForeignKey و ManyToMany.
استكشفنا الحقول المشتقة والتعبيرات (Expressions) لترتيب البيانات حسب قيم محسوبة.
تعرفنا على كيفية التعامل مع القيم الفارغة (NULL) باستخدام NullsFirst
و NullsLast
.
رأينا استخدام الترتيب العشوائي ومتى يكون مناسبًا وأثره على الأداء.
تعلمنا كيفية دمج order_by
مع annotate
و aggregate
لترتيب البيانات بناءً على الحقول المحسوبة.
استعرضنا الترتيب المتقدم باستخدام Case و When لتطبيق شروط معقدة على ترتيب النتائج.
أخيرًا، ناقشنا أفضل الممارسات والأداء عند استخدام order_by
على قواعد البيانات الكبيرة.
استخدم Meta.ordering
للترتيب الافتراضي لتقليل تكرار الكود.
استخدم order_by
لتغيير الترتيب حسب الحاجة دون تعديل الموديل.
انتبه للأداء عند التعامل مع جداول كبيرة أو علاقات متعددة.
استفد من annotate
و Case
لتطبيق ترتيبات ديناميكية ومعقدة.
باستخدام هذه الأدوات، ستتمكن من التحكم الكامل في طريقة عرض البيانات في تطبيقات Django بشكل مرن، عملي، وفعال.
عند العمل مع Django ORM (Object Relational Mapper)، واحدة من أكثر العمليات شيوعًا هي ترتيب البيانات المستخرجة من قاعدة البيانات. هنا يأتي دور الدالة order_by التي توفرها Django كجزء من واجهة QuerySet API.
مساحة اعلانية