Unicode データ

Django はどこでも Unicode データをサポートしています。

この文書は、ASCII以外のエンコーディングでデータやテンプレートを使用するアプリケーションを書く場合に、知っておくべきことを説明します。

データベースの作成

データベースが任意の文字列データを保存できるように設定されていることを確認してください。通常、これは UTF-8 または UTF-16 のエンコーディングを使用することを意味します。もっと制限のあるエンコーディング -- 例えば、latin1 (iso8859-1) -- を使用する場合、特定の文字をデータベースに保存することができず、情報が失われることになります。

  • MySQLユーザーの場合、データベースの文字セットエンコーディングを設定または変更する方法の詳細については、 MySQL manual を参照してください。
  • PostgreSQL を使用している場合は、適切なエンコーディングでデータベースを作成するための詳細については、 PostgreSQL manual を参照してください。
  • Oracle ユーザーは、データベースの文字セットエンコーディングを設定( section 2 )する方法や変更( section 11 )する方法の詳細については、 Oracle manual を参照してください。
  • SQLite を使用しているユーザーは、何もする必要はありません。SQLite は常に UTF-8 を内部エンコーディングに使用します。

Djangoのデータベースバックエンドはすべて自動的に、データベースと通信するために適切なエンコーディングへ文字列を変換します。また、データベースから取得した文字列も自動的に文字列に変換します。データベースが使用しているエンコーディングをDjangoに伝える必要はまったくありません。それは透過的に処理されます。

詳細については、以下の「データベース API」セクションを参照してください。

一般的な文字列操作

Djangoを使用する際、例えば、データベース検索やテンプレートのレンダリングなど、どこでも、文字列に対して2つのエンコーディング選択肢があります。通常の文字列を使用するか、バイト文字列('b' で始まる)を使用できます。

警告

バイト文字列は、そのエンコーディングに関する情報を何も持っていません。そのため、我々は仮定を立てなければなりません。そして、Djangoはすべてのバイト文字列がUTF-8であると仮定します。

Django に他の形式でエンコードされた文字列を渡すと、興味深い方法で問題が発生します。通常、いずれかのタイミングで Django は UnicodeDecodeError を発生させます。

コードが ASCII データのみを使用している場合、ASCII は UTF-8 のサブセットなので、通常の文字列を自由に使って渡すことが安全です。

DEFAULT_CHARSET 設定を 'utf-8' 以外に設定しているからといって、バイト文字列でその他のエンコーディングを使用できると思わないでください! DEFAULT_CHARSET は、テンプレートのレンダリング結果(および電子メール)として生成された文字列にのみ適用されます。Django は、内部的なバイト文字列については常に UTF-8 エンコーディングを想定します。その理由は、 DEFAULT_CHARSET 設定が実際には(アプリケーション開発者であるあなたの)制御下にないからです。それは、あなたのアプリケーションをインストールして使用している人が制御しています ― そして、その人が異なる設定を選択した場合でも、あなたのコードは引き続き動作しなければなりません。したがって、その設定に依存することはできません。

Djangoが文字列を扱う場合、大抵の場合、何をするにもまずそれらを文字列に変換します。従って、一般的なルールとして、バイト文字列を渡した場合は、結果として文字列を受け取る準備をしておくべきです。

翻訳された文字列

文字列やバイト文字列とは別に、Djangoを使用しているときに遭遇する可能性がある文字列型のオブジェクトがもう一つあります。フレームワークの国際化機能は「遅延翻訳(lazy な翻訳)」という概念を導入しています ― 翻訳済みとマークされているが、オブジェクトが文字列として使用されるまで実際の翻訳結果が決定されない文字列です。この機能は、文字列が最初に作成されたときにはコードがインポートされたにもかかわらず、文字列が使用されるまで翻訳ロケールが不明な場合に便利です。

通常、遅延翻訳について心配する必要はありません。ただし、オブジェクトを調べたときに django.utils.functional.__proxy__ オブジェクトであると表示される場合は、それが遅延翻訳であることに注意してください。遅延翻訳を引数として str() を呼び出すと、現在のロケールの文字列が生成されます。

詳細については、 国際化 ドキュメントを参照してください。

便利なユーティリティ関数

いくつかの文字列操作は度々必要とされるため、Django には文字列やバイト文字列の操作を少し簡単にする便利な関数がいくつか含まれています。

変換関数

