تعلم طرق استخدام order_by قي اطار عمل Django

 

عند العمل مع 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')

الترتيب التصاعدي (Ascending)

بشكل افتراضي، Django يرتب النتائج بترتيب تصاعدي.
مثال: إذا كان لدينا موديل Student يحتوي على حقل name:

students = Student.objects.order_by('name')

هنا سيتم ترتيب الطلاب أبجديًا من A إلى Z.

الترتيب التنازلي (Descending)

للحصول على ترتيب تنازلي نضيف علامة السالب (-) قبل اسم الحقل:

students = Student.objects.order_by('-name')

النتيجة: ترتيب الطلاب أبجديًا من Z إلى A.

ملاحظات مهمة:

  • يمكنك تمرير اسم حقل واحد أو أكثر إلى order_by.

  • إذا لم تحدد أي ترتيب، فإن قاعدة البيانات ستعيد النتائج بالترتيب الافتراضي (غالبًا حسب المفتاح الأساسي Primary Key).

أمثلة عملية بسيطة

بعد ما عرفنا الأساسيات، خلينا نشوف كيف نستخدم order_by في مواقف يومية داخل مشاريع Django.

المثال 1: ترتيب المقالات حسب تاريخ النشر

إذا عندنا موديل Article يحتوي على title و published_date:

articles = Article.objects.order_by('-published_date')

النتيجة: عرض المقالات الأحدث أولًا (وهو الأكثر شيوعًا في المدونات والمواقع الإخبارية).

المثال 2: ترتيب الطلاب حسب الاسم

في تطبيق تعليمي، نريد عرض الطلاب أبجديًا:

students = Student.objects.order_by('name')

النتيجة: تظهر قائمة الطلاب مرتبة من A إلى Z.

المثال 3: ترتيب المنتجات حسب السعر

في متجر إلكتروني عندنا موديل Product:

products = Product.objects.order_by('price')
  • النتيجة: المنتجات مرتبة من الأرخص إلى الأغلى.

ولو أردنا العكس (الأغلى أولًا):

products = Product.objects.order_by('-price')

المثال 4: ترتيب الطلبات حسب تاريخ الإنشاء

لو عندنا موديل Order:

orders = Order.objects.order_by('-created_at')
  • النتيجة: تظهر الطلبات الأحدث أولًا، وهو شيء مهم في لوحات التحكم الخاصة بالمشرفين.

الترتيب بأكثر من حقل

أحيانًا نحتاج نرتب النتائج ليس بحقل واحد فقط، بل بمجموعة حقول معًا. في هذه الحالة، order_by يسمح لنا بتمرير أكثر من اسم حقل بالترتيب الذي نريده.

الصياغة العامة:

Model.objects.order_by('field1', '-field2')
  • يتم الترتيب أولًا حسب field1.

  • إذا كانت هناك قيم متساوية في field1، يتم كسر التعادل باستخدام field2.

  • يمكن إضافة أكثر من حقلين.

مثال 1: ترتيب الطلاب حسب الصف ثم الاسم

students = Student.objects.order_by('grade', 'name')

النتيجة:

  1. الطلاب مرتبين حسب الصف (grade) من الأصغر إلى الأكبر.

  2. داخل كل صف، الطلاب مرتبين أبجديًا بالاسم.

مثال 2: ترتيب الموظفين حسب القسم ثم الراتب تنازليًا

employees = Employee.objects.order_by('department', '-salary')

النتيجة:

  • كل قسم (department) يظهر معًا.

  • داخل كل قسم، الموظفون مرتبين من الأعلى راتبًا إلى الأقل.


مثال 3: ترتيب المنتجات حسب الفئة ثم تاريخ الإضافة

products = Product.objects.order_by('category', '-created_at')

النتيجة:

  • المنتجات grouped حسب الفئة.

  • أحدث منتج يظهر أولًا داخل كل فئة.

العلاقة مع Meta داخل الموديل

إلى جانب استخدام order_by مباشرة في الاستعلامات، يوفر Django طريقة أخرى لتعريف ترتيب افتراضي للبيانات من خلال class Meta داخل الموديل.

