データベース

Django は次のデータベースを公式にサポートしています。

サードパーティから提供されているデータベースバックエンド も多数あります。

Django はすべてのデータベースで可能な限り多くの機能をサポートするように努めています。しかし、すべてのデータベースバックエンドが似ているわけではないため、どの機能をサポートし、どのような前提なら安全に想定できるのかについて、設計上の決断を下す必要がありました。

このファイルは Django の利用に関係する可能性のある一部の機能を説明しています。サーバー固有のドキュメントやリファレンスマニュアルを置換することを意図したものではありません。

一般的なメモ

持続的 (persistent) な接続

永続的な接続は HTTP リクエストごとにデータベースへの接続を再確立するオーバーヘッドを回避します。持続的な接続は接続の最大寿命を定義する CONN_MAX_AGE パラメータによって制御されます。このパラメータはデータベースごとに個別に設定できます。

デフォルト値は 0 で、各リクエストの終了時にデータベース接続をクローズするという従来の動作を維持します。持続的な接続を有効にするには、 CONN_MAX_AGE に秒単位の正の整数を設定します。持続的な接続を無制限にするには、None に設定します。

接続管理

Django は、最初にデータベースへの問い合わせを行うときに、データベースへの接続をオープンします。Django はこの接続をオープンしたままにしておき、その後のリクエストで再利用し ます。Django は接続が CONN_MAX_AGE で定義された最大寿命を超えたり、それ以上使えなくなると接続をクローズします。

詳しくは、Djangoは必要なときに、そしてすでに接続がない場合(これが最初の接続であるか、前の接続がクローズされたため)に自動的にデータベースへの接続をオープンします。

各リクエストの最初に、Django は接続が最大寿命に達した場合、接続をクローズします。もしデータベースがアイドル状態の接続をしばらくしてから終了するのであれば、 CONN_MAX_AGE を低い値に設定して、Django がデータベースサーバにすでに終了した接続を使おうとしないようにすべきです。(この問題は、非常にトラフィックの少ないサイトにしか影響しないかもしれません)。

各リクエストの終了時に、 Django は接続が最大寿命に達していたり、回復不可能なエラー状態になっていたりすると、接続をクローズします。リクエストの処理中にデータベースエラーが発生した場合、 Django は接続がまだ動作しているかどうかをチェックし、動作していなければ接続をクローズします。接続が使えなくなると、次のリクエストは新しい接続を取得します。

CONN_HEALTH_CHECKSTrue に設定することで、接続の再利用の堅牢性を向上させ、データベースサーバーによって接続がクローズされた後に新しい接続を受け入れて提供する準備ができている場合(例えば、データベースサーバーの再起動後など)にエラーを防ぐことができます。ヘルスチェックはリクエストごとに一度だけ実行され、リクエストの処理中にデータベースがアクセスされている場合のみ実行されます。

注意事項

各スレッドは独自の接続を維持するため、データベースは少なくともワーカースレッドと同数の同時接続をサポートする必要があります。

データベースが外部システムのデータベースであったり、キャッシュのおかげでビューの大部分からアクセスされないことがあります。このような場合、 CONN_MAX_AGE を低い値、あるいは 0 に設定する必要があります。これにより、このデータベースへの同時接続数を少なく保つことができます。

開発用サーバはリクエストを処理するたびに新しいスレッドを生成し、 持続的な接続の効果を無効にします。開発中は有効にしないでください。

Django がデータベースへの接続を確立するとき、使われているバックエンドに応じて適切なパラメータを設定します。持続的な接続を有効にすると、この設定はリクエストの度に繰り返されなくなります。接続の分離レベルやタイムゾーンなどのパラメータを変更した場合は、リクエストの最後に Django のデフォルトに戻すか、リクエストの最初に適切な値を設定するか、持続的な接続を無効にしてください。

接続が Django のリクエスト/レスポンスサイクルの外側の、長く実行されるプロセスで作成された場合、接続は明示的にクローズされるか、タイムアウトが発生するまでオープンされたままになります。

エンコーディング

Django はすべてのデータベースが UTF-8 エンコーディングを使用することを想定しています。他のエンコーディングを使用すると、Django にとっては有効なデータに対して、データベースから「value too long (値が大きすぎる)」エラーなどの予期しない動作が起こる可能性があります。データベースを正しく設定する方法については、以下のデータベース固有のメモを確認してください。

PostgreSQL のメモ

Django は PostgreSQL 12 以上をサポートします。psycopg 3.1.8+ または psycopg2 2.8.4+ が必要ですが、最新の psycopg 3.1.8+ が推奨されます。

注釈

psycopg2 のサポートは、将来のある時点で非推奨となり削除される可能性があります。

