Djangoでは、querysetはget_or_createと呼ばれるメソッドを提供し、オブジェクトを返すかオブジェクトを作成します。
ただし、getメソッドと同様に、クエリが複数のオブジェクトを返す場合、get_or_createは例外をスローできます。
これをエレガントに行う方法はありますか?
objects = Model.manager.filter(params)
if len(objects) == 0:
obj = Model.objects.create(params)
else:
obj = objects[0]
get_or_create() は単なる便利な関数であるため、pavidが示しているように、独自の記述を作成しても問題はありません。
result = Model.objects.filter(field__lookup=value)[0]
if not result:
result = Model.objects.create(...)
return result
[〜#〜] edit [〜#〜]提案されているように、[:1]スライス(単一エントリリストを返す)を[0](実際のオブジェクトを返す)へのフィルター。これに伴う問題は、クエリに一致しない場合に例外が発生することです。
これにより、同様の例外も発生します。
Model.objects.filter(field_lookup=value).latest()
もう一度質問を見ると、元のポスターが複数のオブジェクト/行を返すことを探しているのか、または単一のオブジェクト/行を取得するときに例外を発生させる方法だけを探しているのかわかりません。
別のオプションがありますか?
results = Model.objects.filter(...)
if results.exists():
return results
else:
return Model.objects.create(...)
そしてもう一つ:
result = None
try:
result = Model.objects.get(...)
except Model.DoesNotExist:
result = Model.objects.create(...)
例外の発生とキャッチに問題はありません!
Django 1.6から)フィルタークエリで最初の結果を返す便利なメソッドfirst()、またはNoneがあります。
obj = Model.manager.filter(params).first()
if obj is None:
obj = Model.objects.create(params)
この関数をエレガントに拡張できるようにするマネージャーメソッドを次に示します。
class FilterOrCreateManager(models.Manager):
"""Adds filter_or_create method to objects
"""
def filter_or_create(self, **kwargs):
try:
created = False
obj = self.filter(**kwargs).first()
if obj is None:
obj = self.create(**kwargs)
created = True
return (obj,created)
except Exception as e:
print(e)
次に、これを使用するモデルにマネージャーを追加します。
class MyObj(models.Model):
objects = FilterOrCreateManager()
その後、get_or_create
と同じように使用できます。
obj_instance, created = MyObj.filter_or_create(somearg='some value')
誰も実際にIndexErrorをキャッチすることを提案していないので、私はちょうどこの質問に出会い、貢献したいと思いました。
try:
obj = Model.objects.filter(params)[0]
except IndexError:
obj = Model.objects.create(params)
これを試すことができます:
result = Model.objects.filter(field__lookup=value)[0]
if not result:
result = Model.objects.create(...)
return result
これは私のために働く:
ビューで、次のように呼び出します。
obj = Category.objects.get_or_create_category(form.cleaned_data.get('name'))
モデルマネージャーで、次のような関数を作成します。
class CategoryManager(models.Manager):
def get_or_create_category(self, query):
try:
result = Category.objects.filter(name = query)[0]
except:
result = Category.objects.create(name = query)
return result
ロジックは簡単です。最初に、名前がクエリ文字列に一致する最初のCategoryオブジェクト(フォームによって提供される)を取得しようとします。取得に失敗した場合(存在しないため)、名前として文字列を使用して新しいカテゴリを作成します。ビューで使用する結果を返します。