オブジェクトが存在する場合はどのように取得しますか、存在しない場合はNoneを取得するにはどうすればよいですか?


222

モデルマネージャーにオブジェクトを取得するように依頼するDoesNotExistと、一致するオブジェクトがない場合に発生します。

go = Content.objects.get(name="baby")

の代わりにDoesNotExist、どうすれgoNone代わりにできますか?

回答:


332

これを行うための「組み込み」の方法はありません。Djangoは毎回DoesNotExist例外を発生させます。pythonでこれを処理する慣用的な方法は、try catchでラップすることです。

try:
    go = SomeModel.objects.get(foo='bar')
except SomeModel.DoesNotExist:
    go = None

私がやったことは、models.Managerをサブクラス化して、safe_get上記のようなコードを作成し、モデルにそのマネージャーを使用することです。そのように書くことができます:SomeModel.objects.safe_get(foo='bar')


7
例外をインポートする代わりに、SomeModel.DoesNotExistを使用することもできます。
スーパーミッチ2014年

195
このソリューションは4行です。私にとってこれは多すぎます。django 1.6 SomeModel.objects.filter(foo='bar').first()では、これを使用して最初の一致を返すか、Noneを返すことができます。次のようなインスタンスがいくつかある場合、失敗しませんqueryset.get()
guettli

9
デフォルトのケースを処理するために例外を使いすぎるのは悪いスタイルだと思います。はい、「許可よりも許しを求める方が簡単です」。しかし、私の目では、例外を例外として使用する必要があります。
Konstantin Schubert

8
明示的は暗黙的よりも優れています。使用するパフォーマンス上の理由がない限り、filter().first()私は例外が行く方法だと思います。
christianbundy

6
first()を使用することは、複数がある場合に気にしない場合にのみ有効です。それ以外の場合は、このソリューションの方が優れています。予期せずに複数のオブジェクトを見つけた場合でも例外がスローされるため、通常はその場合に発生するはずです。
rossdavidh

181

django 1.6以降では、first()メソッドを次のように使用できます。

Content.objects.filter(name="baby").first()

26
この場合、一致が複数ある場合でもエラーは発生しません。
Konstantin Schubert

7
'FeroxTL'は、投稿の1年前に承認された回答について彼がコメントしたため、この回答には@guettliを表示する必要があります。
colm.anseo 2016年

7
@colminator私はむしろ、guettliがスタックオーバーフローの評判を上げたい場合、新しい回答はコメントとして属さないことを学ぶべきだと言います:) FeroxTLは、コメントとして非表示にしたものを回答としてより明確にするためのポイントを取得する必要があります。あなたのコメントはguettliにとって十分信用できると思います。それがあなたの提案であった場合は、回答に追加しないでください。
Joakim

3
@Joakim私は新しい「回答」を投稿しても問題ありません-それが期限である場所にクレジットを与えるだけです:-)
colm.anseo

3
受け入れられた回答と比較して、このアプローチのパフォーマンスはどうですか?
MaxCore

36

django docsから

get()DoesNotExist指定されたパラメーターのオブジェクトが見つからない場合は、例外が発生します。この例外は、モデルクラスの属性でもあります。DoesNotExist から、例外継承django.core.exceptions.ObjectDoesNotExist

あなたは例外をキャッチしNoneて行くために割り当てることができます。

from django.core.exceptions import ObjectDoesNotExist
try:
    go  = Content.objects.get(name="baby")
except ObjectDoesNotExist:
    go = None

33

このためのジェネリック関数を作成できます。

def get_or_none(classmodel, **kwargs):
    try:
        return classmodel.objects.get(**kwargs)
    except classmodel.DoesNotExist:
        return None

以下のようにこれを使用してください:

go = get_or_none(Content,name="baby")

一致するエントリがない場合、goはNoneになり、それ以外はContentエントリを返します。

注:name = "baby"に対して複数のエントリが返された場合は、MultipleObjectsReturned例外が発生します。



14

物事を簡単にするために、ここに素晴らしい返信からの入力に基づいて、私が書いたコードのスニペットを示します。

class MyManager(models.Manager):

    def get_or_none(self, **kwargs):
        try:
            return self.get(**kwargs)
        except ObjectDoesNotExist:
            return None

そして、あなたのモデルでは:

class MyModel(models.Model):
    objects = MyManager()

それでおしまい。これで、MyModel.objects.get()とMyModel.objetcs.get_or_none()ができました。


7
また、インポートすることを忘れないでください:django.core.exceptionsからインポートObjectDoesNotExist
Moti Radomski 14

13

あなたはexistsフィルターと一緒に使うことができます:

Content.objects.filter(name="baby").exists()
#returns False or True depending on if there is anything in the QS

それが存在するかどうかを知りたいだけの場合の代替手段


4
存在する場合、追加のデータベース呼び出しが発生します。良い考えではありません
Christoffer

@Christofferは、なぜそれが余分なdb呼び出しになるのかわかりません。あたりとしてドキュメントNote: If you only want to determine if at least one result exists (and don’t need the actual objects), it’s more efficient to use exists().
アヌパム

2
@Christoffer私はあなたが正しいと思います。私は今質問をもう一度読み、OPは実際に実際のオブジェクトが返されることを望んでいます。そのため、オブジェクトをフェッチする前にexists()with if句とともに使用され、dbにダブルヒットを引き起こします。他の人を助けるためにコメントを残しておきます。
Anupam

7

ビューのさまざまなポイントで例外を処理することは、非常に煩雑になる可能性があります。models.pyファイルで、次のようにカスタムModel Managerを定義することについてはどうでしょうか。

