Django:DBからオブジェクトを取得します。何も一致しない場合は「なし」


101

データベースからオブジェクトを取得できるDjango関数、または何も一致しない場合はNoneはありますか?

現在、私は次のようなものを使用しています:

foo = Foo.objects.filter(bar=baz)
foo = len(foo) > 0 and foo.get() or None

しかし、それはあまり明確ではなく、どこにでもあるのは面倒です。


2
foo = foo [0] if foo if else else None
Visgean Skeloru

2
Pythonには三項演算子があり、ブール演算子を使用する必要はありません。また、len(foo)悪いことです: " 注:セット内のレコード数を確認するだけの場合は、QuerySetでlen()を使用しないでください。SQLのSELECT COUNTを使用して、データベースレベルでカウントを処理する方がはるかに効率的です。 ()、そしてDjangoはまさにこの理由でcount()メソッドを提供していますfoo = foo[0] if foo.exists() else None
書き直し


1
@ mustafa.0xここでは当てはまらないと思います。ここで、カウントをチェックすると、別のクエリが実行されます。次に、実際のデータを取得するためにもう一度クエリを実行します。これは、フィルタリングしてからカウントする方が理にかなっているケースです...しかし、フィルタリングして呼び出すよりも意味がありませんfirst():P
MicronXD

回答:


88

Django 1.6では、first()Querysetメソッドを使用できます。クエリセットで一致した最初のオブジェクトを返します。一致するオブジェクトがない場合はNoneを返します。

使用法:

p = Article.objects.order_by('title', 'pub_date').first()

19
ここにMultipleObjectsReturned()記載されているように、複数のオブジェクトが見つかった場合は発生しません。あなたはここで
sleepycal '13年

25
@sleepycal同意しない。first()それが仮定したとおりに動作します。クエリ結果の最初のオブジェクトを返し、複数の結果が見つかっても気にしません。返された複数のオブジェクトを確認する必要がある場合は、.get()代わりに使用する必要があります。
Cesar Canassa 14年

