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

Djangoでキャッシュ機能付きモデル

これはgumi Engineer's Diaryに書いたものを修正・転載したものです。


キャッシュを自動化しよう
ソーシャルアプリのようなトラフィックが高いサービスを作るときはDBアクセスを減らすことが重要になります。
Djangoにはそのための機能であるdjango.core.cacheが存在していますので、DBアクセスの結果は積極的にキャッシュしたいところです。
ですが、各モデルにいちいちキャッシュの機構を組み込むのは面倒ですし、万が一消し忘れたりすると大変です。
そこで、作成したゲームではキャッシュする抽象モデルクラス(AbstructCachedModel)を作って、ある程度のキャッシュを自動化しています。


AbstructCachedModelの仕組み
キャッシュ機能付き抽象モデルクラスであるAbstructCachedModelに実装してあるメソッドは6つです。

キャッシュのキーを取ってくる

  • get_cache_path()
  • get_cache_all_path()

実際にデータを取得する

  • get()
  • get_all()

忘れずにキャッシュを削除するためのオーバーライド

  • save()
  • delete()

これだけあれば大抵は何とかなります。
条件付きのデータ取得がしたい場合などはこれを継承した個々のクラスで追加しています。


AbstructCachedModelの実装

class AbustractCachedModel(models.Model):
    class Meta:
        abstract = True

    def save(self, *args, **kwargs):
        super(AbstractCachedModel, self).save(*args, **kwargs)
        cache.delete(self.get_cache_path(self.id))
        cache.delete(self.get_all_cache_path())

    def delete(self, *args, **kwargs):
        cache.delete(self.get_cache_path(self.id))
        cache.delete(self.get_all_cache_path())
        super(AbstractCachedModel, self).delete(*args, **kwargs)

    @classmethod
    def get_cache_path(cls, pk):
        return '%s/%s/' % (cls._meta, pk)

    @classmethod
    def get_all_cache_path(cls):
        return '%s/all/' % (cls._meta)

    @classmethod
    def get(cls, pk):
        cache_path = cls.get_cache_path(pk)
        model = cache.get(cache_path, None)
        if model is None:
            try:
                model = cls.objects.get(pk=pk)
                cache.set(cache_path, model, 86400)
            except cls.DoesNotExist:
                pass
        return model

    @classmethod
    def get_all(cls):
        cache_path = cls.get_all_cache_path()
        models = cache.get(cache_path, None)
        if models is None:
            models = list(cls.objects.all())
            cache.set(cache_path, models, 86400)
        return models

使い方は model.objects.get(pk=pk) の代わりに model.get(pk=pk)、model.objects.all() の代わりに model.get_all() を使うだけです。
もし、わからなかったらコメントしてください!


まとめ
AbstractCachedModelを継承すればキャッシュ機能付きのモデルが簡単に作れます。
他に必要な機能があったら適宜拡張して使ってください。
こんな機能もあったら便利!とかあれば、教えてくれると嬉しいです!