.. _ref-unicode: ========================== Django での Unicode の扱い ========================== :revision-up-to: 17812 (1.4) Django はネイティブで Unicode データをサポートしています。データベースの設 定を正しく行っている限り、テンプレートやモデル、データベース間で Unicode 文 字列を問題なく受渡しできます。 このドキュメントでは、非 ASCII の文字コードでエンコードされたデータやテンプ レートを扱うアプリケーションを作成するときに知っておくべきことを説明します。 データベースの作成 ================== まず、データベースが任意の文字列データを保存できるように設定してください。 通常、これは UTF-8 または UTF-16 を使うことを意味します。 latin1 (iso8859-1) のような、より文字セットの制約が強いエンコーディングを使ってい ると、一部の文字をデータベースに保存できず、情報が失われるかもしれません。 * MySQL ユーザは `MySQL 5.1 マニュアルの原文 10.3.2 節`_ 、または `和訳 9.3.2 節`_ を参考に、データベースの文字セットエンコーディングの設 定や変更を行ってください。 * PostgreSQL ユーザは `PostgreSQL 8 マニュアルの原文 21.2.2 節`_ 、 または `和訳 20.2.2 節`_ を参考にして、正しいエンコーディングでデータベー スを作成してください。 * SQLite ユーザは何もする必要がありません。 SQLite は常に内部のエンコーディ ングに UTF-8 を使うからです。 .. _MySQL manual: http://www.mysql.org/doc/refman/5.1/en/charset-database.html .. _PostgreSQL manual: http://www.postgresql.org/docs/8.2/static/multibyte.html#AEN24104 .. _`MySQL 5.1 マニュアルの原文 10.3.2 節`: `MySQL manual`_ .. _`PostgreSQL 8 マニュアルの原文 21.2.2 節`: `PostgreSQL manual`_ .. _`和訳 9.3.2 節`: http://www.mysql.org/doc/refman/5.1/ja/charset-database.html .. _`和訳 20.2.2 節`: http://www.postgresql.jp/document/pg803doc/html/multibyte.html#AEN20640 Django のデータベースバックエンドは、データベースにクエリを発行する際に、 Unicode 文字列を自動的に適切なエンコーディングに変換します。また、データベー スから文字列データを取り出す時にも、適切なエンコーディングで Python の Unicode 文字列に変換します。変換の際に、いちいち Django にデータベースの使っ ているエンコーディングを指定する必要はなく、透過的に操作できます。 詳しくは、後述の「データベース API」を参照してください。 一般的な文字列の操作 ==================== データベースの照合やテンプレートのレンダリングなど、 Django 内で文字列を使 う場面では、 Unicode 文字列と UTF-8 でエンコードされた (string 型の) 文字列 の 2 通りを選択できます。 .. admonition:: 注意 バイト文字列 (string 型) 自体にはエンコーディングに関する情報は付随して いません。そのため、 Django は全てのバイト文字列を UTF-8 でエンコードさ れているとみなします。 UTF-8 以外の文字セットを使ってエンコードされた文字列を Django に渡すと、 内部のどこかでまずいことが起きるでしょう。その結果、 Django はたいてい ``UnicodeDecodeError`` をどこかで送出するはずです。 ASCII は UTF-8 のサブセットに相当するので、コード内で ASCII のデータしか扱 わないのなら、通常の文字列を使って、自由に受渡ししてかまいません。 :setting:`DEFAULT_CHARSET` 設定を ``'utf-8'`` にすれば、バイト文字列の変換 に他の文字セットを使えるかも、なんて考えるのはやめましょう! :setting:`DEFAULT_CHARSET` は、テンプレートのレンダリング結果 (とメール送信) の文字列のエンコーディングにしか使われません。 Django は常に内部のバイト文 字列のエンコーディングにUTF-8 を使います。その理由は、 :setting:`DEFAULT_CHARSET` というものは、アプリケーションの開発者が自由にい じってよいものではなく、アプリケーションをインストールして使うユーザが自由 に設定して使うためのものだからです。ユーザが:setting:`DEFAULT_CHARSET` をど んな値に設定しても、あなたのコードは正しく動かねばなりません。そのため、 :setting:`DEFAULT_CHARSET` に依存したコードを書いてはならないのです。 ほとんどの場合、 Django は文字列を扱う際に、まず Unicode 文字列に変換してか ら処理を行います。従って、一般的なルールとして、バイト文字列を渡すときには、 戻り値が Unicode 文字列となって戻ってくると想定しておいてください。 翻訳された文字列 ---------------- Django を使っていると、 Unicode 文字列とバイト文字列の他に、文字列ライクな 第三の型、すなわち「翻訳された文字列」を目にするはずです。Django フレームワー クの国際化機能には、文字列を翻訳用としてマークはするけれども、実際の翻訳結 果はその文字列を使うときまで決定しないという、「遅延翻訳 (lazy translation)」 の概念があります。この機能は、文字列を実際に使用するまでに翻訳ロケールがはっ きり決まらない一方で、コードを import する時にはもとの文字列を生成しておき たい場合に便利です。 通常は、遅延翻訳文字列について心配する必要はありません。ただ、このオブジェ クトを評価すると、遅延翻訳を表す ``django.utils.functional.__proxy__`` オブ ジェクトになっているということを覚えておいてください。また、 ``unicode()`` の引数に遅延翻訳オブジェクトを指定して呼び出すと、現在のロケールでの翻訳結 果を Unicode 文字列で返すということも覚えておいてください。 遅延翻訳オブジェクトの詳細は、 :doc:`国際化のドキュメント ` を 参照してください。 便利なユーティリティ関数 ------------------------ 文字列の操作は何度も何度も必要になるので、 Django では Unicode 文字列型やバ イト文字列型オブジェクトの操作を少しだけ簡単にするユーティリティ関数を提供 しています。 変換用の関数 ~~~~~~~~~~~~ ``django.utils.encoding`` モジュールには、 Unicode 文字列とバイト文字列との 間で相互に変換を行うための関数が入っています。 * ``smart_unicode(s, encoding='utf-8', string_only=False, errors='strict')`` は、入力 ``s`` を Unicode 文字列に変換します。 ``encoding`` パラメタ には入力文字列のエンコーディングを指定します。(例えば、 Django はフォー ムから入力された UTF-8 でエンコードされていないデータを内部的に処理す る際にこの関数を使っています。) ``strings_only`` パラメタが ``True`` の場合、数値型、ブール型、 ``None`` といった値を ``s`` に渡しても文字列 に変換しません (もとの型のままで値を返します) 。 ``errors`` はエラー 処理時の動作を指定するためのパラメタで、Python の ``unicode()`` 関数 で使われているのと同じ値を受け取ります。 ``__unicode__`` メソッドを実装しているオブジェクトを ``smart_unicode()`` に渡すと、 ``__unicode__`` メソッドを使って変換を 行います。 * ``force_unicode(s, encoding='utf-8', string_only=False, errors='strict')`` は、ほとんどの場合 ``smart_unicode()`` と同じように動作します。ただし、 第一引数が :ref:`遅延翻訳オブジェクト ` の場合には 動作が異なります。 ``smart_unicode()`` は遅延翻訳オブジェクトをそのままにしておきますが、 ``force_unicode()`` を使うと、(その場で翻訳を行って) Unicode 文字列を 返します。通常は ``smart_unicode()`` を使った方がよいでしょう。とはい え、テンプレートタグやフィルタは、「後で文字列になる何か」ではなく、 *「文字列」を扱わねばならない* ので、 ``force_unicode()`` を使った方 が有意義です。 * ``smart_str(s, encoding='utf-8', strings_only=False, errors='strict')`` は本質的に ``smart_unicode()`` の対極にあたるメソッドです。このメソッ ドは、第一引数をバイト文字列に変換します。 ``strings_only`` パラメタ の意味は、 ``smart_unicode()`` および ``force_unicode()`` の同名のパ ラメタと同じです。これは Python の組み込み関数 ``str()`` と少し違った 仕様ですが、 Django 内部のいくつかの場所で必要な機能として実装されて います。 普段は、 ``smart_unicode()`` しか必要ないでしょう。入力データが Unicode か バイト文字列であるような場合には、早い段階でこのメソッドを使って Unicode 化 を行い、その後の操作ではつねに Unicode を扱うようにしてください。 .. _-unicode-uri-and-iri: URI および IRI の処理 ~~~~~~~~~~~~~~~~~~~~~ Web フレームワークは (IRI_ の一形式である) URL を扱えねばなりません。 URL の必要条件の一つとして、 URL は全て ASCII 文字セットで構成せねばなりま せん。しかし、国際化環境では、 URL を IRI_ で構築せねばならない、ざっくりと 言えば、Unicode 文字を含んだ URI を構築せねばならない場合もあるでしょう。 IRI から URI へのクオート処理や変換処理はやや厄介なので、 Django は補助にな る機能をいくつか提供しています。 * ``django.utils.encoding.iri_to_uri()`` は (`RFC 3987`_ 準拠の) IRI から URI への変換を実装しています。 * ``django.utils.http.urlquote()`` および ``django.utils.http.urlquote_plus()`` は、 Python の標準ライブラリで 提供している ``urllib.quote()`` および ``urllib.quote_plus()`` に手を 加えて、(データをエンコーディング前に UTF-8 文字列に変換することで)非 ASCII 文字を扱えるようにしています。 これらの関数は、それぞれのグループごとに少し違った目的があり、きちんとした 使い分けが必要です。通常、IRI や URI パスを構成する個々の部分文字列には ``urlquote()`` を使い、 ``'&'`` や ``'%'`` といった予約文字が正しくエンコー ドされるようにします。その後、 ``iri_to_uri`` を IRI 全体に適用して、非 ASCII 文字が正しい値にエンコードされるようにしてください。 .. note:: 技術的には、 ``iri_to_uri()`` は IRI 仕様に準拠した完全なアルゴリズムを 実装しているわけではありません。このメソッドは、(いまのところ) 国際化ド メイン名のエンコーディングを行っていないからです。 ``iri_to_uri()`` は、 URL 内での使用を許されている ASCII 文字を一切変換しま せん。従って、例えば ``'%'`` のような文字を ``iri_to_url()`` に渡してもエン コーディングは起こりません。つまり、この関数に完全な URL を渡しても、クエリ 文字列部分などを壊してしまうことはありません。 以下の例を見ればわかりやすいでしょう:: >>> urlquote(u'Paris & Orléans') u'Paris%20%26%20Orl%C3%A9ans' >>> iri_to_uri(u'/favorites/François/%s' % urlquote(u'Paris & Orléans')) '/favorites/Fran%C3%A7ois/Paris%20%26%20Orl%C3%A9ans' 注意深く見れば、二つ目の例では、 ``urlquote()`` が生成した URL を ``iri_to_uri()`` が誤って二重クオートしていないことがわかるでしょう。これは 非常に重要かつ有用な機能です。つまり、非 ASCII 文字列が入っているかどうかを 気にせず IRI を構築してもかまわず、最後に ``iri_to_uri()`` を呼び出しさえす ればよいということなのです。 ``iri_to_uri()`` は等冪性がある (何度適用しても同じ結果になる) ので、以下の ような関係が常に成立します:: iri_to_uri(iri_to_uri(some_string)) = iri_to_uri(some_string) 従って、 ``iri_to_uri()`` は同じ IRI に対して二重クオート問題を気にせず何度 でも呼び出せます。 .. _URI: http://www.ietf.org/rfc/rfc2396.txt .. _IRI: http://www.ietf.org/rfc/rfc3987.txt .. _RFC 3987: IRI_ モデル ====== データベースから返される文字列は全て Unicode 文字列です。文字ベースのモデル フィールド (CharField, TextField, URLField など) の値をデータベースから取り 出すと、データが ASCII バイト文字列の範疇に収まるかどうかに関わらず、常に Unicode 型の値になります。 バイト文字列を渡してモデルを作成したり、フィールドに値を入れたりしてもよく、 その場合には Django が必要に応じてデータを Unicode 型に変換します。 ``__str__()`` と ``__unicode__()`` のどちらを使うべきか ------------------------------------------------------- デフォルトで Unicode を使うようにした結果、モデルのデータを出力するときに 注意せねばならない点が一つ増えました。 具体的には、モデルに ``__str__()`` メソッドを定義する代わりに ``__unicode__()`` メソッドを実装するよう推奨します。 ``__unicode__()`` メソッ ドの中では、モデルのフィールド値を使って好きな値を作成でき、その値がバイト 文字列として適切に表現されるかを気にせず返してかまいません (たとえ ``__str__()`` に Unicode オブジェクトを返させるように実装したとしても、 Python の仕様によって、 ``__str__()`` は *常に* バイト文字列を返します)。 もちろん、必要であれば、モデルに ``__str__()`` メソッドを定義してもかまいま せんが、明確な理由がないかぎりそうするべきではありません。 Django の モデルクラスのベースクラスでは、自動的にモデルに ``__str__()`` メソッドを追 加するようになっていて、この ``__str__()`` 実装は内部で ``__unicode__()`` を呼び出し、その結果を UTF-8 で返します。つまり、 ``__unicode__()`` メソッ ドだけを定義しておけば、 Django が自動的にバイト文字列への型強制を処理して くれるのです。 ``get_absolute_url()`` に注意 ----------------------------- URL に含めてよい文字は ASCII 文字だけです。従って、非 ASCII 文字を含むよう なデータから URL を構築する場合、 URL として適切な値になるようにエンコード する必要があります。 ``django.db.models.permalink()`` デコレータはこの処理 を自動的に行います。 URL を手動で生成する場合 (``permalink()`` デコレータを *使わない* 場合)、 作り手が自分でエンコーディングに気を配らねばなりません。この場合、 `前述の <#ref-unicode-uri-and-iri>`_ ``iri_to_uri()`` および ``urlquote()`` 関数を使ってください。例えば:: from django.utils.encoding import iri_to_uri from django.utils.http import urlquote def get_absolute_url(self): url = u'/person/%s/?x=0&y=0' % urlquote(self.location) return iri_to_uri(url) この関数は、 ``self.location`` に "Jack visited Paris & Orléans" のような文 字列が入っていてもただしくエンコードされた URL を返します。 (実際、上の例で は、最初の行のクオート処理で非 ASCII 文字が落ちるので、厳密には ``iri_to_uri()`` の呼び出しは不要です) データベース API ================ ``filter()`` メソッドなどのデータベース API の引数には、 Unicode 文字列と、 UTF-8 でエンコードされたバイト文字列のどちらを渡してもかまいません。以下の 二つのクエリセットは全く同じです:: qs = People.objects.filter(name__contains=u'Å') qs = People.objects.filter(name__contains='\xc3\85') # UTF-8 encoding of Å テンプレート ============ テンプレートの手動生成には、 Unicode とバイト文字列のどちらでも使えます:: from django.template import Template t1 = Template('バイト文字列のテンプレートだよ。') t2 = Template(u'Unicode のテンプレートだよ。') とはいえ、ほとんどのケースではファイルシステムからテンプレートを読み出して いることでしょう。そして、ここにはちょっと難しい問題があります。というのも、 ファイルシステム上のファイルが常に UTF-8 でエンコードされているとは限らない からです。ファイルシステム上のテンプレートファイルが UTF-8 でエンコードされ ていない場合は、 :setting:`FILE_CHARSET` を該当ファイルのエンコードに設定し てください。 Django はテンプレートファイルを読み出すときに、設定値に基づい てテンプレートの内容をデコードして Unicode オブジェクトに変換します (:setting:`FILE_CHARSET` のデフォルト値は ``'utf-8'`` です。) レンダリング済みのテンプレートのエンコーディングは :setting:`DEFAULT_CHARSET` 設定で制御されています。この設定のデフォルト値は UTF-8 です。 テンプレートタグとフィルタ -------------------------- テンプレートタグやフィルタを定義する場合、以下の 2 点に注意してください: * テンプレートタグの ``render()`` メソッドやテンプレートフィルタの戻 り値には、常に Unicode 文字列を使ってください。 * ``smart_unicode()`` よりも ``force_unicode()`` を使うようにしてくださ い。タグのレンダリングやフィルタの呼び出しはテンプレートのレンダリン グ中に行われるので、遅延翻訳オブジェクトから文字列への変換を遅らせる 意味がないからです。また、この時点では、 Unicode 文字列だけを扱うよう にした方が簡単です。 電子メール ========== Django の電子メール送信フレームワーク (``django.core.mail``) は、透過的に Unicode をサポートしており、メッセージ本体やヘッダに Unicode データを指定で きます。ただし、メールアドレスには ASCII 文字しか使わないといったように、一 般の電子メールの仕様は守るよう気を配るべきでしょう。 以下のコード例では、メールアドレス以外が非 ASCII 文字列であるような電子メー ルを送信しています:: from django.core.mail import EmailMessage subject = u'My visit to Sør-Trøndelag' sender = u'Arnbjörg Ráðormsdóttir ' recipients = ['Fred