1. ホーム
  2. python

作成者/変更者、時刻を追加するDRYな方法

2023-08-17 14:39:46

質問

次のようなものがあります。

  • 作成日時
  • 作成日
  • 変更日時
  • 修正日

多くのテーブルで非常によくあるパターンでしょう。

1) model.pyで、作成日を自動的に設定することができます(他はできません)。

created_date = models.DateTimeField(auto_now_add=True, editable=False)

2) model.pyで、作成日/変更日(ただし、リクエストコンテキストを持たないので、ユーザー別ではない)を行うことができます。

def save(self):
    if self.id:
        self.modified_date = datetime.now()
    else:
        self.created_date = datetime.now()
    super(MyModel,self).save()

3) admin.pyで作成/変更日時を設定することもできますが、これは管理者以外の更新に対処できません。

def save_model(self, request, obj, form, change):
    if change:
        obj.modified_by = request.user
        obj.modified_date = datetime.now()
    else:
        obj.created_by = request.user
        obj.created_date = datetime.now()
    obj.save()

4) そして最終的には、view.pyで4つすべてを行うことができますが、管理者の更新をカバーすることはできません。

そのため、現実的にはロジックを分散させ、最低でも3 & 4で繰り返す必要があります(または、両方から呼び出されるモデル上のメソッド、これは見逃されます)。

何か良い方法はないでしょうか? (私は数日間python/jangoで作業しているので、明らかに何かを見逃している可能性があります)

  • login_requiredのようなものを行うことができますか?
  • モデルでリクエストと現在のユーザーにアクセスし、そこにロジックを集中させることは可能か?

どのように解決するのでしょうか?

作成・変更日時はDjangoで扱えるようになったので、以下のように実装できます。

class BaseModel(models.Model):
    created_date = models.DateTimeField(auto_now_add=True)
    modified_date = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True

これを抽象モデルのベースクラスに追加することで、アプリケーションのすべてのモデルに簡単に追加することができます。

ユーザーを格納するのはより困難です。 request.user が利用できないので、ユーザーを格納するのはより困難です。SeanOC が言及したように、これは Web リクエストとモデル層との間の関心事の分離です。このフィールドを常に渡しておくか、あるいは request.user をスレッドローカルに格納するかです。Django CMS は、彼らのパーミッションシステムにこれを採用しています。

class CurrentUserMiddleware(object):
    def process_request(self, request):
        set_current_user(getattr(request, 'user', None))

そして、ユーザートラッキングは別の場所で行われます。

from threading import local
_thread_locals = local()

def set_current_user(user):
    _thread_locals.user=user

def get_current_user():
    return getattr(_thread_locals, 'user', None)

ウェブ以外の環境(管理コマンドなど)では set_current_user をスクリプトの先頭で呼び出す必要があります。