Changed in Django 4.2:

psycopg 3.1.8以降のサポートが追加されました。

PostgreSQL の接続設定

詳細は HOST を参照してください。

connection service file のサービス名と password file のパスワードを使用して接続するためには、それらを DATABASES 内のデータベース設定の OPTIONS 部分で指定しなければなりません。

settings.py
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "OPTIONS": {
            "service": "my_service",
            "passfile": ".my_pgpass",
        },
    }
}
.pg_service.conf
[my_service]
host=localhost
user=USER
dbname=NAME
port=5432
.my_pgpass
localhost:5432:NAME:USER:PASSWORD

PostgreSQLバックエンドは OPTIONS の内容をキーワード引数として接続コンストラクタに渡します。これにより、ドライバの動作をより高度に制御できます。利用可能なすべての parameters についてはPostgreSQLのドキュメントで詳しく説明されています。

警告

テスト目的のサービス名の使用はサポートされていません。これは 将来実装される可能性があります

PostgreSQL の設定を最適化する

Django はデータベース接続のために次のパラメータが必要です。

  • client_encoding: 'UTF8',
  • default_transaction_isolation: デフォルトの 'read committed' または接続オプションで設定した値 (以下を参照)。
  • timezone:
    • USE_TZTrue のとき、デフォルトの 'UTC' または接続のために設定した TIME_ZONE の値。
    • USE_TZFalse のとき、グローバルの TIME_ZONE 設定の値。

これらのパラメータがすでに正しい値を持っている場合、 Django は新しい接続のたびにパラメータを設定しないので、パフォーマンスが少し改善されます。これらのパラメータは postgresql.conf で直接設定することもできますし、 ALTER ROLE でデータベースユーザごとに設定することもできます。

Django はこの最適化なしでも問題なく動作しますが、新しい接続のたびに、これらのパラメータを設定するために追加のクエリを実行します。

分離レベル

PostgreSQL 自体と同様に、Django のデフォルトは READ COMMITTEDisolation level に設定されています。REPEATABLE READSERIALIZABLE などのより高いレベルの分離レベルが必要な場合は、DATABASES 内のデータベース設定の OPTIONS 部分に指定してください。

from django.db.backends.postgresql.psycopg_any import IsolationLevel

DATABASES = {
    # ...
    "OPTIONS": {
        "isolation_level": IsolationLevel.SERIALIZABLE,
    },
}

注釈

より高い分離レベルのもとでは、アプリケーションはシリアライズの失敗時に発生する例外を処理できるように準備しておく必要があります。このオプションは高度な利用のために設計されています。

Changed in Django 4.2:

IsolationLevel が追加されました。

ロール

New in Django 4.2.

データベース接続のためにコネクションの確立に使用するロールとは別のロールを使用する必要がある場合は、次のように DATABASES 内のデータベース設定の OPTIONS 部分で指定してください。

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        # ...
        "OPTIONS": {
            "assume_role": "my_application_role",
        },
    },
}

サーバーサイド パラメータ バインディング

New in Django 4.2.

psycopg 3.1.8+ では、Django のデフォルトは クライアントサイド・バインディングカーソル です。もし サーバーサイド・バインディング を使いたい場合は、 DATABASES のデータベース設定の OPTIONS の部分で設定してください:

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        # ...
        "OPTIONS": {
            "server_side_binding": True,
        },
    },
}

このオプションは psycopg2 では無視されます。

varchar カラムと text カラムのインデックス

モデルフィールドに db_index=True を指定すると、Django は通常 1 つの CREATE INDEX ステートメントを出力します。しかし、フィールドのデータベースタイプが varchartext (例えば、 CharFieldFileFieldTextField で使われます) の場合、Django はカラムに適切な PostgreSQL operator class を使う追加のインデックスを作成します。この追加インデックスは、 containsstartswith のような LIKE 演算子を SQL で使ったルックアップを正しく実行するために必要です。

拡張機能を追加するためのマイグレーション・オペレーション

マイグレーションを使用して PostgreSQL 拡張機能 (hstorepostgis など) を追加する必要がある場合は、 CreateExtension オペレーションを使用してください。

サーバーサイド・カーソル

QuerySet.iterator() を使うと、 Django は サーバーサイドカーソル をオープンします。デフォルトでは、PostgreSQL はカーソルクエリの結果の最初の10%だけがフェッチされると仮定しています。クエリプランナはクエリの計画に費やす時間を減らし、より速く結果を返すようになりますが、結果の10%以上が取得された場合、性能が低下する可能性があります。PostgreSQLがカーソルクエリで取得する行数に関する仮定を制御するには、 cursor_tuple_fraction オプションを使用してください。

