Unicodeデータ
DjangoはどこでもUnicodeデータをサポートしています。
このドキュメントでは、ASCII以外でエンコードされたデータまたはテンプレートを使用するアプリケーションを作成する場合に知っておく必要があることを説明します。
データベースの作成
データベースが任意の文字列データを格納できるように構成されていることを確認してください。 通常、これはUTF-8またはUTF-16のエンコーディングを与えることを意味します。 より制限の厳しいエンコーディング(たとえば、latin1(iso8859-1))を使用すると、データベースに特定の文字を格納できなくなり、情報が失われます。
- MySQLユーザーは、データベースの文字セットエンコーディングを設定または変更する方法の詳細について、 MySQLマニュアルを参照してください。
- PostgreSQLユーザーは、正しいエンコーディングでデータベースを作成する方法の詳細について、 PostgreSQLマニュアル(PostgreSQL 9のセクション22.3.2)を参照してください。
- Oracleユーザーは、データベースの文字セットエンコーディングを設定(セクション2 )または変更(セクション11 )する方法の詳細について、 Oracleマニュアルを参照してください。
- SQLiteユーザーの皆さん、あなたがする必要はありません。 SQLiteは常に内部エンコーディングにUTF-8を使用します。
Djangoのすべてのデータベースバックエンドは、データベースと通信するために文字列を適切なエンコーディングに自動的に変換します。 また、データベースから取得した文字列を自動的に文字列に変換します。 データベースが使用するエンコーディングをDjangoに伝える必要はありません。それは透過的に処理されます。
詳細については、以下の「データベースAPI」のセクションを参照してください。
一般的な文字列処理
データベースルックアップ、テンプレートレンダリング、その他の場所でDjangoで文字列を使用する場合は常に、これらの文字列をエンコードするための2つの選択肢があります。 通常の文字列またはバイト文字列(「b」で始まる)を使用できます。
警告
バイト文字列には、そのエンコーディングに関する情報は含まれていません。 そのため、仮定を立てる必要があり、Djangoはすべてのバイト文字列がUTF-8であると仮定します。
他の形式でエンコードされた文字列をDjangoに渡すと、興味深い方法で問題が発生します。 通常、Djangoはある時点でUnicodeDecodeError
を発生させます。
コードでASCIIデータのみを使用する場合、ASCIIはUTF-8のサブセットであるため、通常の文字列を使用して自由に渡すことができます。
:setting: `DEFAULT_CHARSET` 設定が'utf-8'
以外に設定されている場合は、バイト文字列で他のエンコーディングを使用できると思い込まないでください。 :setting: `DEFAULT_CHARSET` は、テンプレートレンダリング(および電子メール)の結果として生成された文字列にのみ適用されます。 Djangoは、内部バイト文字列に対して常にUTF-8エンコーディングを想定します。 これは、:setting: `DEFAULT_CHARSET` 設定が実際には制御できないためです(アプリケーション開発者の場合)。 これは、アプリケーションをインストールして使用する人の管理下にあります。その人が別の設定を選択した場合でも、コードは引き続き機能する必要があります。 エルゴ、それはその設定に依存することはできません。
ほとんどの場合、Djangoが文字列を処理しているときは、他の処理を行う前に文字列を文字列に変換します。 したがって、原則として、バイト文字列を渡す場合は、結果で文字列を受け取る準備をしてください。
翻訳された文字列
文字列とバイト文字列の他に、Djangoを使用するときに遭遇する可能性のある3番目のタイプの文字列のようなオブジェクトがあります。 フレームワークの国際化機能は、「遅延翻訳」の概念を導入します。これは、翻訳済みとしてマークされているが、オブジェクトが文字列で使用されるまで実際の翻訳結果が決定されない文字列です。 この機能は、コードが最初にインポートされたときに文字列が最初に作成された可能性がある場合でも、文字列が使用されるまで翻訳ロケールが不明な場合に役立ちます。
通常、怠惰な翻訳について心配する必要はありません。 オブジェクトを調べて、それが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の数値、ブール値、および [になります。 X199X]文字列に変換されません(元の型を保持します)。errors
パラメーターは、エラー処理のためにPythonのstr()
関数によって受け入れられる任意の値を取ります。force_str(s, encoding='utf-8', strings_only=False, errors='strict')
は、ほとんどの場合smart_str()
と同じです。 違いは、最初の引数が遅延変換インスタンスの場合です。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文字のみを使用してエンコードされることです。 ただし、国際的な環境では、 IRI からURLを作成する必要がある場合があります。非常に大まかに言えば、 URI で次のことができます。 Unicode文字が含まれています。 IRIを引用してURIに変換するには、次の関数を使用します。
- django.utils.encoding.iri_to_uri()関数。 RFC 3987#section-3.1 の要求に応じてIRIからURIへの変換を実装します。
urllib.parse.quote()
およびurllib.parse.quote_plus()
関数は、Pythonの標準ライブラリに含まれています。
これらの2つの機能グループの目的はわずかに異なるため、それらをまっすぐに保つことが重要です。 通常は、quote()
IRIまたはURIパスの個々の部分で、「&」や「%」などの予約文字が正しくエンコードされるようにします。 次に、iri_to_uri()
を完全なIRIに適用すると、非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()
に渡されたときに二重引用符で囲まれていないことがわかります。 これは非常に重要で便利な機能です。 これは、非ASCII文字が含まれているかどうかを気にせずにIRIを構築し、最後に結果に対して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()
関数を使用してではなく)、エンコードは自分で行う必要があります。 この場合、上記に記載されている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)
この関数は、次の場合でも正しくエンコードされたURLを返します。self.location
「ジャックがパリとオルレアンを訪れた」のようなものです。 (実際、上記の例ではiri_to_uri()
呼び出しは厳密には必要ありません。これは、最初の行の引用ですべての非ASCII文字が削除されているためです。)
テンプレート
テンプレートを手動で作成するときは文字列を使用します。
from django.template import Template
t2 = Template('This is a string template.')
ただし、一般的なケースは、ファイルシステムからテンプレートを読み取ることです。 テンプレートファイルがUTF-8エンコーディングで保存されていない場合は、:setting: `TEMPLATES` 設定を調整してください。 組み込みの django バックエンドには、ディスクからファイルを読み取るために使用されるエンコーディングを変更するための'file_charset'
オプションが用意されています。
:setting: `DEFAULT_CHARSET` 設定は、レンダリングされたテンプレートのエンコードを制御します。 これはデフォルトでUTF-8に設定されています。
ファイル
ユーザーがファイルをアップロードできるようにする場合は、Djangoの実行に使用される環境が非ASCIIファイル名で機能するように構成されていることを確認する必要があります。 環境が正しく構成されていない場合、ASCII以外の文字を含むファイル名でファイルを保存すると、UnicodeEncodeError
例外が発生します。
UTF-8ファイル名のファイルシステムサポートはさまざまであり、環境によって異なる場合があります。 次のコマンドを実行して、インタラクティブなPythonシェルで現在の構成を確認します。
import sys
sys.getfilesystemencoding()
「UTF-8」が出力されます。
LANG
環境変数は、Unixプラットフォームで期待されるエンコーディングを設定する役割を果たします。 この変数を設定するための適切な構文と場所については、オペレーティングシステムとアプリケーションサーバーのドキュメントを参照してください。
開発環境では、次のような設定を~.bashrc
に追加する必要がある場合があります。
export LANG="en_US.UTF-8"
フォームの送信
HTMLフォームの送信は難しい領域です。 送信にエンコード情報が含まれるという保証はありません。つまり、フレームワークは送信されたデータのエンコードを推測する必要がある場合があります。
Djangoは、フォームデータのデコードに「レイジー」アプローチを採用しています。 HttpRequest
オブジェクトのデータは、アクセスしたときにのみデコードされます。 実際、ほとんどのデータはまったくデコードされていません。 HttpRequest.GET
およびHttpRequest.POST
データ構造のみにデコードが適用されます。 これらの2つのフィールドは、メンバーをUnicodeデータとして返します。 HttpRequest
の他のすべての属性とメソッドは、クライアントから送信されたとおりにデータを返します。
デフォルトでは、:setting: `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.GET
またはrequest.POST
にアクセスした後でエンコードを変更することもでき、それ以降のすべてのアクセスでは新しいエンコードが使用されます。
ほとんどの開発者はフォームエンコーディングの変更について心配する必要はありませんが、これは、エンコーディングを制御できないレガシーシステムと通信するアプリケーションにとって便利な機能です。
Djangoは、ファイルアップロードのデータをデコードしません。これは、そのデータが通常、文字列ではなくバイトのコレクションとして扱われるためです。 そこでの自動デコードは、バイトストリームの意味を変更します。