160

私は何人かの同僚とこれについて議論していました。1 つだけを期待しているときに、Django でオブジェクトを取得するための推奨される方法はありますか?

2 つの明白な方法は次のとおりです。

try:
    obj = MyModel.objects.get(id=1)
except MyModel.DoesNotExist:
    # We have no object! Do something...
    pass

と:

objs = MyModel.objects.filter(id=1)

if len(objs) == 1:
    obj = objs[0]
else:
    # We have no object! Do something...
    pass

最初の方法は動作的にはより正しいように見えますが、制御フローで例外を使用するため、オーバーヘッドが発生する可能性があります。2 つ目はより回りくどいですが、例外を発生させることはありません。

これらのどれが好ましいかについて何か考えはありますか? どちらがより効率的ですか?

4

14 に答える 14

193

get()この場合のために特別に用意されています。これを使って。

オプション 2 は、メソッドが Django で実際にどのように実装されているかをほぼ正確get()に表しているため、「パフォーマンス」の違いはないはずです (そして、それについて考えているという事実は、プログラミングの基本的なルールの 1 つに違反していることを示しています。コードを作成してプロファイリングする前にコードを最適化します。コードを入手して実行できるようになるまでは、コードがどのように実行されるかわかりません。それ以前に最適化を試みるのは苦痛です)。

于 2009-06-19T20:04:19.443 に答える
31

django-annoyingというモジュールをインストールして、次のようにすることができます。

from annoying.functions import get_object_or_None

obj = get_object_or_None(MyModel, id=1)

if not obj:
    #omg the object was not found do some error stuff
于 2009-06-20T02:19:06.907 に答える
18

1が正解です。Python では、例外のオーバーヘッドは戻り値と同じです。簡単な証明については、これを見ることができます。

2 これは Django がバックエンドで行っていることです。アイテムが見つからない場合、または複数のオブジェクトが見つかった場合は、get呼び出して例外を発生させます。filter

于 2009-06-19T20:08:30.043 に答える
9

なぜすべてが機能するのですか?4 行を 1 つの組み込みショートカットに置き換えます。(これは独自の try/except を行います。)

from django.shortcuts import get_object_or_404

obj = get_object_or_404(MyModel, id=1)
于 2009-06-20T01:25:03.430 に答える
8

Django の経験について話すことはできませんが、オプション #1 は、1 つのオブジェクトを要求していることをシステムに明確に伝えますが、2 番目のオプションはそうではありません。これは、オプション #1 がキャッシュまたはデータベース インデックスをより簡単に利用できることを意味します。特に、フィルター処理している属性が一意であることが保証されていない場合に有効です。

また、(再び推測しますが) 2 番目のオプションでは、filter() 呼び出しは通常多くの行を返す可能性があるため、何らかの結果コレクションまたは反復子オブジェクトを作成する必要がある場合があります。get() でこれをバイパスします。

最後に、最初のオプションは短く、余分な一時変数を省略しています。わずかな違いですが、少しでも役に立ちます。

于 2009-06-19T16:45:23.160 に答える
7

例外に関する詳細情報。彼らが育てられなければ、ほとんど費用はかかりません。したがって、結果が得られる可能性が高いことがわかっている場合は、例外を使用してください。条件式を使用すると、何があっても毎回チェックするコストがかかるからです。一方、それらは発生時に条件式よりも少し多くのコストがかかるため、ある程度の頻度で結果が得られないと予想される場合 (たとえば、メモリが機能する場合は 30% の時間)、条件付きチェックが判明します。少し安くなるように。

しかし、これは Django の ORM であり、おそらくデータベースへのラウンドトリップ、またはキャッシュされた結果でさえ、パフォーマンス特性を支配する可能性が高いため、読みやすさを優先しますget()

于 2009-06-20T01:32:03.853 に答える
4

この問題を少し試してみたところ、オプション 2 が 2 つの SQL クエリを実行することがわかりました。これは、このような単純なタスクでは過剰です。私の注釈を参照してください:

objs = MyModel.objects.filter(id=1) # This does not execute any SQL
if len(objs) == 1: # This executes SELECT COUNT(*) FROM XXX WHERE filter
    obj = objs[0]  # This executes SELECT x, y, z, .. FROM XXX WHERE filter
else: 
    # we have no object!  do something
    pass

単一のクエリを実行する同等のバージョンは次のとおりです。

items = [item for item in MyModel.objects.filter(id=1)] # executes SELECT x, y, z FROM XXX WHERE filter
count = len(items) # Does not execute any query, items is a standard list.
if count == 0:
   return None
return items[0]

このアプローチに切り替えることで、アプリケーションが実行するクエリの数を大幅に減らすことができました。

于 2013-01-16T11:46:08.337 に答える
1

興味深い質問ですが、私にとってはオプション #2 は時期尚早の最適化の悪臭を放っています。どちらがよりパフォーマンスが高いかはわかりませんが、オプション#1は確かに見た目も感じもPythonicです。

于 2009-06-19T16:36:26.263 に答える
0

オプション 1 はよりエレガントですが、必ず try..except を使用してください。

私自身の経験から、データベース内に一致するオブジェクトが複数存在する可能性はないと確信している場合がありますが、それでも2つ存在する可能性があります... (もちろん、主キーでオブジェクトを取得する場合を除く)。

于 2009-06-22T11:40:02.477 に答える