1. استخدام 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.

2. الترتيب التنازلي في Meta

لجعل الترتيب تنازليًا نضيف السالب (-) قبل الحقل:

class Meta:
    ordering = ['-join_date']   # الأحدث أولًا

3. الفرق بين ordering و order_by

  • ordering في Meta: تحدد الترتيب الافتراضي لكل الاستعلامات على هذا الموديل.

  • order_by: تستخدم عند الحاجة لتغيير الترتيب في استعلام معين.

  • الأولوية لـ order_by: إذا استخدمت order_by في استعلام، سيتجاهل الترتيب الموجود في Meta.

مثال عملي:

# ترتيب افتراضي من Meta (الأحدث أولًا)
articles = Article.objects.all()

# كسر الترتيب الافتراضي وعرض الأقدم أولًا
articles = Article.objects.order_by('published_date')

استخدام الترتيب مع العلاقات (ForeignKey / ManyToMany)

في Django غالبًا يكون عندنا جداول مرتبطة ببعض (علاقات ForeignKey أو ManyToMany)، ومرات نحتاج نرتب البيانات بناءً على حقل موجود في الجدول المرتبط.

1. الترتيب عبر العلاقة (ForeignKey)

نفترض أن عندنا موديل كتاب 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')

النتيجة:
الكتب تظهر مرتبة حسب اسم الكاتب أبجديًا.

2. الترتيب مع ManyToMany

مثال: موديل 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')

النتيجة:
الطلاب يُرتبون بناءً على اسم المقرر الذي يرتبطون به (قد يظهر الطالب أكثر من مرة إذا له أكثر من مقرر).

3. الترتيب عبر العلاقات المتداخلة

ممكن نستعمل __ أكثر من مرة إذا عندنا علاقات متداخلة.
مثال: لو أردنا ترتيب الكتب حسب اسم مدينة الكاتب (لو عند الكاتب حقل city):

books = Book.objects.order_by('author__city')

الحقول المشتقة والتعبيرات (Expressions)

في بعض الحالات ما نحتاج نرتب فقط على الحقول المخزنة في قاعدة البيانات، بل نريد الترتيب بناءً على قيمة مشتقة أو عملية حسابية.
Django يوفر أدوات قوية لذلك عبر F expressions و database functions.

1. الترتيب باستخدام F expressions

F تسمح لنا بالإشارة إلى الحقول داخل قاعدة البيانات نفسها.
مثال: عندنا موديل Order فيه price و discount:

from django.db.models import F

orders = Order.objects.order_by(F('price') - F('discount'))

النتيجة:
الطلبات يتم ترتيبها بناءً على السعر بعد الخصم.

2. الترتيب باستخدام الدوال المدمجة (Database Functions)

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'))

النتيجة:
عناوين المقالات تُرتب بدون التفرقة بين الحروف الكبيرة والصغيرة.

3. الترتيب باستخدام القيم المحسوبة مع annotate

يمكننا إضافة حقل افتراضي عبر annotate ثم الترتيب به:

from django.db.models import Count

articles = Article.objects.annotate(num_comments=Count('comments')).order_by('-num_comments')

النتيجة:
المقالات تُرتب بناءً على عدد التعليقات (الأكثر تعليقات يظهر أولًا).

التعامل مع Null والقيم الفارغة

عند استخدام order_by قد نصادف مشكلة أن بعض الحقول تحتوي على قيم NULL (فارغة). بشكل افتراضي، قواعد البيانات تتعامل مع هذه القيم بطرق مختلفة عند الترتيب:

  • في بعض قواعد البيانات (مثل PostgreSQL): القيم NULL تُعتبر أكبر من أي قيمة أخرى وتظهر في آخر القائمة عند الترتيب التصاعدي.

  • في قواعد أخرى (مثل SQLite و MySQL): القيم NULL تظهر في البداية عند الترتيب التصاعدي.

وهذا قد يؤدي إلى سلوك غير متوقع إذا لم نتحكم به.

