ORM

Django ORMのorder_by入門|データを昇順・降順に並び替える方法

Django ORM

Django ORMのorder_by入門
データを昇順・降順に並び替える

Django ORMのorder_by()メソッドを使って、データを昇順・降順に並び替える方法を解説します。

こんな人向けの記事です

  • Django ORMでデータの並び替え方法を学びたい人
  • 複数フィールドでの並び替えを知りたい人
  • ランダム順やNULL値の扱いを理解したい人

Step 1order_by()の基本

QuerySetのorder_by()メソッドでデータの並び順を指定します。フィールド名の前に-を付けると降順になります。

Python
# models.py
from django.db import models

class Employee(models.Model):
    name = models.CharField(max_length=100)
    age = models.IntegerField()
    salary = models.IntegerField()
    hired_date = models.DateField()
    department = models.CharField(max_length=50)

    def __str__(self):
        return self.name
Python
# 昇順(小さい順): フィールド名をそのまま指定
employees = Employee.objects.order_by("age")
for emp in employees:
    print(f"{emp.name}: {emp.age}歳")

print("---")

# 降順(大きい順): フィールド名の前に - を付ける
employees = Employee.objects.order_by("-salary")
for emp in employees:
    print(f"{emp.name}: {emp.salary}円")
実行結果
佐藤花子: 25歳
鈴木一郎: 30歳
田中太郎: 35歳
---
田中太郎: 600000円
鈴木一郎: 450000円
佐藤花子: 350000円

Step 2複数フィールドでの並び替え

複数のフィールドを指定すると、第1キー、第2キーの順で並び替えられます。

Python
# 部署で昇順 → 同じ部署内では年齢で降順
employees = Employee.objects.order_by("department", "-age")
for emp in employees:
    print(f"{emp.department} | {emp.name}: {emp.age}歳")

# filter()と組み合わせる
employees = Employee.objects.filter(
    department="開発部"
).order_by("-salary")

# order_by()をチェーンすると、後の指定が優先される
employees = Employee.objects.order_by("name").order_by("age")
# → ageで並び替えられる(nameの指定は上書きされる)
実行結果
営業部 | 田中太郎: 35歳
営業部 | 佐藤花子: 25歳
開発部 | 鈴木一郎: 30歳
開発部 | 山田次郎: 28歳
order_by()のチェーンに注意
order_by()を複数回チェーンすると、最後に指定した並び順だけが有効になります。複数フィールドで並び替えたい場合は、1回のorder_by()に複数の引数を渡してください。

Step 3Metaクラスでデフォルト順を指定

モデルのMetaクラスでorderingを指定すると、order_by()を呼ばなくても常にその順序で取得されます。

Python
# models.py
class Employee(models.Model):
    name = models.CharField(max_length=100)
    age = models.IntegerField()
    hired_date = models.DateField()

    class Meta:
        ordering = ["-hired_date"]  # 入社日の降順がデフォルト

    def __str__(self):
        return self.name
Python
# orderingが指定されているため、自動的に入社日降順で取得
employees = Employee.objects.all()

# デフォルトの並び順を解除したい場合
employees = Employee.objects.order_by()  # 引数なしで並び順リセット

# デフォルトの並び順を別の順に上書き
employees = Employee.objects.order_by("name")
orderingの影響
Meta.orderingを指定すると全てのクエリにORDER BY句が追加されます。パフォーマンスに影響する場合があるので、必要なときだけorder_by()で指定するという設計も検討しましょう。

Step 4ランダム順とNULL値の制御

Python
from django.db.models import F

# ランダムな順序で取得
employees = Employee.objects.order_by("?")

# ランダムに1件取得
random_employee = Employee.objects.order_by("?").first()
print(f"ランダム: {random_employee.name}")

# NULLの並び順を制御(Django 4.0以降)
from django.db.models.functions import Coalesce
from django.db.models import Value

# NULL値を最後にする
employees = Employee.objects.order_by(
    F("hired_date").asc(nulls_last=True)
)

# NULL値を最初にする
employees = Employee.objects.order_by(
    F("hired_date").desc(nulls_first=True)
)
ランダム順のパフォーマンス
order_by("?")はデータベースのORDER BY RANDOM()に変換されます。データ件数が多い場合はパフォーマンスが低下するため、大量データでのランダム取得にはIDを使った別の方法を検討してください。

Step 5実践的な並び替え

Python
from django.db.models import Count, Avg

# 関連モデルのフィールドで並び替え(JOINが発行される)
# employees = Employee.objects.order_by("department__name")

# アノテーションを使った並び替え
# 部署ごとの社員数で並び替え
from django.db.models import Count

departments = Employee.objects.values("department").annotate(
    emp_count=Count("id")
).order_by("-emp_count")
for dept in departments:
    print(f"{dept['department']}: {dept['emp_count']}人")

# 条件付きの並び替え(Case/When)
from django.db.models import Case, When, IntegerField

priority_order = Employee.objects.annotate(
    dept_priority=Case(
        When(department="役員", then=0),
        When(department="管理部", then=1),
        When(department="開発部", then=2),
        default=3,
        output_field=IntegerField(),
    )
).order_by("dept_priority", "name")

# ビューでのソート切り替え
def employee_list(request):
    sort = request.GET.get("sort", "name")
    direction = request.GET.get("dir", "asc")

    valid_sorts = ["name", "age", "salary", "hired_date"]
    if sort not in valid_sorts:
        sort = "name"

    order_field = f"-{sort}" if direction == "desc" else sort
    employees = Employee.objects.order_by(order_field)

    return render(request, "employee/list.html", {
        "employees": employees,
        "current_sort": sort,
        "current_dir": direction,
    })

まとめ

  • order_by("field")で昇順、order_by("-field")で降順に並び替え
  • 複数フィールドはorder_by("field1", "-field2")のように1回で指定する
  • Meta.orderingでデフォルトの並び順を設定できる
  • order_by("?")でランダム順、nulls_last=TrueでNULLの位置を制御
  • Case/Whenやアノテーションで高度な並び替えも可能