Djangoのデータベースをsqlite3からMySQLに移行したときにハマったこと

sqlite3はデータベースが1つのファイルで簡潔するので、バックアップがファイルのコピーで済む等、扱いが非常に簡単です。
そのため僕はDjangoのアプリケーションを作るときは、sqlite3を使用しています。
しかし、sqlite3ではトランザクション実行時にファイル全体がロックされるので、複数プロセス・スレッドから同時に書き込みを行うような用途の場合、別のデータベースエンジンを使用した方が良いと思われます。

僕は今、一定時間ごとにwebページをクローリングして、得られたデータをデータベースに保存するようなプログラムを今書いています。
cronで指定したインターバル以内にプログラムが終了しなかった場合、古いプロセスと新しいプロセスで同時にデータベースへのアクセスが発生してしまい、うまくデータベースへの書き込みが出来ないという自体に陥ってしまいました。
そこで、データベースエンジンをsqlite3からMySQLに移行することにしました。


Djangoではデータベースのバックアップとリストアがmanage.pyで行え、

python manage.py dumpdata app_name --format=json > app.json

でデータベースのバックアップが、

python manage.py loaddata app.json

でデータベースのリストアがそれぞれ実行できます。

基本的には、Djangoのこの機能を使えばデータベースの移行は簡単に行えると思っていたのですが、sqlite3とMySQLの違いによって、修正が必要な箇所がいくつかありました。そこで、このエントリでは、僕がDjangoのデータベースをsqlite3からMySQLに移行したときにハマったポイントについてまとめたいと思います。


1. MySQLのvarchar型の長さの制限
DjangoのCharFieldのMySQLでの型はvarcharですが、MySQLではvarchar型の長さは255文字までに制限されているので、CharFieldのmax_lengthを256以上に設定していた場合は255以下に変更する必要があります。


2. MySQLの文字列比較の大文字・小文字の区別
MySQLではvarchar型の比較時に大文字と小文字をデフォルトで区別しません。
そのため文字列フィールドをuniqueに設定していた場合、sqlite3では問題なかったデータでも、MySQLに移行させた場合にエラーとなる場合があります。
そのため、MySQLのデータベースをsyncdbで初期化した後、こちらの参考エントリの方法をで、大文字・小文字の区別を行うようにテーブルを変更する必要があります。


3. MySQLのdatetime型はタイムゾーンが扱えない
MySQLのdatetime型はタイムゾーンが扱えないらしく、DateTimeFieldの値がタイムゾーン情報を持っていた場合に例外が発生します。
そのため、以下のようにタイムゾーン情報を消す必要があります。

new_datetime_obj = original_datetime_obj.replace(tzinfo=None)