class ContentManager(model.Manager):
    def get_nicely(self, **kwargs):
        try:
            return self.get(kwargs)
        except(KeyError, Content.DoesNotExist):
            return None

そしてそれをコンテンツモデルクラスに含めます

class Content(model.Model):
    ...
    objects = ContentManager()

このようにして、ビューで簡単に処理できます。

post = Content.objects.get_nicely(pk = 1)
if post:
    # Do something
else:
    # This post doesn't exist

1
私はこのソリューションが本当に好きですが、Python 3.6を使用するときにそれをそのまま機能させることができませんでした。ContentManagerのリターンを変更して機能させるためのメモを残したかっreturn self.get(**kwargs)た。答えに誤りがあると言っているのではなく、それを新しいバージョンで使用しようとしている人のためのヒント(または他のバージョンでそれが私のために機能しないようにしたもの)にすぎません。
skagzilla 2017年

7

これは、再実装したくないかもしれない迷惑な機能の1つです。

from annoying.functions import get_object_or_None
#...
user = get_object_or_None(Content, name="baby")

私はコードをチェックしget_object_or_NoneましたがMultipleObjectsReturned、複数のオブジェクトがある場合でもまだ発生することがわかりました。したがって、ユーザーはtry-except(関数自体がtry-exceptすでに持っている)で囲むことを検討する場合があります。
John Pang

4

例外処理、条件ステートメント、またはDjango 1.6以降の要件を含まないシンプルな1行のソリューションが必要な場合は、代わりに次のようにします。

x = next(iter(SomeModel.objects.filter(foo='bar')), None)

4

使うのは悪くないと思います get_object_or_404()

from django.shortcuts import get_object_or_404

def my_view(request):
    my_object = get_object_or_404(MyModel, pk=1)

この例は、次と同等です。

from django.http import Http404

def my_view(request):
    try:
        my_object = MyModel.objects.get(pk=1)
    except MyModel.DoesNotExist:
        raise Http404("No MyModel matches the given query.")

get_object_or_404()の詳細については、djangoオンラインドキュメントをご覧ください。


2

django 1.7以降では、次のようにできます。

class MyQuerySet(models.QuerySet):

    def get_or_none(self, **kwargs):
        try:
            return self.get(**kwargs)
        except self.model.DoesNotExist:
            return None


class MyBaseModel(models.Model):

    objects = MyQuerySet.as_manager()


class MyModel(MyBaseModel):
    ...

class AnotherMyModel(MyBaseModel):
    ...

「MyQuerySet.as_manager()」の利点は、次の両方が機能することです。

MyModel.objects.filter(...).get_or_none()
MyModel.objects.get_or_none()

1

ここでは、必要に応じて渡すことを可能にするヘルパー関数のバリエーションだQuerySetインスタンスは、モデルの以外のクエリセットから一意のオブジェクト(存在する場合)を取得したい場合には、all例えばAに属する子アイテムのサブセットからのオブジェクトのクエリセットを(親インスタンス):

def get_unique_or_none(model, queryset=None, **kwargs):
    """
        Performs the query on the specified `queryset`
        (defaulting to the `all` queryset of the `model`'s default manager)
        and returns the unique object matching the given
        keyword arguments.  Returns `None` if no match is found.
        Throws a `model.MultipleObjectsReturned` exception
        if more than one match is found.
    """
    if queryset is None:
        queryset = model.objects.all()
    try:
        return queryset.get(**kwargs)
    except model.DoesNotExist:
        return None

これは次の2つの方法で使用できます。

  1. obj = get_unique_or_none(Model, **kwargs) 以前に議論したように
  2. obj = get_unique_or_none(Model, parent.children, **kwargs)

1

例外なく:

if SomeModel.objects.filter(foo='bar').exists():
    x = SomeModel.objects.get(foo='bar')
else:
    x = None

例外を使用する:

try:
   x = SomeModel.objects.get(foo='bar')
except SomeModel.DoesNotExist:
   x = None

Pythonでいつ例外を使用する必要があるかについては、少し議論があります。一方で、「許可よりも許しを求める方が簡単です」。私はこれに同意しますが、例外はそのままにしておくべきだと思います。例外と「理想的なケース」は、例外を打つことなく実行されるべきです。


1

という名前のモデルにアタッチされているDjango組み込み例外を使用できます.DoesNotExist。したがって、ObjectDoesNotExist例外をインポートする必要はありません。

代わりに:

from django.core.exceptions import ObjectDoesNotExist

try:
    content = Content.objects.get(name="baby")
except ObjectDoesNotExist:
    content = None

できるよ:

try:
    content = Content.objects.get(name="baby")
except Content.DoesNotExist:
    content = None

0

これは、メソッドがNoneを返すことを除いて、Djangoのget_object_or_404からの模倣品です。これは、only()クエリを使用して特定のフィールドのみを取得する必要がある場合に非常に役立ちます。このメソッドは、モデルまたはクエリセットを受け入れることができます。

from django.shortcuts import _get_queryset


def get_object_or_none(klass, *args, **kwargs):
    """
    Use get() to return an object, or return None if object
    does not exist.
    klass may be a Model, Manager, or QuerySet object. All other passed
    arguments and keyword arguments are used in the get() query.
    Like with QuerySet.get(), MultipleObjectsReturned is raised if more than
    one object is found.
    """
    queryset = _get_queryset(klass)
    if not hasattr(queryset, 'get'):
        klass__name = klass.__name__ if isinstance(klass, type) else klass.__class__.__name__
        raise ValueError(
            "First argument to get_object_or_none() must be a Model, Manager, "
            "or QuerySet, not '%s'." % klass__name
        )
    try:
        return queryset.get(*args, **kwargs)
    except queryset.model.DoesNotExist:
        return None

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.