DjangoでUnicode 6.0の絵文字を利用するutf8mb4(MySQL or MariaDB)設定のイメージ画像

DjangoでUnicode6.0 絵文字を利用するutf8mb4(MySQL or MariaDB)設定

  • 公開日:2018/10/19
  • 更新日:2019/01/13
  • 投稿者:n bit

Djangoと、MySQLまたはMariaDBでUnicode 6.0の絵文字を取り扱うには、データベースの文字コードを「utf8mb4」に変更しておく必要があります。基本的にMySQLでもMariaDBでも設定方法は同じです。今回はMariaDBを使った場合の設定を説明しています。

  • Python
  • Django

この記事は約 分で読めます。(文字)

Unicode 6.0の絵文字

2010年にUnicodeコンソーシアムが制定し、日本の絵文字が国際標準で使えるようになりました。Unicode 6.0 で採用された携帯電話の絵文字(Emoji)の一覧と、携帯キャリアごとの対応状況については下記のページをご参照ください。

Unicode 6.0の絵文字は4バイト文字になります。

utf8mb4

Twitterの絵文字が入ったようなツィートをブログ内に埋め込もうとすると4バイト文字の問題が発生します。通常UTF-8は最大3バイトまでしか対応していないため、4バイトのUnicode 6.0絵文字は対応しません。

例えば、UTF-8のデータベースにDjangoで絵文字入りのコンテンツ保存しようとすると下記のエラーが発生します。

django.db.utils.InternalError: (1366, "Incorrect string value: '4バイトの文字...' for column 'colum_name' at row 1")

そのためデータベースの文字コードをUTF-8からutf8mb4に変更しておく必要があります。

サーバの文字コード設定を変更

utf8mb4のデータベース作成前にサーバ側の設定をutf8mb4用に変更しておきます。

アプリケーションサーバにログイン

アプリケーションサーバにログインしmy.cnfの設定を変更しておきます。

my.cnfの設定を変更

$ sudo vim /etc/my.cnf.d/client.cnf

[client]ブロックの末尾に追加

[client]

default-character-set=utf8mb4

データベースサーバにログイン

次に、データベースサーバにログインしmy.cnfの設定を変更しておきます。

my.cnfの設定を変更:[mysqld]ブロック

$ sudo vim /etc/my.cnf.d/server.cnf
[mysqld]ブロックの末尾に追加
[mysqld]

character-set-server=utf8mb4
# 1
collation-server=utf8mb4_unicode_ci
init_connect='SET NAMES utf8mb4'
init_connect='SET collation_connection = utf8mb4_unicode_ci'
skip-character-set-client-handshake
# 2
innodb_file_format = Barracuda
innodb_file_per_table = 1
innodb_large_prefix

エラー対策

#2以下の3行はmigrate時の下記のエラー対策です。

$ python manage.py makemigrations app_name

$ python manage.py migrate app_name

django.db.utils.OperationalError: (1071, 'Specified key was too long; max key length is 767 bytes')

エラーの原因

ひとつのカラムのキープレフィックスには最大値が767バイトという制限があります。utf8mb4 を指定したことで191文字(767bytes ÷ 4bytes = 191.75)までしか利用できなくなります。そのため、djangoが生成するテーブルに191文字より大きいカラムが含まれているとエラーが発生します。

文字コードがUTF-8の場合、通常の2バイト文字であれば256文字になります。

my.cnfの設定を変更:[cluent]ブロック

$ sudo vim /etc/my.cnf.d/client.cnf
[cluent]ブロックの末尾に追加

もし[cluent]ブロックがない場合はそのまま[cluent]ブロックを作成してください。

[cluent]

default-character-set=utf8mb4

サービスの再起動

データベースサーバのサービスを再起動しておきます。

$ sudo systemctl restart mariadb.service

データベースの作成

サーバー側の設定が完了しましたらデータベースを作成していきます。

MariaDBに接続

SQLを実行するため、MariaDBに接続します。

$ mysql -u root -p

念のため利用可能な文字セットを一覧表示して utf8mb4 が利用できるか確認しておきます。

MariaDB [(none)]> SHOW CHARACTER SET;

+----------+-----------------------------+---------------------+--------+
| Charset | Description | Default collation | Maxlen |
+----------+-----------------------------+---------------------+--------+
| utf8 | UTF-8 Unicode | utf8_general_ci | 3 |



| utf8mb4 | UTF-8 Unicode | utf8mb4_general_ci | 4 |
+----------+-----------------------------+---------------------+--------+
39 rows in set (0.00 sec)

データベース「sample_app01 」を作成します

MariaDB [(none)]> create database sample_app01;

Query OK, 1 row affected (0.01 sec)