django.utils.encoding モジュールには、文字列とバイト列との間で相互に変換するために便利ないくつかの関数が含まれています。

  • smart_str(s, encoding='utf-8', strings_only=False, errors='strict') は、その入力を文字列に変換します。 encoding パラメータは入力エンコーディングを指定します(例えば、Djangoはこれを内部で使用して、UTF-8 エンコードされていないかもしれないフォーム入力データを処理します)。 strings_only パラメータが True に設定されている場合、Python の数値、真偽値、そして None は文字列に変換されずに元の型を保持します。 errors パラメータは、Python の str() 関数がエラーハンドリングに受け入れる値のいずれかを取ります。
  • force_str(s, encoding='utf-8', strings_only=False, errors='strict') は、ほとんどの場合 smart_str() と同じです。違いは、最初の引数が 遅延翻訳(Lazyな翻訳) インスタンスである場合です。smart_str() は遅延翻訳を保持しますが、force_str() はそれらのオブジェクトを文字列に強制します(翻訳が発生します)。通常は smart_str() を使用しますが、文字列に絶対に 変換する必要がある テンプレートタグやフィルタには force_str() が役立ちます。
  • smart_bytes(s, encoding='utf-8', strings_only=False, errors='strict') は基本的に smart_str() の反対です。第一引数をバイト文字列に強制します。 strings_only パラメータは、smart_str() および force_str() と同じ動作をします。これは Python の組み込み関数 str() とは少し異なる意味論ですが、Django の内部で必要とされるいくつかの場所での違いです。

通常、force_str() を使用するだけで十分です。文字列またはバイト文字列の可能性があるあらゆる入力データに対して、できるだけ早くそれを呼び出しましょう。そうすることで、その結果を常に文字列として扱うことができます。

URI と IRI の処理

Web フレームワークは URL(IRI の一種)を扱う必要があります。URL の要件の1つは、ASCII 文字のみを使用してエンコードされることです。しかし、国際的な環境では、Unicode 文字を含むことができる IRI から URL を構築する必要があるかもしれません。IRI を URI に引用して変換するためには、これらの関数を使用します。

これら2つの関数のグループは、わずかに異なる目的があり、それらを区別することが重要です。通常、"&" や "%" のような予約文字が正しくエンコードされるように、IRI や URI パスの個々の部分に quote() を使用します。その後、完全な IRI に iri_to_uri() を適用し、非 ASCII 文字を正しいエンコード値に変換します。

注釈

技術的に言うと、iri_to_uri() が IRI 仕様の完全なアルゴリズムを実装していると言うのは正確ではありません。まだ、アルゴリズムの国際ドメイン名エンコーディング部分を実行していません。

iri_to_uri() 関数は、URL で許可されている ASCII 文字を変更しません。したがって、例えば、文字 "%" は iri_to_uri() に渡されたときに、さらにエンコードされることはありません。これは、完全な URL をこの関数に渡しても、クエリ文字列やそれに類するものを台無しにすることはないという意味です。

ここで例を挙げるとより理解しやすくなります:

>>> from urllib.parse import quote
>>> from django.utils.encoding import iri_to_uri
>>> quote("Paris & Orléans")
'Paris%20%26%20Orl%C3%A9ans'
>>> iri_to_uri("/favorites/François/%s" % quote("Paris & Orléans"))
'/favorites/Fran%C3%A7ois/Paris%20%26%20Orl%C3%A9ans'

よく見ると、2番目の例で quote() によって生成された部分が iri_to_uri() に渡された際に二重引用符で囲まれていないことがわかります。これは非常に重要で便利な機能です。つまり、IRIを構築する際に、非ASCII文字が含まれているかどうかを心配せずに、最後になってから結果に対して iri_to_uri() を呼び出すことができるということです。

同様に、Djangoは django.utils.encoding.uri_to_iri() を提供しており、これは RFC 3987#section-3.2 に従ってURIからIRIへの変換を実装しています。

デモンストレーションの例:

>>> from django.utils.encoding import uri_to_iri
>>> uri_to_iri("/%E2%99%A5%E2%99%A5/?utf8=%E2%9C%93")
'/♥♥/?utf8=✓'
>>> uri_to_iri("%A9hello%3Fworld")
'%A9hello%3Fworld'

最初の例では、UTF-8 の文字はクォートされていません。2番目の例では、パーセントエンコーディングは、有効な UTF-8 の範囲外にあるか、予約された文字を表しているため、変更されません。

iri_to_uri()uri_to_iri() 関数は冪等であり、つまり以下が常に真であることを意味します。

iri_to_uri(iri_to_uri(some_string)) == iri_to_uri(some_string)
uri_to_iri(uri_to_iri(some_string)) == uri_to_iri(some_string)

同じURI/IRIに対して複数回安全に呼び出すことができます。二重引用符の問題を気にする必要はありません。

モデル

データベースから返される全ての文字列が str オブジェクトとして返されるため、文字ベースのモデルフィールド(CharField、TextField、URLField など)には、Django がデータベースからデータを取得する際、Unicode の値が格納されます。これは、データが ASCII バイト文字列に収まる場合でも、 常に 当てはまります。

モデルを作成する際やフィールドに値を設定する際にバイト文字列を渡すことができ、Django は必要に応じてそれを文字列に変換します。

get_absolute_url() の注意事項