トランザクションプールとサーバーサイドカーソル

トランザクションプーリングモード(たとえば、PgBouncer )で接続プーラを使用するには、その接続のサーバーサイドカーソルを無効にする必要があります。

サーバーサイドカーソルは接続に対してローカルに存在し、 AUTOCOMMITTrue の場合、トランザクションの終了時にも接続をオープンしたままにします。後続のトランザクションはサーバーサイドカーソルからより多くの結果を取得しようとするかもしれません。トランザクションプーリングモードでは、後続のトランザクションが同じ接続を使用するという保証はありません。異なる接続が使用される場合、トランザクションがサーバーサイドカーソルを参照する時にエラーが発生します。サーバーサイドカーソルは、それが作成された接続でのみアクセス可能だからです。

一つの解決策は、 DATABASESDISABLE_SERVER_SIDE_CURSORSTrue に設定して、接続のサーバーサイドカーソルを無効にすることです。

トランザクションプーリングモードでサーバーサイドカーソルの恩恵を受けるには、サーバーサイドカーソルを使用するクエリを実行するために、データベースへのもう1つの接続 を設定します。この接続はデータベースに直接接続するか、セッションプーリングモードの接続プーラに接続する必要があります。

もう一つの方法は、サーバーサイドのカーソルを使用している QuerySetatomic() ブロックでラップすることです。これはトランザクションの間、 autocommit を無効にするからです。これにより、サーバーサイドカーソルはトランザクションの間だけ生きることになります。

自動インクリメントの主キーの値を手動で指定する

Django は自動インクリメントの主キーを格納するために PostgreSQL の identity カラムを使います。identity カラムには、次に利用可能な値を追跡する sequence の値が代入されます。自動インクリメントのフィールドに手動で値を代入しても、フィールドのシーケンスは更新されません。たとえば下記のようになります:

>>> from django.contrib.auth.models import User
>>> User.objects.create(username="alice", pk=1)
<User: alice>
>>> # The sequence hasn't been updated; its next value is 1.
>>> User.objects.create(username="bob")
IntegrityError: duplicate key value violates unique constraint
"auth_user_pkey" DETAIL:  Key (id)=(1) already exists.

そのような値を指定する必要がある場合は、既にテーブルにある値を再利用しないように、後でシーケンスをリセットしてください。 sqlsequencereset 管理コマンドはそのための SQL 文を生成します。

テストデータベースのテンプレート

TEST['TEMPLATE'] 設定を使用すると、テストデータベースを作成するための template (たとえば 'template0') を指定できます。

non-durable 設定でテストの実行を高速化する

PostgreSQL を non-durable に設定する ことで、テストの実行時間を短縮できます。

警告

これは危険です。サーバがクラッシュしたり電源が切れたりした場合に、データベースがデータ損失や破損の影響を受けやすくなります。クラスタ内の全データベースの内容を簡単にリストアできる開発マシンでのみ使用してください。

MariaDB に関する注意事項

Django は MariaDB 10.4 以降をサポートしています。

MariaDBを使用するには、MySQLバックエンドを使用してください、これは2つの間で共有されています。詳細は MySQL に関する注意事項 を参照してください。

MySQL に関する注意事項

バージョン サポート

Django は MySQL 8.0.11 以降をサポートしています。

Django の inspectdb 機能は information_schema データベースを使います。このデータベースには全てのデータベーススキーマの詳細なデータが含まれています。

Django は、データベースが Unicode (UTF-8 エンコーディング) をサポートすることを期待し、 トランザクションと参照整合性を強制するタスクを MySQL に委譲します。MyISAM ストレージエンジンを使う場合、MySQL が後者 2 つを実際には強制していないという事実に注意することが重要です。次の節を参照してください。

ストレージ エンジン

MySQL にはいくつかの storage engines があります。サーバ設定でデフォルトのストレージエンジンを変更できます。

MySQL のデフォルトのストレージエンジンは InnoDB です。このエンジンは完全にトランザクショナルで、外部キー参照をサポートしています。推奨される選択です。しかし、InnoDB は AUTO_INCREMENT の値を記憶せず、"max(id)+1" として再作成するため、MySQL の再起動時に自動インクリメントカウンタが失われます。このため、 AutoField の値が不用意に再利用される可能性があります。

MyISAM の主な欠点は、トランザクションをサポートしていないことと、外部キー制約を強制していないことです。

MySQL DB API ドライバ

MySQL には PEP 249 で説明されている Python Database API を実装したドライバがいくつかあります:

  • mysqlclient はネイティブドライバです。 推奨される選択肢です
  • MySQL Connector/Python は Oracle による純粋な Python ドライバで、MySQL クライアントライブラリや標準ライブラリ以外の Python モジュールを必要としません。

