ORM

Django ORMのID検索入門|get・filter・get_object_or_404

Django ORM ID検索

Django ORMのID検索入門
get・filter・get_object_or_404

Django ORMでIDを指定してデータを取得する方法と、存在しないIDへの対処法を解説します。

こんな人向けの記事です

  • IDを指定して1件のデータを取得したい人
  • get, filter, get_object_or_404の使い分けを知りたい人
  • 存在しないデータへのエラーハンドリングを理解したい人

Step 1getメソッドでの取得

IDを指定して1件のデータを取得するには、objects.get()メソッドを使用します。

Python
from myapp.models import Company

# IDが1のデータを取得
company = Company.objects.get(id=1)

print(company.name)          # 株式会社テスト
print(company.founding_date)  # 2024-08-16

get()はQuerySetではなく、モデルのインスタンスを直接返します。そのため、取得結果に対して直接フィールドにアクセスできます。

ポイント: get()はIDだけでなく、任意のフィールドで検索できます。ただし、結果が必ず1件になる条件で使用してください。0件または2件以上の場合はエラーが発生します。

Step 2ビューでの使い方

URLパラメータからIDを受け取り、データを取得するパターンです。

urls.py
from django.urls import path
from . import views

urlpatterns = [
    path("company/<int:pk>/", views.CompanyDetailView.as_view(), name="company_detail"),
]
views.py
from django.views import View
from django.shortcuts import render
from .models import Company

class CompanyDetailView(View):
    def get(self, request, pk):
        company = Company.objects.get(id=pk)
        context = {"company": company}
        return render(request, "myapp/company_detail.html", context)

URLに含まれる<int:pk>の部分がビューの引数pkとして渡されます。これを使ってget(id=pk)でデータを取得します。

Step 3テンプレートでの表示

get()で取得したデータは1件のインスタンスなので、繰り返し処理なしで直接フィールドにアクセスできます。

templates/myapp/company_detail.html
<h1>会社詳細</h1>

<table>
  <tr>
    <th>ID</th>
    <td>{{ company.id }}</td>
  </tr>
  <tr>
    <th>会社名</th>
    <td>{{ company.name }}</td>
  </tr>
  <tr>
    <th>設立日</th>
    <td>{{ company.founding_date }}</td>
  </tr>
</table>

<a href="{% url 'myapp:company_list' %}">一覧に戻る</a>

Step 4存在しないIDの処理

存在しないIDでget()を呼び出すと、DoesNotExist例外が発生します。適切なエラーハンドリングが必要です。

views.py
from django.shortcuts import render, redirect
from django.contrib import messages
from .models import Company

class CompanyDetailView(View):
    def get(self, request, pk):
        try:
            company = Company.objects.get(id=pk)
        except Company.DoesNotExist:
            messages.error(request, "指定された会社は存在しません")
            return redirect("myapp:company_list")

        context = {"company": company}
        return render(request, "myapp/company_detail.html", context)

注意: get()は2つの例外を発生させる可能性があります。Model.DoesNotExist(0件の場合)とModel.MultipleObjectsReturned(2件以上の場合)です。IDによる検索では後者は通常発生しませんが、他のフィールドで検索する場合は注意が必要です。

Step 5get_object_or_404の活用

Djangoには、データが見つからない場合に自動的に404エラーを返す便利なショートカットがあります。

views.py
from django.shortcuts import render, get_object_or_404
from .models import Company

class CompanyDetailView(View):
    def get(self, request, pk):
        # 見つからない場合は自動的に404エラーページを表示
        company = get_object_or_404(Company, id=pk)
        context = {"company": company}
        return render(request, "myapp/company_detail.html", context)

get_object_or_404を使うと、try-exceptを書く必要がなくなり、コードがシンプルになります。実際のWebアプリケーション開発では最も推奨される方法です。

ポイント: get_object_or_404は内部でget()を呼び出し、DoesNotExist例外をHttp404例外に変換します。RESTful APIの設計において、存在しないリソースへのアクセスに404を返すのは正しいHTTPセマンティクスです。

Step 6filterとgetの違い

get()filter()は似ていますが、戻り値と挙動が異なります。

メソッド戻り値0件の場合複数件の場合
get()モデルインスタンスDoesNotExist例外MultipleObjectsReturned例外
filter()QuerySet空のQuerySet全件含むQuerySet
Python
# get() - 1件のインスタンスを返す
company = Company.objects.get(id=1)
print(company.name)  # 直接アクセス可能

# filter() - QuerySetを返す
companies = Company.objects.filter(id=1)
print(companies.first().name)  # first()で1件目を取得

# filter()は存在しなくてもエラーにならない
result = Company.objects.filter(id=9999)
print(result.exists())  # False

IDで1件取得する場合はget()、条件に一致するデータを柔軟に取得する場合はfilter()を使い分けます。

まとめ

  • objects.get(id=pk)でIDを指定して1件のデータを取得できる
  • get()はモデルインスタンスを直接返すため、繰り返し処理なしでフィールドにアクセスできる
  • 存在しないIDではDoesNotExist例外が発生するため、エラーハンドリングが必要
  • get_object_or_404()を使うと、見つからない場合に自動で404を返せる
  • get()は1件確定の取得、filter()は条件検索に使い分ける