URL は ASCII 文字のみを含むことができます。ASCII でないデータの断片から URL を構築している場合は、URL に適した方法で結果をエンコードすることに注意してください。 reverse() 関数は、これを自動的に処理してくれます。

URL を手動で構築している場合(つまり、 reverse() 関数を 使用していない 場合)、エンコーディングを自分で処理する必要があります。この場合、 above で説明された iri_to_uri() 関数と quote() 関数を使用してください。例えば:

from urllib.parse import quote
from django.utils.encoding import iri_to_uri


def get_absolute_url(self):
    url = "/person/%s/?x=0&y=0" % quote(self.location)
    return iri_to_uri(url)

この関数は、 self.location が "Jack visited Paris & Orléans" のようなものであっても、正しくエンコードされた URL を返します。(実際には、上記の例では iri_to_uri() 呼び出しは厳密には必要ありません。なぜなら、最初の行での引用符によってすべての非 ASCII 文字が削除されていたからです。)

テンプレート

手動でテンプレートを作成する際には文字列を使用します:

from django.template import Template

t2 = Template("This is a string template.")

通常は、ファイルシステムからテンプレートを読み込むことが一般的です。テンプレートファイルが UTF-8 エンコーディングで保存されていない場合は、TEMPLATES 設定を調整してください。組み込みの django バックエンドは、ディスクからファイルを読み込む際に使用するエンコーディングを変更するための 'file_charset' オプションを提供しています。

DEFAULT_CHARSET 設定は、レンダリングされたテンプレートのエンコーディングを制御します。これはデフォルトで UTF-8 に設定されています。

テンプレートタグとフィルタ

自分自身のテンプレートタグとフィルタを書く際に覚えておくべきヒントをいくつか紹介します:

  • テンプレートタグの render() メソッドとテンプレートフィルタからは、常に文字列を返してください。
  • これらの場所では、smart_str() よりも force_str() を優先して使用してください。タグのレンダリングとフィルタの呼び出しは、テンプレートがレンダリングされている間に発生するため、遅延翻訳オブジェクトを文字列に変換することを遅らせる利点はありません。その時点で文字列のみを扱う方が簡単です。

ファイル

ユーザーにファイルのアップロードを許可するつもりであれば、Django を実行する環境が非 ASCII ファイル名に対応しているよう設定する必要があります。環境が正しく設定されていない場合、非 ASCII 文字を含むファイル名や内容のファイルを保存しようとしたときに UnicodeEncodeError 例外に遭遇します。

UTF-8ファイル名に対するファイルシステムのサポートは変わることがあり、環境に依存する場合があります。現在の設定をインタラクティブなPythonシェルで確認するには、次のコマンドを実行してください:

import sys

sys.getfilesystemencoding()

これは "UTF-8" と出力されます。

LANG 環境変数は、Unixプラットフォームにおいて予期されるエンコーディングを設定する責任があります。この変数を設定する適切な構文と場所については、使用するオペレーティングシステムとアプリケーションサーバーのドキュメントを参照してください。例については、 Django を Apache と mod_wsgi とともに使うには? を参照してください。

開発環境では、~/.bashrc に次のような設定を追加する必要があるかもしれません。

export LANG="en_US.UTF-8"

フォームの送信

HTML フォームの送信は、難しい領域です。送信がエンコーディング情報を含むとは限らず、それはフレームワークが送信されたデータのエンコーディングを推測しなければならないことを意味します。

Djangoはフォームデータのデコードに対して「遅延」アプローチを採用しています。HttpRequest オブジェクト内のデータは、アクセスしたときにのみデコードされます。実際には、ほとんどのデータはデコードされません。HttpRequest.GETHttpRequest.POST データ構造のみがデコードが適用されます。これら二つのフィールドは、メンバーをUnicodeデータとして返します。HttpRequest の他の属性やメソッドは、クライアントによって送信されたデータをそのまま正確に返します。

デフォルトでは、 DEFAULT_CHARSET 設定がフォームデータの想定されるエンコーディングとして使用されます。特定のフォームに対してこれを変更する必要がある場合は、 HttpRequest インスタンスの encoding 属性を設定できます。例えば:

def some_view(request):
    # We know that the data must be encoded as KOI8-R (for some reason).
    request.encoding = "koi8-r"
    ...

request.GETrequest.POST にアクセスした後であっても、エンコーディングを変更することができ、以降のすべてのアクセスでは新しいエンコーディングが使用されます。

ほとんどの開発者はフォームのエンコーディングを変更することを心配する必要はありませんが、制御できないレガシーシステムと通信するアプリケーションにとって、これは便利な機能です。

Django はファイルアップロードのデータをデコードしません。そのデータは通常、文字列ではなくバイトのコレクションとして扱われるからです。そこでの自動デコードは、バイトストリームの意味を変更してしまうでしょう。

Back to Top