これらのドライバはスレッドセーフで、コネクションプールを提供します。

DB API ドライバに加え、Django は ORM からデータベースドライバにアクセスするためのアダプタが必要です。Django は mysqlclient 用のアダプタを提供しますが、 MySQL Connector/Python にはそれ自身 its own のものが含まれます。

mysqlclient

Django には mysqlclient 1.4.3 以降が必要です。

MySQL Connector/Python

MySQL Connector/Python は download page から入手できます。Django アダプタはバージョン 1.1.X 以降で利用可能です。Django の最新リリースには対応していないかもしれません。

タイムゾーンの定義

Django の タイムゾーンサポート を使うつもりなら、 mysql_tzinfo_to_sql を使って MySQL データベースにタイムゾーンテーブルを読み込んでください。これはデータベースごとではなく、MySQLサーバーに対して一度だけ行う必要があります。

データベースを作成する

コマンドラインツールとこのSQLを使用して、 create your database することができます:

CREATE DATABASE <dbname> CHARACTER SET utf8;

これにより、すべてのテーブルとカラムがデフォルトでUTF-8を使用するようになります。

照合順序 (collation) の設定

カラムの照合順序 (collation) の設定は、どの文字列を等しいものとして比較するかだけでなく、データをソートする順序も制御します。 CharFieldTextField には db_collation パラメータを指定して、カラムの照合順序を設定できます。

照合順序はデータベース全体およびテーブルごとに設定することもできます。これについては、MySQL ドキュメント documented thoroughly で詳しく説明されています。このような場合、照合順序はデータベースの設定やテーブルを直接操作して設定する必要があります。Django はそれらを変更する API を提供しません。

デフォルトでは、UTF-8 データベースでは、MySQL は utf8_general_ci 照合順序を使用します。この結果、すべての文字列の等値比較は 大文字小文字を区別しない 方法で行われます。つまり、"Fred""freD" はデータベースレベルでは等しいとみなされます。フィールドにユニーク制約がある場合、"aa""AA" の両方を同じカラムに挿入しようとするのは不正です。デフォルトの照合順序では等しい (つまり一意ではない) と比較されるからです。特定のカラムやテーブルで大文字小文字を区別して比較したい場合は、そのカラムやテーブルを utf8_bin 照合順序を使用するように変更してください。

MySQL Unicode Character Sets によると、照合順序 utf8_general_ci の比較は、utf8_unicode_ci の比較よりも高速ですが、正しさは若干劣ることに注意してください。もしこれがあなたのアプリケーションで許容できるのであれば、 utf8_general_ci を使うべきです。これが許容できない場合(例えば、ドイツ語の辞書の並び順が必要な場合)、より正確な utf8_unicode_ci を使用してください。

警告

モデルのフォームセットは大文字小文字を区別して一意なフィールドをバリデーションします。したがって、大文字小文字を区別しない照合順序を使用している場合、大文字小文字の違いだけで一意なフィールド値を持つフォームセットはバリデーションを通過しますが、 save() を呼び出すと IntegrityError が発生します。

データベースに接続する

設定のドキュメント を参照してください。

接続設定はこの順番で使用されます:

  1. OPTIONS.
  2. NAME, USER, PASSWORD, HOST, PORT
  3. MySQL オプションファイル。

言い換えると、OPTIONS でデータベースの名前を設定した場合、これは NAME よりも優先されます。これは MySQL option file の内容を上書きすることになります。

以下は MySQL オプションファイルを使用するサンプル設定です:

# settings.py
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.mysql",
        "OPTIONS": {
            "read_default_file": "/path/to/my.cnf",
        },
    }
}
# my.cnf
[client]
database = NAME
user = USER
password = PASSWORD
default-character-set = utf8

他にも sslinit_commandsql_mode などの MySQLdb connection options が役に立ちます。

sql_mode の設定

sql_mode オプションのデフォルト値には STRICT_TRANS_TABLES が含まれています。このオプションは、挿入時にデータが切り捨てられた場合に警告をエラーにエスカレートさせるため、Djangoはデータ損失を防ぐためにMySQLで strict mode を有効にすることを強く推奨します (STRICT_TRANS_TABLES または STRICT_ALL_TABLES のいずれか)。

SQLモードをカスタマイズする必要がある場合は、他のMySQLオプションと同様に sql_mode 変数を設定できます。設定ファイル内、または DATABASES のデータベース設定の OPTIONS 部分に 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'" エントリを追加することで設定できます。

分離レベル

