2009-06-19 11 views
108

私はこれについていくつかの同僚と議論していました。 Djangoでオブジェクトを取得する方法はありますか?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つ目はよりラウンドアバウトですが、例外は発生しません。

これらのいずれかの考えが良いですか?どちらがより効率的ですか?

答えて

137

get()は、このケース専用です。これを使って。

オプション2は実際にDjangoで実際に実装されている方法とほぼ同じですので、 "パフォーマンス"の違いはありません(また、それについて考えているという事実は、あなたがつまりコードを作成して実行するまでは、そのコードがどのように実行されるか分からず、その前に最適化しようとすると痛みの道筋になります)。

1

興味深い質問ですが、私にとっては、早期最適化のオプション#2があります。私はどちらがより演奏的であるかはわかりませんが、オプション#1は確かに見えて、私にはもっと違和感を感じます。

6

私はDjangoの経験について話すことができませんが、オプション1は、1つのオブジェクトを要求していることをシステムに明確に示しています。つまり、オプション1は、特にフィルタリングしている属性が一意であることが保証されていない場合に、キャッシュまたはデータベースのインデックスをより簡単に活用できます。

また、2番目のオプションは、通常、filter()呼び出しが多くの行を返すため、何らかの種類の結果コレクションまたはイテレータオブジェクトを作成する必要があります。これをget()でバイパスします。

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

+0

ジャンゴではありませんが、それでも経験上のスポット。明示的で簡潔で安全なのは、言語やフレームワークに関係なく、良い原則です。 – nevelis

13

1が正しいです。 Pythonでは、例外にはリターンと等しいオーバーヘッドがあります。簡単な証明のために、thisを見ることができます。

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

+1

そのテストはかなり不公平です。例外をスローする際のオーバーヘッドの大部分は、スタックトレースの処理です。このテストのスタック長は1で、アプリケーションで通常見つけられるよりもはるかに低かった。 –

+0

@ロブヤング:どういう意味ですか?典型的な "許可よりむしろ許しを求める"スキームでスタックトレース処理はどこにありますか?処理時間は、例外が移動する距離に依存します(Javaで記述してe.printStackTrace()を呼び出していない場合)。そして、ほとんどの場合(辞書検索のように)、例外は 'try'のすぐ下にスローされます。 –

5

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

from django.shortcuts import get_object_or_404 

obj = get_object_or_404(MyModel, id=1) 
+1

これは望ましい動作ですが、欠落しているオブジェクトを作成したい場合や、プルがオプションの情報だった場合に便利です。 – SingleNegationElimination

+1

これは 'Model.objects.get_or_create()'が – boatcoder

6

例外に関する詳細をいくつか示します。彼らが育てられなければ、ほとんど費用はかかりません。したがって、おそらく結果が出ることがわかっている場合は、条件式を使用するたびに毎回チェックするコストを支払うので、例外を使用します。一方、飼育時には条件式よりも少しコストがかかるので、何らかの頻度で結果が得られないと予想される場合(たとえば、メモリの場合は30%)、条件チェックが行われます少し安くなる。

これはDjangoのORMであり、おそらくデータベースへのラウンドトリップやキャッシュされた結果であっても、パフォーマンス特性を支配する可能性が高いため、正確に1つの結果が期待されるため、 get()

27

あなたはdjango-annoyingと呼ばれるモジュールをインストールし、これを行うことができます。オプション1は、よりエレガントですが、try..except使用してください

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 
+0

のためのものです。そのような方法を持つのはなぜ迷惑なのですか?私にはうまく見える! – Thomas

+2

@トーマス私は、そのような方法をとらないのは面倒だと考えています。 – user193130

0

を。

自分自身の経験から、データベースに複数の一致するオブジェクトが存在しないことがありますが、まだ2つありますと言うことができます。主キー)。

1

私は別のデザインを提案します。あなたが可能な結果に機能を実行したい場合は

、あなたはこのように、クエリセットから派生することができます:http://djangosnippets.org/snippets/734/

結果はかなり素晴らしいです、あなたは例えばでした:

ここ
MyModel.objects.filter(id=1).yourFunction() 

、フィルターは空のクエリーセットまたは単一項目のクエリーセットを返します。カスタムクエリーセットの機能も連鎖可能で再利用可能です。あなたがすべてのあなたのエントリのためにそれを実行する場合:MyModel.objects.all().yourFunction()

彼らはまた、管理インターフェースでアクションとして使用することが理想的です:

def yourAction(self, request, queryset): 
    queryset.yourFunction() 
3

私はこの問題にビットを果たし、オプション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] 

をこのアプローチに切り替えることで、私は、実質的に自分のアプリケーションが実行されるクエリの数を削減することができました。

6

私はパーティーに少し遅れましたが、Django 1.6ではクエリセットにfirst()メソッドがあります。

https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.first


一致するオブジェクトが存在しない場合は、最初のクエリセットにマッチしたオブジェクト、またはNoneを返します。 QuerySetに順序付けが定義されていない場合、クエリーセットは主キーによって自動的に順序付けされます。

例:

p = Article.objects.order_by('title', 'pub_date').first() 
Note that first() is a convenience method, the following code sample is equivalent to the above example: 

try: 
    p = Article.objects.order_by('title', 'pub_date')[0] 
except IndexError: 
    p = None 
関連する問題