Django + MySQL 5.5 でモデルオブジェクトの datetime の値が生成時と生成後で変わる
class Diavolo(models.Model): ctime = models.DateTimeField(auto_now_add=True)
っていう生成時に現在日時を自動的に保存してくれるやつを作るじゃないですか。
>>> d1 = Diavolo.objects.create() >>> d2 = Diavolo.objects.get(pk=d1.pk) >>> d1.ctime == d2.ctime False
はい。
>>> d1.ctime datetime.datetime(2013, 10, 25, 13, 32, 25, 244575) >>> d2.ctime datetime.datetime(2013, 10, 25, 13, 32, 25)
つらい。
MySQL を使っている場合、Django の ORM は models.DateTimeField を MySQL の TIMESTAMP 型として扱うけど、MySQL 5.5 はマイクロ秒をサポートしてない。
でも Model.objects.create() の戻り値のオブジェクトは INSERT するために生成されたやつなので、この時点ではマイクロ秒がそのまま残ってる。
対して、改めてデータベースから取得したやつは truncate されてる。なので
class DiavoloMapper(ModelMapper): class Meta: model = Diavolo def get_diavolo_as_dict(id): obj = Diavolo.objects.get(pk=id) return DiavoloMapper(obj).as_dict()
みんな大好き bpmappers を使ったこんな Mapper があったとして、こういうテスト書くと落ちる。
from testfixtures import compare class TestGetDiavoloAsDict(TestCase): def _getTarget(self): from mappers import get_diavolo_as_dict return get_diavolo_as_dict def _callFUT(self, id): func = self._getTarget() return func(id) def test_one(self): obj = Diavolo.objects.create(pk=1) dct = self._callFUT(id=1) compare(dct, dict(id=1, ctime=obj.ctime))
なので obj じゃなくて改めてモデルオブジェクトを取得してくるか、マイクロ秒を切り取るかしないといけない。まあ ctime みたいな auto_now_add されるやつはいっそテストしなくてもいいけど。
ちなみにローカルで sqlite 使ってたりするとこのテスト通る。こわいですね。