Django と SQLite/MySQL : 最大値とUNIQUE
前回に引き続き、DBを移行する場合にひっかかりそうなTIPS第2弾。Djangoってよりは、Modelの裏側の意識が必要なケースありますね的な話です。
MySQL使っていて、こんなエラーが出た時には参考になるかもしれません。
Specified key was too long; max key length is 767 bytes (SQLState:S1000)
今回の結論としては、SQLite -> MySQLにマイグレートする場合は、こんなの気をつけましょうって所です。
- VARCHARの最大値は 255 を使う
- uniqe_together使ってる時は注意が必要
環境は Django 1.6.x で、settings.py のバックエンドの指定は、次のようにして確認しました。
VARCHARの最大値
ModelでCharField指定したとき、バックエンドがSQLiteの時は 256 が通ったのですが、MySQLの場合は 255 じゃないとダメなので255にしておきましょうというだけの事です。
key = models.CharField( - max_length=256, + max_length=255, db_index = True, unique = True, )
マイグレートした時に触るのって、ちょっとしたことでも勇気がいるので、始めっからやれるのが一番ですね(;
後述のUNIQUE INDEXが最大767byteだという話とからんで出て来たのですが、ドキュメントには、そもそも255との記載もありましたので間違いなさそうです(なんで256が使えたのだろうか?)。
uniqe_together
Modelの定義の中で、2つのフィールド値を引っ付けてユニークな値として扱う unique_together があります。
class Meta: app_label = 'sample' unique_together = (('contents', 'name')) get_latest_by = 'date'
便利な機能なのですが、バックエンドがMySQLの場合(かつUTF-8を扱うとき)は、次の制限に引っかかりやすくなります(SQLITEは、制限はもちっと揺るみたいで遭遇しなかった)。
「MySQLのUNIQUEなINDEXは、長さ767byteまでしか使えない」という制限は、MySQLでUTF-8を使ってると遭遇しやるいと思います。UTF-8はMySQL上で、3byte/文字で処理されるので、最大の255文字*3byte = 765byteが最大だよという事になる。
でこれが最大なもんで、unique_togetherした時に、UTF-8 かつ MAXLENGTHまでもたせたCharFieldが含まれてると一発でオーバーするよというのがハマリポイントでした。Django側だけ見てると気づかないですよね。。。気づくのに苦労しました。
僕がとった回避方法は、MAXLENGTHから少し下の値を使うという形をとりました。該当カラムだけASCIIにするという方法もありですね。
たぶん manage.py syncdb の段階で落ちるので manage.py sqlall などで生成されるSQL文とにらめっこする等の対応になるかと思います(僕は、そうしてました)。