読者です 読者をやめる 読者になる 読者になる

Django の管理サイトに、下書き機能を付けられないか考え中

Django

Gmail の下書きとか、はてなのバックアップみたいなやつ。できたら便利だと思う。Webアプリケーションを操作していて最も恐ろしいのは、長文を書いている最中に何らかのアクシデントが起きて、入力した長文が消えることだろう。もう一度同じ文章を書く気力がなかなか湧かない。

その点 Gmail はよく出来てると思う。定期的に内容を保存してくれるので、ブラウザがクラッシュしても問題ない。

社内で使っている Django アプリでは、かなり長文を入力する。最近ある同僚が、長文を入力する場合エディタなどで入力してからアプリにコピーしていると言っているのを聞いた。確かに気持は分かる。が、なんか悔しいので Gmail の下書き機能をまねた機能が実現できないか考えてみる。

仕様というか願望はこんな感じで。

  • とりあえず管理サイトを対象に。
  • TextField を対象にバックアップをとる。
  • バックアップは定期的に保存する。
  • バックアップはローカルじゃなくて、DBに保存する。
  • バックアップはユーザ毎に保存する。
  • データ追加時に、バックアップがあれば復元する。
  • プラガブル。
  • 作業は settings.INSTALLED_APPS に追加するだけ。

で、技術的に解決しなきゃいけない問題はこれくらいかな。

  1. バックアップは JavaScript でやるとして、管理サイトに JavaScript をどうやって仕込むか?
    • admin/base_site.html を上書きすればいける?
    • ロードする JavaScript をどこに置くか?
  2. 保存したバックアップはいつ削除するか?
    • データを保存したときに削除すればいい?
    • 保存したときをどうやって知る?
    • dispatcher を使えばいけるっぽい。
  3. プロジェクトにロードされているモデルをどうやって知るか?
    • django.db.models.get_models/get_model を使えばいける?
  4. バックアップの保存/削除時に、どうやってユーザと紐付けるか?
    • 保存の時は request.user でいけるだろう。
    • 削除の時はどうやってユーザを知るか?

というようなことを考えて、いろいろいじってみた結果2、3はクリアできたみたい。だが post_model_saved でユーザが分からないのでモデル保存時にバックアップを削除できない。困ったな。

# encoding: utf-8
from django.contrib.auth.models import User
from django.db import models
from django.db.models.loading import get_models, get_model
from django.db.models import dispatcher
from django.db.models.signals import post_save
 
class ModelDraft(models.Model):
    user = models.ForeignKey(User, verbose_name=u'ユーザ')
    app_name = models.CharField(u'アプリケーション名', max_length=255)
    model_name = models.CharField(u'モデル名', max_length=255)
    field_name = models.CharField(u'フィールド名', max_length=255)
    body = models.TextField(u'下書き', blank=True)

def post_model_saved(signal, sender, instance, **kwds):
    print signal, sender, instance, kwds
    
for m in get_models():
    if m.__module__.startswith('django.') or m.__name__ == 'ModelDraft':
        continue
    cls =  get_model(m.__module__.split('.')[-2], m.__name__)
    dispatcher.connect(receiver=post_model_saved, signal=post_save, sender=cls)