get_or_createの更新時のデフォルト値の扱いなど

Djangoには、「get_or_create」という非常に便利なメソッドがあります。このメソッドは、まだDBにデータが登録されていない場合は登録し、登録されている場合はそのデータを返します。この記事では、「get_or_create」の更新時のデフォルト値の扱いなどの、いくつかのtipsをメモしています。

新規作成時のデフォルト値の指定

まずは新規作成時です。以下のようなモデルがあるとします。

from django.db import models


class NormalItem(models.Model):
    item_code = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=128)
    expire_date = models.DateField()

このモデルで「get_or_create」メソッドを使うには、以下のようにします。

from datetime import date, timedelta
item, created = NormalItem.objects.get_or_create(
    item_code=10,
    defaults=dict(
        name=u'新規作成',
        expire_date=date.today() + timedelta(days=10),
    ),
)

この場合、「item」インスタンスには新規作成したモデル、「created」には新規作成されたことを表す「True」値が入っています。ここでの注意点は、「get_or_create」の引数に「defaults」を指定している点です。これを指定しないと、DBの「expire_date」カラムがnull値を許可しないため、「IntegrityError」が発生します。

IntegrityError: test_normalitem.expire_date may not be NULL

更新時のデフォルト値の扱い

「get_or_create」メソッドを実行した場合、引数で指定した検索条件がヒットした場合はそのデータが返ってきます。その場合、defaultsで指定した値は無視され、DBに保存されている内容が返ってきます。しかし、defaultsで渡す内容が最新の情報であるために、それらで更新したい場合はわざわざセットし直すのは面倒です。

defaults = dict(
    name=u'更新',
    expire_date=date.today() + timedelta(days=10),
    attr1=u'ATTR1',
    attr2=u'ATTR2',
    attt3=u'ATTR3',
    attr4=u'ATTR4',
    attr5=u'ATTR5',
)
item, created = NormalItem.objects.get_or_create(
    item_code=10, defaults=defaults)
if not created:
    # defaultsは無視されるので、設定し直す必要あり
    item.name = defaults['name']
    item.expire_date = defaults['expire_date']
    item.attr1 = defaults['attr1']
    item.attr2 = defaults['attr2']
    item.attr3 = defaults['attr3']
    item.attr4 = defaults['attr4']
    item.attr5 = defaults['attr5']
    item.save()

このような更新の場合、「setattr」関数を使うとすっきりと書くことができます。

defaults = dict(
    name=u'更新',
    attr1=u'ATTR1',
    attr2=u'ATTR2',
    attt3=u'ATTR3',
    attr4=u'ATTR4',
    attr5=u'ATTR5',
)
item, created = NormalItem.objects.get_or_create(
    item_code=10, defaults=defaults)
if not created:
    for k, v in defaults.items():
        setattr(item, k, v)
    item.save()

この記事が役に立った場合、シェアしていただけると励みになります!!