LoginSignup
13
10

More than 5 years have passed since last update.

django/MySQL で Unicode 6.0 絵文字を使う

Last updated at Posted at 2014-10-30

目的

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 に下記の設定をする。

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 の文字コードを指定する。

settings.py
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 を追加する。

manage.py
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 が追加される。

参照資料

13
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
10