目的
django から Unicode 6.0 で追加された絵文字 を使いたい。
動作環境
- Python 3.2.0 以上 (3.4.3 で動作確認)
- Django 1.4.0 以上? (1.7.0, 1.8.1 で動作確認)
- mysqlclient (1.3.3, 1.3.6 で動作確認)
- MySQL 5.5.14 以上 (5.5.40, 5.6.16 で動作確認)
Python
Unicode 6.0 絵文字を使うためには、Python 3.2.0 以上が必要になる。
django
django の対応バージョンについては、調べ切れていない。
1.7.0, 1.8.1 で動作したから良しとする。
mysqlclient
Python 3.x 系列に対応した mysqlclient を使用する。
MySQL
キープレフィックスの制限(後述)を3072バイトまで拡張できる MySQL 5.5.14 以上 を使用する。
設定
MySQL
MySQL に絵文字を格納するためには、データベース作成時の文字コードを utf8mb4 にする。
CREATE DATABASE cmtestdb CHARACTER SET utf8mb4;
MySQL(InnoDB) では、ひとつのカラムのキープレフィックスの最大値が 767 バイトという制限がある。
utf8mb4 を指定した場合、 767byte÷4で191文字までしか使えない。
django のミドルウェアが生成するテーブルに191文字より大きいカラムが含まれているため下記のエラーが発生する。
Specified key was too long; max key length is 767 bytes
このエラーを回避するためには、my.cnf に下記の設定をする。
innodb_file_format = Barracuda
innodb_file_per_table = 1
innodb_large_prefix
innodb_large_prefix を指定することで、キープレフィックスの最大値を 3072 バイトまで拡張できる。
mysqlclient
普通にインストールする。
pip install mysqlclient
django
データベース定義
django から MySQL に接続する際、 utf8mb4 の文字コードを指定する。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'DBNAME',
'USER': 'DBUSER',
'PASSWORD': 'DBPASSWORD',
'HOST': 'DBHOST',
'PORT': '3306',
'OPTIONS': {
'charset': 'utf8mb4',
},
},
}
"CREATE TABLE" 文の拡張
innodb_large_prefix の指定を有効にするためには、"CREATE TABLE" SQL文で ROW_FORMAT=DYNAMIC
もしくは ROW_FORMAT=COMPRESSED
を指定する。
python manage.py migrade
を実行すると "CREATE TABLE" SQL文を発行する。
django 1.7 の実装では、"CREATE TABLE" SQL文の拡張ポイントが見つからなかった。
そこで、django 1.7 オリジナルの "CREATE TABLE" SQL文 生成部分に動的にパッチを当てる形で ROW_FORMAT=DYNAMIC
を追加する。
import os
import sys
def patch_mysql_sql_create_model(original):
"""
:param :class:`django.db.backends.creation.BaseDatabaseCreation` original: BaseDatabaseCreation
:return: BaseDatabaseCreation
:rtype: :class:`django.db.backends.creation.BaseDatabaseCreation`
"""
def revised(self, model, style, known_models=set()):
"""
:class:`django.db.backends.creation.BaseDatabaseCreation` #sql_create_model の処理に対して下記の処理を追加する。
* SQL 文の末尾に 'ROW_FORMAT=DYNAMIC;' を追加する。
追加条件は、下記になる。
* データベースが MySQL
* SQL 文の先頭が、'CREATE TABLE'
"""
fullname = self.__module__ + "." + self.__class__.__name__
if fullname == 'django.db.backends.mysql.creation.DatabaseCreation':
original_output, pending_references = original(self, model, style, known_models)
final_output = []
for sql in original_output:
if sql.startswith('CREATE TABLE') is False:
continue
if sql.endswith(';'):
sql = sql[:-1]
sql += 'ROW_FORMAT=DYNAMIC'
final_output.append(sql)
return final_output, pending_references
else:
return original(self, model, style, known_models)
return revised
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.local")
if len(sys.argv) > 1 and sys.argv[1] == 'migrate':
import django
if django.VERSION >= (1, 8):
from django.db.backends.base.creation import BaseDatabaseCreation
else:
from django.db.backends.creation import BaseDatabaseCreation
BaseDatabaseCreation.sql_create_model = patch_mysql_sql_create_model(BaseDatabaseCreation.sql_create_model)
from django.db.backends.mysql.schema import DatabaseSchemaEditor
DatabaseSchemaEditor.sql_create_table += ' ROW_FORMAT=DYNAMIC'
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
上記の設定を行うと、 python manage.py migrate
を実行したときに "CREATE TABLE" SQL文 の末尾に ROW_FORMAT=DYNAMIC
が追加される。