LoginSignup
4
4

More than 5 years have passed since last update.

get_or_newのススメ

Last updated at Posted at 2014-03-16

Webサービスでユーザーの状態をRDBを使って管理している事は多いので、レコードを取得時に無ければ作成といったコードを色んなプロジェクトで見かけます。

Djangoにもテーブルのレコードが有れば取得(SELECT)、無ければレコードの作成(INSERT)を行う get_or_create というメソッドが標準で用意されています。

例えば懸賞サイトでユーザーごとにポイント残高を表示したい場合、ユーザーのレコードが無い場合はデフォルト値である0ポイントになります。

無ければレコード作成という動きは、この0ポイントという情報を わざわざ テーブルに保存するという事ですが

デフォルト値って保存する必要ありますか?

その上、作成(INSERT)は 更新系の処理 です。
更新をするという事は排他制御や2重実行など色々な事を考慮する必要が有ります。INSERTによってロックも発生しますし。

とはいえ単なる get ではモデルのインスタンスが取得できず処理が書きにくいでしょう。

そこで get_or_new です

無ければインスタンスのみ作成してレコード作らない get_or_new を作りましょう。これなら副作用なくインスタンスを取得できます。

コードだとこういう感じになります

# models.py
from django.db import models


class User(models.Model):
    point = models.IntegerField('獲得ポイント', default=0)

    @classmethod
    def get_or_new(cls, pk):
        try:
            return cls.objects.get(pk=pk)
        except cls.DoesNotExist:
            return cls(pk=pk)


# view.py
from django.views.generic import TemplateView
from .models import User


class IndexView(TemplateView):
    template_name = "index.html"
    def get(request, user_id):
        user = User.get_or_new(user_id)
        self.render_to_response({'user': user})


これなら必要なUserのインスタンスもとれて、もちろん user.point として 0 が返ってきます。他にもメリットがあって

  • INSERTを行わないので当然ロックがない
  • DBには最初のSELECTしか飛ばないので負荷も殆どない
  • デフォルト値のレコードが作成されないのでデータ量が増えない

などなど。
注意点として、get_or_newから取得したデータを元に更新処理を行ってはいけません。2重実行した場合に間違ったデータが最終的に書き込まれます。

今回は参照の話なので、今度更新系の話も書こうと思います。

4
4
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
4
4