並行読み込みを実行する場合、異なるセッション(例えば、異なるリクエストを処理する別々のスレッド)からのデータベース・トランザクションは互いに影響し合う可能性があります。これらの相互作用は各セッションの transaction isolation level の影響を受けます。接続の分離レベルは DATABASESOPTIONS 部分にある 'isolation_level' エントリで設定できます。このエントリの有効な値は4つの標準的な分離レベルです:

  • 'read uncommitted'
  • 'read committed'
  • 'repeatable read'
  • 'serializable'

または None でサーバで設定された分離レベルを使用します。しかし、Django は MySQL のデフォルトである repeatable read ではなく、commited read をデフォルトとしています。repeatable read ではデータ損失が発生する可能性があります。特に、 get_or_create()IntegrityError を発生させますが、その後の get() 呼び出しではオブジェクトが表示されないような場合があります。

テーブルを作成する

Django はスキーマを生成するとき、ストレージエンジンを指定しないので、テーブルはデータベースサーバが設定したデフォルトのストレージエンジンで作成されます。最も簡単な解決策は、データベースサーバのデフォルトのストレージエンジンを、希望のエンジンに設定することです。

ホスティングサービスを利用していて、サーバーのデフォルトストレージエンジンを変更できない場合、いくつかの選択肢があります。

  • テーブルが作成された後、 ALTER TABLE ステートメントを実行して、テーブルを新しいストレージエンジン(InnoDBなど)に変換します:

    ALTER TABLE <tablename> ENGINE=INNODB;
    

    これはテーブルの数が多い場合、面倒です。

  • テーブルを作成する前に MySQLdb の init_command オプションを使用する方法もあります:

    "OPTIONS": {
        "init_command": "SET default_storage_engine=INNODB",
    }
    

    これは、データベース接続時のデフォルトのストレージエンジンを設定します。テーブルが作成された後は、このオプションを削除してください。テーブルの作成時にのみ必要なクエリを各データベース接続に追加することになります。

テーブル名

MySQL の最新バージョンでも、特定の条件下で特定の SQL 文を実行すると、テーブル名の大文字と小文字が変わってしまうという既知の問題 known issues があります。この動作から発生する可能性のある問題を避けるために、可能であれば小文字のテーブル名を使うことをお勧めします。Django はモデルからテーブル名を自動生成するときに小文字のテーブル名を使うので、これは主に db_table パラメータでテーブル名を上書きする場合に考慮すべき点です。

セーブポイント

Django ORM と MySQL (InnoDB ストレージエンジン を使う場合) はどちらも、データベース セーブポイント をサポートしています。

MyISAM ストレージエンジンを使用している場合、 トランザクション API のセーブポイント関連メソッド を使おとすると、データベース由来のエラーが発生することに注意してください。これは、MySQL データベース/テーブルのストレージエンジンを検出するのは高コストな操作であるため、このような検出結果に基づいてこれらのメソッドを何もしない関数に動的に変換する価値はないと判断されるためです。

特定のフィールドに関する注意事項

文字 (Char) フィールド

フィールドに unique=True を使用している場合、カラムタイプが VARCHAR で格納されているフィールドの max_length が 255 文字に制限される可能性があります。これは CharField, SlugField に影響します。詳細は the MySQL documentation を参照してください。

TextField の制限

MySQL は BLOB または TEXT カラムの最初の N 文字だけをインデックス化できます。 TextField は定義された長さを持たないので、 unique=True としてマークすることはできません。MySQL は次のように報告します: "BLOB/TEXT column '<db_column>' used in key specification without a key length" 。

Time および DateTime フィールドの 1 秒単位未満の時間のサポート

MySQL では、カラム定義に端数表示 (例: DATETIME(6)) が含まれていれば、秒未満の端数を格納できます。

Django は、データベースサーバがサポートしている場合、既存のカラムを秒未満の端数を含むようにアップグレードしません。既存のデータベースで秒未満のカラムを有効にしたい場合は、以下のようなコマンドを実行して、手動で対象のデータベースのカラムを更新する必要があります:

ALTER TABLE `your_table` MODIFY `your_datetime_column` DATETIME(6)

または データマイグレーションRunSQL オペレーションを使用します。

TIMESTAMP カラム

もし TIMESTAMP カラムを含むレガシーデータベースを使用している場合、データの破損を避けるために、USE_TZ = False を設定する必要があります。inspectdb はこれらのカラムを DateTimeField にマッピングし、タイムゾーンサポートを有効にすると、MySQLとDjangoの両方が値をUTCからローカル時間に変換しようと試みます。

QuerySet.select_for_update() による行のロック

MySQL と MariaDB は SELECT ... FOR UPDATE 文のいくつかのオプションをサポートしていません。もし select_for_update() がサポートされていないオプションで使用された場合、 NotSupportedError が発生します。

