LoginSignup
1
0

More than 5 years have passed since last update.

[Django]Djangoのget_or_createの接続先はdb_for_writeに従う

Posted at

DjangoでバックエンドDBにMySQLを利用してMaster/Slave構成とした場合の話です。
たぶんトランザクションをサポートしているようなDBであれば同様です。

settings.py
DATABASES = {
    'app_master': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'app',
        'USER': 'user',
        'PASSWORD': 'pass',
        'HOST': '192.168.33.50',
        'PORT': '3306',
    },
    'app_slave': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'app',
        'USER': 'user',
        'PASSWORD': 'pass',
        'HOST': '192.168.33.60',
        'PORT': '3306',
        'TEST': {
            'MIRROR': 'app_master',
        },
    },
}
database_router.py
class DatabaseRouter(object):
    def db_for_read(self, model, **hints):
        """
        Read
        """

        return "app_slave"

    def db_for_write(self, model, **hints):
        """
        Write
        """

        return "app_master"

    ...

上記のようなコードを書いて、更新系は【app_master】に、参照系は【app_slave】に流すという設定とします。

アプリケーション内で、「ユニークキーに対応するデータがあればUPDATE、なければINSERT」というようなことをします。
MySQLの【ON DUPLICATE KEY UPDATE】のようなことです。

@staticmethod
def upsert_item_num(user_id, item_id, num):
    try:
        # ItemModelはuser_id、item_idでユニーク
        item = ItemModel.objects.get("user_id"=user_id, "item_id"=item_id)
        item.num = num
        item.save()
    except ItemModel.DoesNotExist as e:
        item = ItemModel.object.create(user_id=user_id, item_id=item_id, num=num)

    return item
@transaction.atomic(using="app_master")
def update(self, request, pk):
    # なんかいろいろやって
    upsert_item_num(1, 1, 10)

    # さらにいろいろやって追加で
    upsert_item_num(1, 1, 20)

最初こういうコード書いていたんだけど、Master/Slaveの設定を反映させたら急にエラーになった。
2回目のupsert_item_numでDuplicate entryというエラー。

Modelのgetは常にSlaveを参照するように設定しているので、コミットされていない状態だと、2回目のgetもDoesNotExistになっていた。
こういう問題はModelのget_or_createを利用するようにしておけば回避できる。

@staticmethod
def upsert_item_num(user_id, item_id, num):
    item = ItemModel.object.get_or_create(user_id=user_id, item_id=item_id)
    item.num = num
    item.save()

    return item

get_or_createは更新系に対してクエリを投げるぽいです。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0