7
[編集] OPが言ったように、.get()彼のニーズには適していません。この質問に対する正しい答えは、@ kaapstormによって以下にあり、明らかにより適切な答えです。filter()この方法を悪用すると、予期しない結果が生じる可能性があります。これは、OPがおそらく認識しなかったものです(ここで何かを
見落とし

1
@sleepycalこのアプローチから予期しない結果はどのように生じますか?
HorseloverFat 14

12
データベースの制約がオブジェクト間の一意性を正しく管理していない場合、ロジックが単一のオブジェクト(first()によって選択される)のみであると想定しているが、実際には複数のオブジェクトが存在するというエッジケースに遭遇する可能性があります。first()の代わりにget()を使用すると、を発生させることにより、保護を強化できますMultipleObjectsReturned()。返される結果が複数のオブジェクトを返すことが予期されていない場合は、そのように扱わないでください。ここでこれについて長い議論がありました
スリーピーカル

139

これを行うには2つの方法があります。

try:
    foo = Foo.objects.get(bar=baz)
except model.DoesNotExist:
    foo = None

または、ラッパーを使用できます。

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

このように呼ぶ

foo = get_or_none(Foo, baz=bar)

8
機能に例外を使用しているという事実は好きではありません。これはほとんどの言語で悪い習慣です。Pythonではどうですか?
pcv

18
これがPythonのイテレーションのしくみ(StopIterationを上げる)であり、それが言語のコア部分です。私もtry / except機能の大ファンではありませんが、Pythonicと見なされているようです。
Matt Luongo、2012年

4
@pcv:「ほとんどの」言語に関する信頼できる従来の知恵はありません。たまたま使用する特定の言語について考えていると思いますが、そのような数量詞には注意してください。例外は、Pythonの場合と同様に、遅い(または「十分に速い」)場合があります。質問に戻ると、これはフレームワークの欠点であり、queryset1.6より前にはこれを行う方法が提供されていませんでした。しかし、この構造については-それは単に不器用です。あなたの好きな言語のチュートリアル作者が言ったので、それは「悪」ではありません。グーグルを試してください:「許可ではなく許しを求めなさい」。
Tomasz Gandor 2013

@TomaszGandor:ええ、あなたの好きな言語チュートリアルが言っているように、それは不器用で読みにくいです。例外が発生した場合、標準以外のものが発生していると思います。読みやすさが重要です。しかし、他に合理的な選択肢がない場合は同意します。あなたはそれを選ぶべきであり、悪いことは何も起こりません。
pcv

@pcv getメソッドはreturnを想定していないNoneため、例外は理にかなっています。オブジェクトがそこにあると確信している場合や、オブジェクトがそこにあると「想定されている」場合に使用することになっています。また、このような例外の使用は「大丈夫」と見なされます。get別のコントラクトを持つが必要なので、例外をキャッチして抑制します。これは、後の変更です。
Dan Passaro 2013年

83

sorkiの回答にいくつかのサンプルコードを追加するには(これをコメントとして追加しますが、これは私の最初の投稿であり、コメントを残す十分な評判がないため)、次のようにget_or_noneカスタムマネージャーを実装しました。

from django.db import models

class GetOrNoneManager(models.Manager):
    """Adds get_or_none method to objects
    """
    def get_or_none(self, **kwargs):
        try:
            return self.get(**kwargs)
        except self.model.DoesNotExist:
            return None

class Person(models.Model):
    name = models.CharField(max_length=255)
    objects = GetOrNoneManager()

そして今、私はこれを行うことができます:

bob_or_none = Person.objects.get_or_none(name='Bob')

これはとてもエレガントです。
NotSimon 2014

13

djangoの迷惑な機能を使用することもできます(別の便利な機能があります!)

それをインストールします:

pip install django-annoying

from annoying.functions import get_object_or_None
get_object_or_None(Foo, bar=baz)

9

Foo にカスタムマネージャーを提供します。とても簡単です。コードをカスタムマネージャーで機能させ、モデルにカスタムマネージャーを設定して、Foo.objects.your_new_func(...)です。

汎用関数が必要な場合(カスタムマネージャーを使用するモデルだけでなく、それを使用するため)、独自の関数を記述して、それをpythonパス上のどこかに配置してインポートします。


4

kwargsが複数のオブジェクトを取得する場合にget()関数がこれを発生させるため、マネージャーまたは一般的な関数のいずれを介して行う場合でも、TRYステートメントで「MultipleObjectsReturned」をキャッチすることもできます。

ジェネリック関数に基づく:

def get_unique_or_none(model, *args, **kwargs):
    try:
        return model.objects.get(*args, **kwargs)
    except (model.DoesNotExist, model.MultipleObjectsReturned), err:
        return None

そしてマネージャーで:

class GetUniqueOrNoneManager(models.Manager):
    """Adds get_unique_or_none method to objects
    """
    def get_unique_or_none(self, *args, **kwargs):
        try:
            return self.get(*args, **kwargs)
        except (self.model.DoesNotExist, self.model.MultipleObjectsReturned), err:
            return None

これは他の答えのすべての最良の点を捉えているようです。素敵な1つ:)
エドワードニューウェル2014年

@ emispowder、Noneを返したくない場合、SAMEページに表示される「一致するデータがありません」などの警告メッセージを返したい場合はどうすればよいですか?
ヘレナ

0

QuerySetモデルのallオブジェクトクエリセット以外のクエリセット(存在する場合)から一意のオブジェクト(存在する場合)を取得する場合に、インスタンスにオプションで渡すことができるヘルパー関数のバリエーションを次に示します(例:に属する子アイテムのサブセットから)親インスタンス):

def get_unique_or_none(model, queryset=None, *args, **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(*args, **kwargs)
    except model.DoesNotExist:
        return None

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

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

-1

ほとんどの場合、次のように使用できます。

foo, created = Foo.objects.get_or_create(bar=baz)

Fooテーブルに新しいエントリを追加することが重要でない場合のみ(他の列にはNone / default値があります)

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