この記事はPython その2 Advent Calendar 2015の6日目の記事です。
Djangoを1.7から1.8以上にバージョンアップすると、
RuntimeWarning: Pickled model instance's Django version is not specified.
obj = pickle.loads(value)
のような警告が表示されることがあります。
モデルインスタンスをPickleでデシリアライズすると必ず発生するのかと思ったのですが、この警告が表示されるないこともあり、条件が不明だったので原因を調べてみました。
警告の原因
端的に言うと、Django 1.7以前で動作するアプリケーションでシリアライズしたモデルのデータを、Django 1.8以上で動作するアプリケーションでデシリアライズすると発生します。
警告を発生させているコードはここです。
virtualenvなどで1.7, 1.8/1.9をインストールした環境を作って、同じアプリケーションでバージョンを切り替えてみると簡単に再現させられます。
Django 1.7のvirtualenvで以下のような感じでキャッシュをセット。
$ python manage.py shell
>>> from django.core.cache import caches
>>> from django.utils.six.moves import cPickle as pickle
>>> from myapp.models import MyModel
>>> obj = MyModel.objects.get(pk=1)
>>> caches['default'].set('my-model-instance', pickle.dumps(obj))
Django 1.8のvirtualenvで以下のような感じでキャッシュからモデルインスタンスをデシリアライズ。
$ python manage.py shell
>>> from django.core.cache import caches
>>> from django.utils.six.moves import cPickle as pickle
>>> from myapp.models import MyModel
>>> pickle.loads(caches['default'].get('my-model-instance'))
<string>:1: RuntimeWarning: Pickled model instance's Django version is not specified.
対処方法
対処方法としては以下のようなものが考えられます。
無視する
確信はもてませんが、Djangoのソースコードのコメントを読む限り、defer
で遅延ロードしているプロパティがなければ影響がないようにも見えます。
また、Memcacheのような揮発性のキャッシュならば時間が経てば新しいキャッシュに置き換えられていくので、問題は自然解消するはずです。
Djangoをバージョンアップしたタイミングでキャッシュをクリアする
おすすめはしません。
$ python manage.py shell
>>> from django.core.cache import caches
>>> caches['default'].clear()
もしくはsettings.py
のCACHES
の設定で、KEY_PREFIXを変更したり、VERSION
をインクリメントするのも、ほぼキャッシュ全消しと同じ効果があります。
Pickleしたデータのキャッシュのキーだけを変える
今回僕が捕ろうとしている方法です。
キャッシュのキーにDjangoのバージョン番号を入れてしまうことで、将来さらにバージョンが上がった場合でも、自動的にバージョン毎に別のキャッシュが使われるようにします。
>>> from django.core.cache import caches
>>> from django.utils.six.moves import cPickle as pickle
>>> from django.utils.version import get_version
>>> from myapp.models import MyModel
>>> obj = MyModel.objects.get(pk=1)
>>> caches['default'].set('{}:my-model-instance'.format(get_version()), pickle.dumps(obj))
問題になっているのはPickleしたモデルインスタンスをキャッシュする場合だけなので、上記のような感じで該当箇所を修正しておけば、1.9以上にバージョンアップすることになっても困ることはないはずです。