1. الترتيب مع 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))

2. مثال عملي

نفترض أن عندنا جدول كتب مع حقل تقييم (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

(القيم الفارغة في النهاية)

3. الحلول البديلة

  • يمكننا استبدال القيم الفارغة بقيم افتراضية عند الاستعلام باستخدام Coalesce:

from django.db.models.functions import Coalesce

books = Book.objects.order_by(Coalesce('rating', 0))

النتيجة:
يتم التعامل مع القيم NULL وكأنها 0.

الترتيب مع annotate و aggregate

في Django، يمكننا استخدام annotate و aggregate لإضافة حقول محسوبة أو جمع بيانات، ثم ترتيب النتائج بناءً على هذه الحقول.
هذا مفيد جدًا عند الحاجة لعرض بيانات مشتقة مثل عدد التعليقات، متوسط التقييم، أو مجموع المبيعات.

1. الترتيب بعد 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') يرتب المقالات بحيث يظهر المقال الأكثر تعليقًا أولًا.

2. الترتيب بعد 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')
  • النتيجة: المنتجات الأكثر تقييمًا تظهر أولًا.

3. ملاحظات عملية

  • 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 لتحقيق هذا النوع من الترتيب الشرطي.

1. الفكرة الأساسية

  • Case يسمح بإنشاء شروط لترتيب الصفوف.

  • When يحدد الشرط والقيمة التي تُستخدم للترتيب.

2. مثال عملي: ترتيب المقالات حسب الحالة

نفترض أن لدينا موديل 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 بناءً على هذه القيم، بحيث تظهر المقالات المنشورة أولًا.

3. مثال متقدم: ترتيب حسب عدة شروط

يمكن ترتيب البيانات حسب أكثر من شرط في نفس الوقت:

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'  # داخل كل حالة، الترتيب حسب تاريخ النشر
)
  • النتيجة:

    1. المقالات المنشورة، مرتبة بالأحدث أولًا.

    2. ثم المسودات، مرتبة بالأحدث أولًا.

    3. ثم المقالات المؤرشفة.

الأداء وأفضل الممارسات عند استخدام order_by

استخدام order_by قوي جدًا ومرن، لكنه قد يؤثر على أداء قاعدة البيانات إذا لم نستخدمه بحذر، خصوصًا مع الجداول الكبيرة أو الاستعلامات المعقدة.

1. الانتباه لعدد الحقول المرتبة

  • كل حقل إضافي في order_by يزيد من تعقيد الاستعلام ويؤثر على سرعة التنفيذ.

  • حاول ترتيب الحقول الأساسية فقط، أو استخدام ترتيب افتراضي في Meta لتقليل التكرار.

2. الترتيب على الحقول المفهرسة (Indexed Fields)

  • ترتيب الحقول المفهرسة أسرع بكثير من الحقول غير المفهرسة.

  • إذا كنت تستخدم ترتيب متكرر على حقل كبير، فكر في إضافة فهرس (index) له:

class Article(models.Model):
    title = models.CharField(max_length=200, db_index=True)

3. الحذر مع الترتيب العشوائي (?)

  • استخدام order_by('?') يؤدي إلى إعادة ترتيب كل الصفوف عشوائيًا في قاعدة البيانات.

  • هذا مكلف جدًا على الجداول الكبيرة.

  • الحلول البديلة:

    • اختيار صفوف عشوائية باستخدام ID عشوائي أو OFFSET عشوائي.

    • حفظ ترتيب عشوائي مؤقت في حقل عند الحاجة.

4. استخدام select_related و prefetch_related مع order_by

  • عند ترتيب البيانات عبر علاقات ForeignKey أو ManyToMany، يفضل استخدام:

books = Book.objects.select_related('author').order_by('author__name')
  • هذا يقلل عدد الاستعلامات ويزيد الأداء بشكل كبير.

5. نصائح عامة

  • استخدم 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.

هل كان هذا مفيدًا لك؟

أضف تعليقك

قد يعجبك: