数据库

Django 正式支持以下数据库

还有一些由第三方提供的数据库后端

Django 试图在所有数据库后端支持尽可能多的功能。但是,并非所有数据库后端都相同,我们不得不做出设计决策,决定支持哪些功能以及可以安全地做出哪些假设。

此文件描述了一些可能与 Django 使用相关的功能。它并非旨在替代服务器特定的文档或参考手册。

一般说明

持久连接

持久连接避免了在每次 HTTP 请求中重新建立与数据库连接的开销。它们由CONN_MAX_AGE参数控制,该参数定义连接的最长生命周期。可以为每个数据库单独设置。

默认值为0,保留了在每个请求结束时关闭数据库连接的历史行为。要启用持久连接,请将CONN_MAX_AGE设置为一个正整数秒。对于无限持久连接,将其设置为None

连接管理

Django 在第一次进行数据库查询时打开与数据库的连接。它保持此连接打开并在后续请求中重用它。Django 一旦超过CONN_MAX_AGE定义的最大年龄或不再可用时关闭连接。

详细地说,Django 只要需要连接并且还没有连接时就会自动打开与数据库的连接——无论是第一次连接还是因为之前的连接已关闭。

在每个请求开始时,如果连接已达到其最大年龄,Django 会关闭连接。如果您的数据库在一段时间后终止空闲连接,则应将CONN_MAX_AGE设置为较低的值,以便 Django 不尝试使用已被数据库服务器终止的连接。(此问题可能只影响流量非常低的网站。)

在每个请求结束时,如果连接已达到其最大年龄或处于不可恢复的错误状态,Django 会关闭连接。如果在处理请求期间发生任何数据库错误,Django 会检查连接是否仍然有效,如果无效则关闭它。因此,数据库错误最多会影响每个应用程序工作线程的一个请求;如果连接变得不可用,则下一个请求会获得一个新的连接。

CONN_HEALTH_CHECKS设置为True可用于提高连接重用的健壮性,并在数据库服务器已关闭连接(现在已准备好接受和提供新连接,例如在数据库服务器重启后)时防止错误。健康检查仅在每个请求中执行一次,并且仅当在处理请求期间访问数据库时执行。

注意事项

由于每个线程都维护自己的连接,因此您的数据库必须支持至少与您拥有的工作线程一样多的同时连接。

有时大多数视图不会访问数据库,例如因为它是一个外部系统的数据库,或者由于缓存。在这种情况下,您应该将CONN_MAX_AGE设置为较低的值甚至0,因为维护不太可能重用的连接没有意义。这将有助于将对此数据库的同时连接数保持在较小范围内。

开发服务器为其处理的每个请求创建一个新线程,从而抵消了持久连接的效果。在开发期间不要启用它们。

当 Django 建立与数据库的连接时,它会根据正在使用的后端设置适当的参数。如果启用持久连接,则此设置不再在每个请求中重复。如果您修改了诸如连接的隔离级别或时区之类的参数,则应在每个请求结束时恢复 Django 的默认值,在每个请求开始时强制执行适当的值,或禁用持久连接。

如果在 Django 的请求-响应周期之外的长运行进程中创建连接,则该连接将保持打开状态,直到显式关闭或超时发生。您可以使用django.db.close_old_connections()关闭所有旧的或不可用的连接。

编码

Django 假设所有数据库都使用 UTF-8 编码。使用其他编码可能会导致意外行为,例如您的数据库对在 Django 中有效的数据发出“值过长”错误。有关如何正确设置数据库的信息,请参阅下面的数据库特定说明。

PostgreSQL 说明

Django 支持 PostgreSQL 13 及更高版本。需要 psycopg 3.1.8+ 或 psycopg2 2.8.4+,但建议使用最新的 psycopg 3.1.8+。

注意

psycopg2的支持可能会在将来的某个时间弃用并删除。

PostgreSQL 连接设置

有关详细信息,请参阅HOST

要使用连接服务文件中的服务名称和密码文件中的密码进行连接,您必须在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的内容作为关键字参数传递给连接构造函数,从而允许更高级地控制驱动程序的行为。PostgreSQL 文档详细描述了所有可用的参数

警告

不支持出于测试目的使用服务名称。这可能会在以后实现

优化 PostgreSQL 的配置

Django 的数据库连接需要以下参数

  • client_encoding'UTF8'

  • default_transaction_isolation:默认情况下为'read committed',或在连接选项中设置的值(见下文),

  • 时区:

如果这些参数已经具有正确的数值,Django 不会为每个新的连接设置它们,这会略微提高性能。您可以在 postgresql.conf 中直接配置它们,或者更方便地使用 ALTER ROLE 为每个数据库用户配置。

没有这种优化,Django 也可以正常工作,但每个新连接都会执行一些额外的查询来设置这些参数。

隔离级别

与 PostgreSQL 本身一样,Django 默认使用 READ COMMITTED 隔离级别。如果您需要更高的隔离级别,例如 REPEATABLE READSERIALIZABLE,请在数据库配置的 OPTIONS 部分(位于 DATABASES 中)进行设置。

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

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

注意

在更高的隔离级别下,您的应用程序应准备好处理序列化失败时引发的异常。此选项适用于高级用途。

角色

如果您需要使用与建立连接的角色不同的角色进行数据库连接,请在数据库配置的 OPTIONS 部分(位于 DATABASES 中)进行设置。

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

连接池

Django 5.1 中新增。

要使用 psycopg 的连接池,您可以将数据库配置的 OPTIONS 部分中的 "pool" 设置为一个字典,传递给 ConnectionPool,或者设置为 True 以使用 ConnectionPool 的默认值。

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

此选项需要安装 psycopg[pool]psycopg-pool,并且在使用 psycopg2 时会被忽略。

服务器端参数绑定

使用 psycopg 3.1.8 及更高版本时,Django 默认使用 客户端绑定游标。如果您想使用 服务器端绑定,请在数据库配置的 OPTIONS 部分(位于 DATABASES 中)进行设置。

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

此选项在使用 psycopg2 时会被忽略。

varchartext 列的索引

当在模型字段上指定 db_index=True 时,Django 通常会输出单个 CREATE INDEX 语句。但是,如果字段的数据库类型为 varchartext(例如,由 CharFieldFileFieldTextField 使用),则 Django 将创建一个额外的索引,该索引使用适合该列的 PostgreSQL 运算符类。额外的索引对于正确执行在其 SQL 中使用 LIKE 运算符的查找(如使用 containsstartswith 查找类型)是必需的。

添加扩展的迁移操作

如果您需要使用迁移添加 PostgreSQL 扩展(如 hstorepostgis 等),请使用 CreateExtension 操作。

服务器端游标

使用 QuerySet.iterator() 时,Django 会打开一个 服务器端游标。默认情况下,PostgreSQL 假设只会获取游标查询结果的前 10%。查询规划器花费更少的时间来计划查询并更快地开始返回结果,但如果检索的结果超过 10%,则可能会降低性能。PostgreSQL 对游标查询检索的行数的假设由 cursor_tuple_fraction 选项控制。

事务池和服务器端游标

在事务池模式(例如 PgBouncer)下使用连接池管理器需要禁用该连接的服务器端游标。

AUTOCOMMITTrue 时,服务器端游标是特定于连接的,并且在事务结束时保持打开状态。后续事务可能会尝试从服务器端游标中获取更多结果。在事务池模式下,无法保证后续事务会使用相同的连接。如果使用了不同的连接,则当事务引用服务器端游标时会引发错误,因为服务器端游标只能在创建它们的连接中访问。

一种解决方案是在 DATABASES 中通过将 DISABLE_SERVER_SIDE_CURSORS 设置为 True 来禁用连接的服务器端游标。

为了在事务池模式下利用服务器端游标,您可以设置 另一个到数据库的连接 以执行使用服务器端游标的查询。此连接需要直接连接到数据库或连接到会话池模式下的连接池管理器。

另一种选择是将每个使用服务器端游标的 QuerySet 包裹在 atomic() 块中,因为它会在事务持续时间内禁用 autocommit。这样,服务器端游标只会在事务持续时间内存在。

手动指定自增主键的值

Django 使用 PostgreSQL 的标识列来存储自增主键。标识列使用 序列 填充值,该序列跟踪下一个可用值。手动为自增字段分配值不会更新字段的序列,这可能会导致以后发生冲突。例如

>>> 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'] 设置指定一个 模板(例如 'template0'),从中创建测试数据库。

使用非持久性设置加快测试执行速度

您可以通过配置PostgreSQL为非持久化来加快测试执行速度。

警告

这很危险:如果服务器崩溃或电源故障,它会使您的数据库更容易丢失或损坏数据。仅在开发机器上使用此功能,在开发机器上,您可以轻松恢复集群中所有数据库的全部内容。

MariaDB 注释

Django 支持 MariaDB 10.5 及更高版本。

要使用 MariaDB,请使用 MySQL 后端,这两个后端是共享的。有关更多详细信息,请参阅MySQL 注释

MySQL 注释

版本支持

Django 支持 MySQL 8.0.11 及更高版本。

Django 的inspectdb功能使用information_schema数据库,其中包含所有数据库模式的详细数据。

Django 期望数据库支持 Unicode(UTF-8 编码),并委托它执行事务和引用完整性的任务。需要注意的是,当使用 MyISAM 存储引擎时,MySQL 实际上并没有强制执行后两项,请参见下一节。

存储引擎

MySQL 有几个存储引擎。您可以在服务器配置中更改默认存储引擎。

MySQL 的默认存储引擎是InnoDB。此引擎是完全事务性的,并支持外键引用。这是推荐的选择。但是,InnoDB 自增计数器在 MySQL 重启时会丢失,因为它不记住AUTO_INCREMENT值,而是将其重新创建为“max(id)+1”。这可能导致意外重复使用AutoField值。

MyISAM的主要缺点是它不支持事务或强制执行外键约束。

MySQL DB API 驱动程序

MySQL 有几个驱动程序实现了 Python 数据库 API,如PEP 249中所述

  • mysqlclient 是一个原生驱动程序。这是推荐的选择

  • MySQL Connector/Python 是 Oracle 的一个纯 Python 驱动程序,不需要 MySQL 客户端库或标准库之外的任何 Python 模块。

这些驱动程序是线程安全的,并提供连接池。

除了 DB API 驱动程序外,Django 还需要一个适配器来从其 ORM 访问数据库驱动程序。Django 为 mysqlclient 提供了一个适配器,而 MySQL Connector/Python 包含它自己的适配器

mysqlclient

Django 需要 mysqlclient 1.4.3 或更高版本。

MySQL Connector/Python

MySQL Connector/Python 可从下载页面获取。Django 适配器在 1.1.X 及更高版本中可用。它可能不支持 Django 的最新版本。

时区定义

如果您计划使用 Django 的时区支持,请使用mysql_tzinfo_to_sql将时区表加载到 MySQL 数据库中。这只需要对您的 MySQL 服务器执行一次,而不是每个数据库都执行。

创建数据库

您可以使用命令行工具和此 SQL创建数据库

CREATE DATABASE <dbname> CHARACTER SET utf8;

这将确保所有表和列默认使用 UTF-8。

排序规则设置

列的排序规则设置控制数据排序的顺序以及哪些字符串比较相等。您可以指定db_collation参数来设置CharFieldTextField的列的排序规则名称。

排序规则也可以在数据库级别和每个表级别设置。这在 MySQL 文档中有详细记录。在这种情况下,您必须通过直接操作数据库设置或表来设置排序规则。Django 没有提供更改它们的 API。

默认情况下,使用 UTF-8 数据库时,MySQL 将使用utf8_general_ci排序规则。这会导致所有字符串相等性比较都在不区分大小写的方式下进行。也就是说,"Fred""freD"在数据库级别被认为是相等的。如果某个字段具有唯一约束,则尝试将"aa""AA"都插入同一列将是非法的,因为它们使用默认排序规则比较时相等(因此是非唯一的)。如果您希望在特定列或表上进行区分大小写的比较,请将该列或表更改为使用utf8_bin排序规则。

请注意,根据MySQL Unicode 字符集utf8_general_ci排序规则的比较速度更快,但准确性略低于utf8_unicode_ci的比较。如果这对您的应用程序是可以接受的,则应使用utf8_general_ci,因为它速度更快。如果这是不可接受的(例如,如果您需要德语字典顺序),则使用utf8_unicode_ci,因为它更准确。

警告

模型表单集以区分大小写的方式验证唯一字段。因此,当使用不区分大小写的排序规则时,具有仅大小写不同的唯一字段值的表单集将通过验证,但在调用save()时,将引发IntegrityError

连接到数据库

请参阅设置文档

连接设置按以下顺序使用

  1. OPTIONS.

  2. NAMEUSERPASSWORDHOSTPORT

  3. MySQL 选项文件。

换句话说,如果您在OPTIONS中设置了数据库的名称,这将优先于NAME,而NAME将覆盖MySQL 选项文件中的任何内容。

这是一个使用 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

其他几个MySQLdb 连接选项可能很有用,例如sslinit_commandsql_mode

设置sql_mode

sql_mode选项的默认值包含STRICT_TRANS_TABLES。当数据在插入时被截断时,该选项会将警告升级为错误,因此 Django 强烈建议为 MySQL 激活严格模式以防止数据丢失(STRICT_TRANS_TABLESSTRICT_ALL_TABLES)。

如果您需要自定义 SQL 模式,您可以像其他 MySQL 选项一样设置sql_mode变量:在配置文件中或使用条目'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"在数据库配置的OPTIONS部分中DATABASES

隔离级别

在运行并发加载时,来自不同会话(例如,处理不同请求的单独线程)的数据库事务可能会相互交互。这些交互受每个会话的事务隔离级别的影响。您可以使用数据库配置的OPTIONS部分中的'isolation_level'条目来设置连接的隔离级别DATABASES。此条目的有效值为四个标准隔离级别

  • 'read uncommitted'

  • 'read committed'

  • 'repeatable read'

  • 'serializable'

None以使用服务器配置的隔离级别。但是,Django 最适合使用并默认使用读已提交,而不是 MySQL 的默认值可重复读。使用可重复读可能会导致数据丢失。特别是,您可能会看到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 语句时表名的案例发生更改。建议您尽可能使用小写表名,以避免由此行为引起的任何问题。Django 在根据模型自动生成表名时使用小写表名,因此这主要是在您通过 db_table 参数覆盖表名时需要考虑的问题。

保存点

Django ORM 和 MySQL(使用 InnoDB 存储引擎)都支持数据库 保存点

如果您使用 MyISAM 存储引擎,请注意,如果您尝试使用 事务 API 的与保存点相关的 方法,您将收到数据库生成的错误。原因是检测 MySQL 数据库/表的存储引擎是一个代价高昂的操作,因此决定不值得根据此类检测的结果动态地将这些方法转换为无操作。

特定字段的说明

字符字段

如果使用 unique=True 作为字段,则任何以 VARCHAR 列类型存储的字段的 max_length 可能会限制为 255 个字符。这会影响 CharFieldSlugField。有关更多详细信息,请参阅 MySQL 文档

TextField 限制

MySQL 只能索引 BLOBTEXT 列的前 N 个字符。由于 TextField 没有定义的长度,因此您不能将其标记为 unique=True。MySQL 将报告:“密钥规范中使用了 BLOB/TEXT 列‘<db_column>’,但没有密钥长度”。

时间和日期时间字段的小数秒支持

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.31.0 及更高版本。

SQLite 为主要只读或需要较小安装空间的应用程序提供了一个极好的开发替代方案。但是,与所有数据库服务器一样,也有一些特定于 SQLite 的差异,您应该了解这些差异。

子字符串匹配和大小写敏感性

对于所有 SQLite 版本,在尝试匹配某些类型的字符串时,都存在一些略微违反直觉的行为。当在 Querysets 中使用 iexactcontains 过滤器时,就会触发这些行为。行为分为两种情况

1. 对于子字符串匹配,所有匹配都是不区分大小写的。也就是说,诸如 filter(name__contains="aa") 之类的过滤器将匹配名称为 "Aabb" 的名称。

2. 对于包含 ASCII 范围之外的字符的字符串,即使将不区分大小写选项传递到查询中,所有精确字符串匹配也将区分大小写。因此,在这些情况下,iexact 过滤器的行为与 exact 过滤器完全相同。

一些可能的解决方法在 sqlite.org 上有记录,但 Django 中的默认 SQLite 后端没有使用它们,因为将它们整合进来相当难以稳健地完成。因此,Django 公开了默认的 SQLite 行为,在进行不区分大小写或子字符串过滤时,您应该注意这一点。

十进制处理

SQLite 没有真正的十进制内部类型。十进制值在内部转换为 REAL 数据类型(8 字节 IEEE 浮点数),如 SQLite 数据类型文档 中所述,因此它们不支持正确舍入的十进制浮点运算。

“数据库已锁定”错误

SQLite 旨在成为一个轻量级数据库,因此无法支持高水平的并发。 OperationalError: database is locked 错误表示您的应用程序正在经历比 sqlite 在默认配置下所能处理的更多的并发。此错误意味着一个线程或进程对数据库连接具有独占锁,而另一个线程在等待释放锁时超时。

Python 的 SQLite 包装器具有一个默认超时值,该值确定第二个线程在超时并引发 OperationalError: database is locked 错误之前允许等待锁的时间。

如果您遇到此错误,可以通过以下方法解决:

  • 切换到另一个数据库后端。在某个时刻,SQLite 对于现实世界的应用程序来说变得过于“轻量级”,而这些并发错误表明您已达到该点。

  • 重写您的代码以减少并发并确保数据库事务是短暂的。

  • 通过设置 timeout 数据库选项来增加默认超时值

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

    这将使 SQLite 在抛出“数据库已锁定”错误之前等待更长时间;它不会真正解决这些错误。

事务行为

Django 5.1 中新增。

SQLite 支持三种事务模式:DEFERREDIMMEDIATEEXCLUSIVE

默认为 DEFERRED。如果您需要使用其他模式,请在数据库配置的 OPTIONS 部分(在 DATABASES 中)中设置它,例如

"OPTIONS": {
    # ...
    "transaction_mode": "IMMEDIATE",
    # ...
}

为了确保您的事务在引发“数据库已锁定”错误之前等待 timeout,请将事务模式更改为 IMMEDIATE

为了获得 IMMEDIATEEXCLUSIVE 的最佳性能,事务应尽可能短。对于所有视图而言,这可能难以保证,因此在这种情况下不建议使用 ATOMIC_REQUESTS

有关更多信息,请参阅 SQLite 中的事务

QuerySet.select_for_update() 不受支持

SQLite 不支持 SELECT ... FOR UPDATE 语法。调用它将不起作用。

使用 QuerySet.iterator() 时的数据隔离

在使用 QuerySet.iterator() 迭代表时修改表时,需要考虑 SQLite 中的数据隔离 中描述的特殊注意事项。如果在循环中添加、更改或删除了行,则该行可能会或可能不会出现在后续从迭代器中获取的结果中,或者可能会出现两次。您的代码必须处理此问题。

在 SQLite 上启用 JSON1 扩展

要在 SQLite 上使用 JSONField,您需要在 Python 的 sqlite3 库上启用 JSON1 扩展。如果您的安装中未启用该扩展,则会引发系统错误(fields.E180)。

要启用 JSON1 扩展,您可以按照 wiki 页面 上的说明进行操作。

注意

SQLite 3.38+ 默认启用 JSON1 扩展。

设置 pragma 选项

Django 5.1 中新增。

Pragma 选项 可以通过在数据库配置的 OPTIONS 部分(在 DATABASES 中)中使用 init_command 在连接时设置。以下示例显示了如何启用同步写入的额外持久性和更改 cache_size

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        # ...
        "OPTIONS": {
            "init_command": "PRAGMA synchronous=3; PRAGMA cache_size=2000;",
        },
    }
}

Oracle 说明

Django 支持 Oracle 数据库服务器 19c 及更高版本。需要 oracledb Python 驱动程序 1.3.2 或更高版本。

自版本 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 TABLECREATE SEQUENCECREATE PROCEDURECREATE 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_LOBSYS.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 将根据此选择使用不同的连接描述符。

完整 DSN 和 Easy Connect

如果 HOSTPORT 都为空,则可以在 NAME 中使用完整 DSN 或 Easy Connect 字符串。例如,在使用 RAC 或可插拔数据库且没有 tnsnames.ora 时,需要使用此格式。

Easy Connect 字符串示例

"NAME": "localhost:1521/orclpdb1"

完整 DSN 字符串示例

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

线程选项

如果您计划在多线程环境中运行 Django(例如,在任何现代操作系统上使用默认 MPM 模块的 Apache),则**必须**将 Oracle 数据库配置的 threaded 选项设置为 True

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

否则可能会导致崩溃和其他异常行为。

INSERT … RETURNING INTO

默认情况下,Oracle 后端使用 RETURNING INTO 子句来有效地检索插入新行时 AutoField 的值。在某些不寻常的设置中,此行为可能会导致 DatabaseError,例如,当插入到远程表或带有 INSTEAD OF 触发器的视图中时。可以通过将数据库配置的 use_returning_into 选项设置为 False 来禁用 RETURNING INTO 子句。

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

在这种情况下,Oracle 后端将使用单独的 SELECT 查询来检索 AutoField 值。

命名问题

Oracle 对名称长度有限制,最多为 30 个字符。为了适应这一点,后端会截断数据库标识符以使其符合限制,并将截断名称的最后四个字符替换为可重复的 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 数据类型用作列名时,此错误仍可能发生。特别是,注意避免使用 datetimestampnumberfloat 作为字段名称。

NULL 和空字符串

Django 通常更喜欢使用空字符串('')而不是 NULL,但 Oracle 将两者视为相同。为了解决这个问题,Oracle 后端会忽略字段上显式的 null 选项(这些字段的可能值为空字符串),并生成 DDL,就像 null=True 一样。从数据库中获取数据时,假设这些字段中的 NULL 值实际上表示空字符串,并且数据会静默转换为反映此假设。

TextField 限制

Oracle 后端将 TextFields 存储为 NCLOB 列。Oracle 对此类 LOB 列的使用有一些限制。

  • LOB 列不能用作主键。

  • LOB 列不能用于索引。

  • LOB 列不能用于 SELECT DISTINCT 列表中。这意味着,尝试在包含 TextField 列的模型上使用 QuerySet.distinct 方法时,在针对 Oracle 运行时会导致 ORA-00932 错误。作为解决方法,请将 QuerySet.defer 方法与 distinct() 结合使用,以防止 TextField 列包含在 SELECT DISTINCT 列表中。

对内置数据库后端进行子类化

Django 附带内置的数据库后端。您可以对现有的数据库后端进行子类化,以修改其行为、功能或配置。

例如,假设您需要更改单个数据库功能。首先,您必须创建一个新目录,并在其中包含一个名为 base 的模块。例如

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

base.py 模块必须包含一个名为 DatabaseWrapper 的类,该类是 django.db.backends 模块中现有引擎的子类。以下是对 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 功能差异很大。有关这些非官方后端特定功能的查询以及任何支持查询,应发送到每个第三方项目提供的支持渠道。

返回顶部