Djangoのモデルに該当するデータがないときに404ページを返す方法

Djangoで、例えばURLにプライマリキーを含ませて、viewでそのプライマリキーを変数として受け取る。

そしてその変数でDBに検索をかけて、該当するカラムの情報を抜き出す。というのをやりたいとき。

それは次のようなコードで実現できるかなと思います。

user = User.objects.get(id=id)

しかし上記のコードだけだと、該当するレコードがない場合は Does not exist というエラーが返ってしまいます。

該当するレコードがない場合は404ページを返して、よしなに処理したいというのをやってみました。

ユースケース

Userというモデルを用意し、プロフィールページでUserの情報を表示する。という想定です。

  1. ユーザーはブラウザから、Userのプライマリキーを含んだURLを叩く
  2. Djangoのviewでプライマリキーを変数として受け取る
  3. 受け取った変数をキーにUserモデルの情報を探す
  4. 有ればそのデータを、無ければ404ページをユーザーへ返す

urls.py

URLに変数を含む時の書き方。 uuid:id の部分で変数を受け取り、views.pyへ渡します。

...
path('users/<uuid:id>/', views.profile, name='profile'),
...

*…Userモデルのプライマリキーをuuidとなるように設定していたため、ここでは uuid:id と記述しています。

views.py…方法①

filter()とexists()メソッドを使う方法です。

filter()でフィルターをかけ、exists()で存在すればTrue、無ければFalseが返るので、それを分岐条件にします。

from django.http import Http404

def profile(request, id):
    if User.objects.filter(pk=id).exists():
        user = User.objects.get(pk=id)
        context = {
            'user': user,
        }
        return render(request, 'myapp/profile.html', context)
    else:
        raise Http404("No User matches the given query.")

views.py…方法②

見つからなかった場合「DoesNotExist」というエラーが返ることがわかっているので、try-except構文でよしなに処理すれば良いのかなと。

from django.http import Http404

def my_view(request, id):
    try:
        user = User.objects.get(pk=id)
        context = {
            'user': user,
        }
        return render(request, 'myapp/profile.html', context)
    except User.DoesNotExist:
        raise Http404("No User matches the given query.")

views.py…方法③

get_object_or_404()というメソッドを使う方法です。

これが一番スマートですね。動き的には、方法②と同等の処理をしているようです。

from django.shortcuts import get_object_or_404

def profile(request, id):
    user = get_object_or_404(User, pk=id)
    context = {
        'user': user,
    }
    return render(request, 'myapp/profile.html', context)

まとめ

これでよしなに処理できます。やったね!

404ページをカスタマイズして表示する方法もあるそうな。でも面倒なので気が向いた時に頑張ります。