質問

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

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つ目はもっと回り道ですが、例外は発生しません。

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

役に立ちましたか?

解決

get()は、このケース専用に提供されています。それを使用します。

オプション2は、ほぼ正確に get()メソッドがDjangoで実際に実装される方法です。したがって、「パフォーマンス」はありません。差それがどのように実行されるかわからない、そしてそれ以前に最適化を試みることは苦痛の道である。

他のヒント

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

1は正しいです。 Pythonでは、例外はリターンと同等のオーバーヘッドを持ちます。簡略化された証拠については、これ

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

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

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


  

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

例:

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

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

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

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

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

from django.shortcuts import get_object_or_404

obj = get_object_or_404(MyModel, id=1)

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

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

この問題を少し試してみて、オプション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]

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

興味深い質問ですが、私にとってはオプション2の時期尚早な最適化の続きです。どちらがよりパフォーマンスが良いかはわかりませんが、オプション#1は確かに見た目と感じが私にとってより素晴らしく感じられます。

別のデザインを提案します。

可能な結果に対して機能を実行する場合、次のようにQuerySetから派生できます。 http: //djangosnippets.org/snippets/734/

結果は非常に素晴らしいです。たとえば:

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

ここでは、filterは空のクエリセットまたは単一のアイテムを持つクエリセットを返します。カスタムクエリセット関数もチェーン可能で再利用可能です。すべてのエントリに対して実行する場合: MyModel.objects.all()。yourFunction()

これらは、管理インターフェイスでアクションとして使用するのにも理想的です:

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

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

私自身の経験から、データベースには複数の一致するオブジェクトが存在することはないと確信していることがありますが、2つ存在することがあります...キー)。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top