オプション MariaDB MySQL
SKIP LOCKED X (≥10.6) X
NOWAIT X X
OF   X
NO KEY    

MySQL で select_for_update() を使用する場合は、少なくともユニーク制約に含まれるフィールドのセットに対してクエリセットをフィルタリングするか、インデックスがカバーするフィールドに対してだけフィルタリングするようにしてください。そうしないと、トランザクションの間、テーブル全体に対して排他的書き込みロックがかかります。

自動型キャストは予期せぬ結果を引き起こすことがあります

文字列型で整数値のクエリを実行する場合、MySQL はテーブル内のすべての値の型を整数に強制的に変換してから比較を実行します。テーブルに 'abc', 'def' という値があり、WHERE mycolumn=0 というクエリを実行した場合、両方の行がマッチします。同様に、WHERE mycolumn=1 は値 'abc1' にマッチします。したがって、 Django の文字列型フィールドは、クエリで使う前に必ず値を文字列にキャストします。

Field を直接継承したカスタムモデルフィールドを実装したり、 get_prep_value() をオーバーライドしたり、 RawSQLextra()raw() をオーバーライドする場合は、適切な型キャストを行う必要があります。

SQLite に関する注意事項

Django は SQLite 3.27.0 以降をサポートしています。

SQLite は、主に読み取り専用であったり、より小さなインストール容量を必要とするアプリケーションのための優れた開発代替手段を提供します。しかし、すべてのデータベースサーバと同様に、SQLite 固有の注意すべき違いがいくつかあります。

部分文字列のマッチングと大文字と小文字の区別

SQLite のすべてのバージョンで、いくつかの型の文字列をマッチさせようとすると、少し直感に反する動作があります。 これは Queryset で iexactcontains フィルタを使用した場合に発生します。動作は2つのケースに分かれます:

1. For substring matching, all matches are done case-insensitively. That is a filter such as filter(name__contains="aa") will match a name of "Aabb".

2. For strings containing characters outside the ASCII range, all exact string matches are performed case-sensitively, even when the case-insensitive options are passed into the query. So the iexact filter will behave exactly the same as the exact filter in these cases.

この回避策はドキュメント化されています documented at sqlite.org が、Django のデフォルトの SQLite バックエンドでは利用されていません。それらを堅牢に組み込むのはかなり難しいからです。そのため、Django はデフォルトの SQLite の動作を踏襲しており、大文字小文字を区別しないフィルタリングや部分文字列フィルタリングを行う際には、このことを意識する必要があります。

10進数 (Decimal) の扱い

SQLiteには本物の10進数の内部型はありません。 SQLite datatypes documentation で説明されているように、10進数の値は内部的に REAL データ型 (8バイトの IEEE 浮動小数点数) に変換されます。

"Database is locked" エラー

SQLite は軽量なデータベースであるため、高い並行性をサポートすることはできません。 OperationalError: database is locked のエラーは、アプリケーションがデフォルト設定の sqlite で扱える以上の同時実行を試行していることを示しています。このエラーは、あるスレッドまたはプロセスがデータベース接続を排他的にロックし、別のスレッドがロックが解除されるのを待ってタイムアウトしたことを意味します。

Python の SQLite ラッパーにはデフォルトのタイムアウト値があり、2番目のスレッドがタイムアウトして OperationalError: database is locked というエラーを発生させるまでに、どれだけの時間ロックを待つことができるかを決めます。

このエラーが発生した場合は、次の方法で解決できます:

  • 他のデータベース・バックエンドに乗り換えることです。ある段階でSQLiteは実世界のアプリケーションには "軽すぎる "ようになり、この種の同時実行エラーはその段階に達していることを示しています。

  • 並行処理を減らし、データベーストランザクションが短時間で終了するようにコードを書き換えます。

  • データベースオプションの timeout を設定することで、デフォルトのタイムアウト値を増やすことができます:

    "OPTIONS": {
        # ...
        "timeout": 20,
        # ...
    }
    

    これはSQLiteが "データベースがロックされています" エラーを発生させるまで少し待機させます。解決するために何かするわけではありません。

QuerySet.select_for_update() はサポートされていません。

SQLite は SELECT ... FOR UPDATE 構文をサポートしていません。これを呼び出しても何も起こりません。

QuerySet.iterator() を使用する際の分離の問題

QuerySet.iterator() を使用してテーブルをイテレートしながらテーブルを変更する場合、 Isolation In SQLite で説明されている特別な注意点があります。ループ内で行が追加、変更、削除された場合、その行はイテレータからフェッチされた後続の結果に現れるかもしれませんし、現れないかもしれません。コード内でこれを処理する必要があります。

SQLite で JSON1 拡張機能を有効にする