ユーザはローカルからアクセス可能なものとアプリケーションサーバからアクセス可能なものの2種類を作成しておきます。

MariaDB [(none)]> grant all privileges on sample_app01.* to ユーザ名@localhost identified by 'パスワード';

Query OK, 0 rows affected (0.01 sec)

MariaDB [(none)]> grant all privileges on sample_app01.* to ユーザ名@接続元IP(APPサーバのIPを指定) identified by 'パスワード';
Query OK, 0 rows affected (0.01 sec)

データベースが作成されたかを確認します。

MariaDB [(none)]> show databases;

+--------------------+
| Database |
+--------------------+



| sample_app01 |
+--------------------+
9 rows in set (0.01 sec)

ユーザが作成されたかを確認します。

MariaDB [(none)]> select Host,User from mysql.user; 

+----------------+--------+
| Host | User |
+----------------+--------+
| 127.0.0.1 | root |
| 接続元IP | ユーザ名 |
| ::1 | root |
| localhost | root |
| localhost | ユーザ名 |
+----------------+--------+
5 rows in set (0.00 sec)

データベースの文字コードを確認

正しくutf8mb4の文字コードでデータベースが作られているか確認しておきます。

データベースの一覧を表示

MariaDB [(none)]> show databases;

+--------------------+
| Database |
+--------------------+



| sample_app01 |
+--------------------+
9 rows in set (0.01 sec)

確認するデータベースを選択しテーブルを表示


MariaDB [(none)]> use sample_app01;

Database changed
MariaDB [sample_app01]> show tables;
Empty set (0.00 sec)

文字コードを確認

MariaDB [sample_app01]> show variables like 'char%';

+--------------------------+----------------------------+
| Variable_name | Value |
+--------------------------+----------------------------+
| character_set_client | utf8mb4 |
| character_set_connection | utf8mb4 |
| character_set_database | utf8mb4 |
| character_set_filesystem | binary |
| character_set_results | utf8mb4 |
| character_set_server | utf8mb4 |
| character_set_system | utf8 |
| character_sets_dir | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.00 sec)

上記のように表示されれば正しく文字コードがセットされています。character_set_system の utf8 は固定値ですので無視しておいて大丈夫です。 

Djangoの設定

データベースが作成できましたので、次はDjango側の設定を行っていきます。

settings.pyにデータベース定義

先ほど作成したデーターベースをsettings.pyに設定します。

DATABASES = {

'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'sample_app01',
'USER': 'ユーザ名',
'PASSWORD': 'パスワード',
'HOST': '接続先IP(DBサーバのIPを指定)',
'PORT': '3306',
'OPTIONS': {
'charset': 'utf8mb4',
},
},
}

manage.pyを1部修正

これまでの設定だけでは「innodb_large_prefix」は有効になりません。有効にするためには、CREATE TABLE文で ROW_FORMAT=DYNAMIC もしくは ROW_FORMAT=COMPRESSED を指定したCREATE TABLE文を発行しテーブルを作成する必要があります。

Djangoは、manage.pyのmigrate 実行時にCREATE TABLE文を発行しますので、manage.pyに1部コードを付け加えます。今回は、python manage.py migrate を実行したときにCREATE TABLE文の末尾に ROW_FORMAT=DYNAMIC をつけるように以下の2行を追加します。

    from django.db.backends.mysql.schema import DatabaseSchemaEditor

DatabaseSchemaEditor.sql_create_table += " ROW_FORMAT=DYNAMIC"

manage.py

#!/usr/bin/env python

import os
import sys

if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "プロジェクト名.settings")

# 以下の2行を追加
from django.db.backends.mysql.schema import DatabaseSchemaEditor
DatabaseSchemaEditor.sql_create_table += " ROW_FORMAT=DYNAMIC"

try:
from django.core.management import execute_from_command_line
except ImportError:
# The above import may fail for some other reason. Ensure that the
# issue is really that Django is missing to avoid masking other
# exceptions on Python 2.
try:
import django
except ImportError:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
)
raise
execute_from_command_line(sys.argv)

これで設定は完了です。サーバにデプロイしてmigrateしてみてください。

$ python manage.py makemigrations app_name

$ python manage.py migrate app_name

Applying cmapp.0001_initial... OK

これで4バイトの絵文字もデータベースに保存できるようになります。

今日のdot

ブログなどを運営しているとTwitterのツィートなどコンテンツ内に埋め込む事はよくあると思います。ツィート内の絵文字などは4バイト文字のため「utf8mb4」の文字コードでないとデータベースに保存することができません。

そのため、データベースの文字コードを最初から「utf8mb4」に設定しておく事をお勧めします。