SQLite で JSONField を使うには、Python の sqlite3 ライブラリで JSON1 extension を有効にする必要があります。拡張機能が有効になっていない場合、システムエラー (fields.E180) が発生します。

JSON1エクステンションを有効にするには、 the wiki page の手順に従ってください。

注釈

JSON1 拡張は SQLite 3.38+ ではデフォルトで有効になっています。

Oracle に関する注意事項

Django は Oracle Database Server バージョン 19c 以降をサポートしています。バージョン 1.3.2 以上の oracledb Python ドライバが必要です。

バージョン 5.0 で非推奨: cx_Oracle のサポートは非推奨になりました。

python manage.py migrate コマンドを動作させるには、Oracleデータベースユーザーが以下のコマンドを実行する権限を持っている必要があります:

  • CREATE TABLE
  • CREATE SEQUENCE
  • CREATE PROCEDURE
  • CREATE TRIGGER

プロジェクトのテスト・スイートを実行するには、通常、ユーザーはこれらの 追加 権限を必要とします:

  • CREATE USER
  • ALTER USER
  • DROP USER
  • CREATE TABLESPACE
  • DROP TABLESPACE
  • CREATE SESSION WITH ADMIN OPTION
  • CREATE TABLE WITH ADMIN OPTION
  • CREATE SEQUENCE WITH ADMIN OPTION
  • CREATE PROCEDURE WITH ADMIN OPTION
  • CREATE TRIGGER WITH ADMIN OPTION

RESOURCE ロールは CREATE TABLE, CREATE SEQUENCE, CREATE PROCEDURE, CREATE TRIGGER の権限を持っており、RESOURCE WITH ADMIN OPTION を与えられたユーザは RESOURCE を与えることができますが、個々の権限 (例えば CREATE TABLE) を与えることはできません。そのため、RESOURCE WITH ADMIN OPTION は通常テストの実行には十分ではありません。

これらを実行するには、ユーザは CREATE VIEW WITH ADMIN OPTIONCREATE MATERIALIZED VIEW WITH ADMIN OPTION 権限も必要です。特に、これは Django 自身のテストスイートに必要です。

これらの権限はすべて DBA ロールに含まれており、プライベートな開発者のデータベースで使用するのに適しています。

Oracle データベースのバックエンドは SYS.DBMS_LOB パッケージと SYS.DBMS_RANDOM パッケージを使用しているので、ユーザーには実行パーミッションが必要です。通常はデフォルトですべてのユーザーがアクセス可能ですが、そうでない場合は以下のようにパーミッションを付与する必要があります:

GRANT EXECUTE ON SYS.DBMS_LOB TO user;
GRANT EXECUTE ON SYS.DBMS_RANDOM TO user;

データベースに接続する

Oracle データベースのサービス名を使用して接続するには、settings.py ファイルを次のように設定します:

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.oracle",
        "NAME": "xe",
        "USER": "a_user",
        "PASSWORD": "a_password",
        "HOST": "",
        "PORT": "",
    }
}

この場合、HOSTPORT の両方を空のままにします。しかし、tnsnames.ora ファイルや同様の命名メソッドを使用せず、SID(この例では "xe")を使用して接続したい場合は、次のように HOSTPORT の両方を入力します:

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.oracle",
        "NAME": "xe",
        "USER": "a_user",
        "PASSWORD": "a_password",
        "HOST": "dbprod01ned.mycompany.com",
        "PORT": "1540",
    }
}

HOSTPORT の両方を指定するか、両方を空文字列のままにしてください。Django はその選択によって異なる接続記述子を使います。

Full DSN と Easy Connect

HOSTPORT の両方が空の場合、NAME に Full DSN または Easy Connect 文字列を使用できます。このフォーマットは、例えば tnsnames.ora のない RAC やプラガブル (pluggable) なデータベースを使用する場合に必要です。

Easy Connect 文字列の例:

"NAME": "localhost:1521/orclpdb1"

Full DSN 文字列の例:

"NAME": (
    "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))"
    "(CONNECT_DATA=(SERVICE_NAME=orclpdb1)))"
)

スレッド (threaded) オプション

Django をマルチスレッド環境 (例えば、最近のオペレーティングシステムでデフォルトの MPM モジュールを使った Apache など) で動作させたい場合は、 Oracle データベースの設定の threaded オプションを True に設定する必要があります:

"OPTIONS": {
    "threaded": True,
}

これを行わないと、クラッシュその他の異常な動作が発生する可能性があります。

INSERT ... RETURNING INTO

デフォルトでは、Oracle バックエンドは新しい行を挿入するときに AutoField の値を効率的に取得するために RETURNING INTO 句を使用します。 この動作は、リモートテーブルへの挿入や INSTEAD OF トリガを使用したビューへの挿入など、特殊なセットアップの場合に DatabaseError を引き起こす可能性があります。 RETURNING INTO 句は、データベース設定の use_returning_into オプションを False に設定することで無効にできます:

"OPTIONS": {
    "use_returning_into": False,
}

この場合、Oracle バックエンドは個別の SELECT クエリを使って AutoField の値を取得します。

命名に関する問題

Oracle では、名前の長さに 30 文字という制限があります。これに対応するため、バックエンドはデータベース識別子を適当な長さに切り詰め、切り詰めた名前の最後の4文字を繰り返し使用可能なMD5ハッシュ値に置き換えます。さらに、バックエンドはデータベース識別子をすべて大文字にします。

このような変換を防ぐには(通常、レガシーデータベースを扱う場合や、他のユーザーのテーブルにアクセスする場合にのみ必要です)、db_table の値として引用符で囲まれた名前を使用します:

class LegacyModel(models.Model):
    class Meta:
        db_table = '"name_left_in_lowercase"'


class ForeignModel(models.Model):
    class Meta:
        db_table = '"OTHER_USER"."NAME_ONLY_SEEMS_OVER_30"'

引用符で囲まれた名前は、Django がサポートしている Oracle 以外の他のデータベースバックエンドでも使用できます。ただし、引用符には効果がありません。

migrate を実行すると、特定の Oracle キーワードがモデルフィールド名や db_column オプションの値として使われた場合に、 ORA-06552 エラーが発生することがあります。 Django はクエリで使われる全ての識別子を引用符で囲むことで、このような問題のほとんどを 防ぎますが、Oracle のデータ型がカラム名として使われると、このエラーが発生する可能性が あります。 特に、フィールド名として date, timestamp, number, float を使わないように注意してください。

NULL と空の文字列

Django は通常、 NULL よりも空文字列 ('') を好んで使いますが、 Oracle はどちらも同じように扱います。これを回避するために、 Oracle バックエンドは、空の文字列を値とするフィールドの明示的な null オプションを無視し、 null=True であるかのように DDL を生成します。データベースから取得する際、これらのフィールドの NULL 値は本当に空の文字列を意味すると仮定され、この仮定を反映するようにデータは静かに変換されます。

TextField の制限

Oracle バックエンドは TextFieldsNCLOB カラムとして保存します。Oracle は通常、このような LOB カラムの使用にいくつかの制限を設けています:

  • LOBカラムは主キーとして使用できません。
  • LOB 列はインデックスでは使用できません。
  • LOB 列は SELECT DISTINCT リストでは使用できません。つまり、Oracle に対して TextField 列を含むモデルで QuerySet.distinct メソッドを使用すると、ORA-00932 エラーが発生します。回避策として、 QuerySet.defer メソッドと distinct() を併用して、 TextField 列が SELECT DISTINCT リストに含まれないようにしてください。

組み込みデータベース・バックエンドをサブクラス化する

Django には組み込みのデータベースバックエンドが付属しています。既存のデータベースバックエンドをサブクラス化して、その動作や機能、設定を変更できます。

たとえば、データベースの機能を一つだけ変更する必要があるとします。まず、base モジュールを含む新しいディレクトリを作成します。たとえば:

mysite/
    ...
    mydbengine/
        __init__.py
        base.py

base.py モジュールには、 django.db.backends モジュールの既存のエンジンをサブクラス化した DatabaseWrapper というクラスを含める必要があります。以下は、PostgreSQL エンジンをサブクラス化して allows_group_by_selected_pks_on_model という機能を変更する例です:

mysite/mydbengine/base.py
from django.db.backends.postgresql import base, features


class DatabaseFeatures(features.DatabaseFeatures):
    def allows_group_by_selected_pks_on_model(self, model):
        return True


class DatabaseWrapper(base.DatabaseWrapper):
    features_class = DatabaseFeatures

最後に、settings.py ファイルで DATABASE-ENGINE を指定する必要があります:

DATABASES = {
    "default": {
        "ENGINE": "mydbengine",
        # ...
    },
}

現在のデータベースエンジンのリストは django/db/backends で見ることができます。

サードパーティのデータベース・バックエンドを使う

公式にサポートされているデータベースに加え、サードパーティが提供するバックエンドがあり、Django で他のデータベースを使うことができます:

これらの非公式のバックエンドがサポートする Django のバージョンと ORM の機能は、バックエンドによって大きく異なります。これらの非公式なバックエンドの特定の機能に関する質問や、サポートに関する質問は、各サードパーティプロジェクトが提供するサポートチャンネルに直接問い合わせてください。

Back to Top