数据库¶
Django 官方支持以下数据库:
还有一些第三方提供的 数据库后端。
Django 试图在所有数据库后端上支持尽可能多的功能。然而,并不是所有的数据库后端都是一样的,我们不得不在设计上决定支持哪些功能,以及我们可以安全地做出哪些假设。
该文件描述了一些可能与 Django 使用有关的功能。 它不能替代特定于服务器的文档或参考手册。
通用注意事项¶
持久连接¶
持久连接避免了在每个 HTTP 请求中重新建立与数据库的连接的开销。它们由 CONN_MAX_AGE
参数控制,该参数定义了连接的最大生命周期。它可以独立设置在每个数据库上。
默认值是 0
,保留了每次请求结束时关闭数据库连接的历史行为。要启用持久连接,可将 CONN_MAX_AGE
设置为正整数秒。对于无限制的持久连接,将其设置为 None
。
连接管理¶
当 Django 第一次进行数据库查询时,就会打开一个与数据库的连接。它保持这个连接的开放性,并在以后的请求中重复使用。一旦连接超过了 CONN_MAX_AGE
所定义的最大时长,或者当它不再可用时,Django 就会关闭这个连接。
详细来说,Django 每当需要连接数据库而又没有连接的时候,就会自动打开一个连接到数据库——或者是因为这是第一个连接,或者是因为之前的连接被关闭。
在每次请求开始时,如果连接已达到最大时长,Django 就会关闭连接。如果你的数据库在一段时间后终止了空闲的连接,你应该将 CONN_MAX_AGE
设置为一个较低的值,这样 Django 就不会尝试使用一个已经被数据库服务器终止的连接。(这个问题可能只影响到流量很低的网站。)
在每个请求结束时,如果连接已达到其最大寿命或处于不可恢复的错误状态,Django 将关闭连接。如果在处理请求时发生了任何数据库错误,Django 会检查连接是否仍然可用,如果不可用,将关闭连接。因此,数据库错误最多会影响每个应用程序工作线程的一个请求;如果连接变得无法使用,下一个请求将获得一个新的连接。
将 CONN_HEALTH_CHECKS
设置为 True
可以用于提高连接复用的健壮性,并防止在连接已被数据库服务器关闭且现在已准备好接受和提供新连接时发生错误,例如在数据库服务器重新启动后。健康检查只在每个请求中执行一次,只有在处理请求期间访问数据库时才会执行。
已添加了 CONN_HEALTH_CHECKS
设置。
附加说明¶
由于每个线程都维护自己的连接,所以你的数据库必须支持至少与你的工作线程一样多的同时连接。
有时,数据库不会被大多数视图访问,例如因为它是外部系统的数据库,或者由于缓存的原因。在这种情况下,你应该将 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
的支持可能会在将来的某个时候被弃用并移除。
已添加对 psycopg
3.1.8+ 的支持。
PostgreSQL 连接配置¶
详见 HOST
。
要使用 连接服务文件 中的服务名称和 密码文件 中的密码进行连接,你必须在数据库配置中的 :settings:`OPTIONS` 部分指定它们。
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"OPTIONS": {
"service": "my_service",
"passfile": ".my_pgpass",
},
}
}
[my_service]
host=localhost
user=USER
dbname=NAME
port=5432
localhost:5432:NAME:USER:PASSWORD
警告
出于测试目的不支持使用服务名称。这个 可能会在以后实现。
优化 PostgreSQL 的配置¶
Django 的数据库连接需要以下参数:
client_encoding
:'UTF8'
,default_transaction_isolation
:'read committed'
默认情况下,或在连接选项中设置的值(见下文)。
如果这些参数已经有了正确的值,Django 就不会为每个新的连接设置这些参数,这样可以稍微提高性能。你可以直接在 postgresql.conf
中设置它们,或者更方便地在每个数据库用户中用 ALTER ROLE 设置。
如果没有这个优化,Django 也能正常工作,但每个新的连接都会做一些额外的查询来设置这些参数。
隔离等级¶
和 PostgreSQL 本身一样,Django 默认为 READ COMMITTED
隔离级别 。如果你需要更高的隔离级别,比如 REPEATABLE READ
或者 SERIALIZABLE
,可以在数据库配置的 DATABASES
中的 OPTIONS
部分进行设置:
from django.db.backends.postgresql.psycopg_any import IsolationLevel
DATABASES = {
# ...
"OPTIONS": {
"isolation_level": IsolationLevel.SERIALIZABLE,
},
}
备注
在更高的隔离级别下,你的应用程序应该准备好处理序列化失败时引发的异常。这个选项是为进阶用途设计的。
已添加 IsolationLevel
。
角色¶
如果需要为数据库连接使用不同的角色,而不是用于建立连接的角色,请在 DATABASES
中的数据库配置的 OPTIONS
部分进行设置:
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
# ...
"OPTIONS": {
"assume_role": "my_application_role",
},
},
}
服务器端参数绑定¶
使用 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 通常会输出一条 CREATE INDEX
语句。 但是,如果字段的数据库类型是 varchar
或 text
(例如,CharField
、FileField
和 TextField
),那么 Django 将为该列创建一个额外的索引,使用适当的 PostgreSQL 操作类 。 这个额外的索引对于正确执行在 SQL 中使用 LIKE
操作符的查询是必要的,就像 contains
和 startswith
查询类型一样。
添加扩展的迁移操作¶
如果你需要使用迁移来添加 PostgreSQL 扩展(如 hstore
、postgis
等),请使用 CreateExtension
操作。
服务器端游标¶
在使用 QuerySet.iterator()
时,Django 打开了一个 服务器端游标。默认情况下,PostgreSQL 假定游标查询结果的前 10% 将被获取。查询计划器在规划查询时花费更少的时间,开始更快地返回结果,但如果获取的结果超过 10%,性能可能会下降。PostgreSQL 对游标查询的行数假设是由 cursor_tuple_fraction 选项控制的。
事务池和服务器端游标¶
在事务池模式下使用连接池(如 PgBouncer )需要禁用该连接的服务器端游标。
服务器端游标是本地连接,当 AUTOCOMMIT
为 True
时,服务器端游标在事务结束时保持开放。后续事务可能会尝试从服务器端游标中获取更多的结果。在事务池模式下,不能保证后续事务会使用同一个连接。如果使用了不同的连接,当事务引用服务器端游标时就会出现错误,因为服务器端游标只有在创建它们的连接中才能访问。
一种解决方案是在 DATABASES
中通过将 DISABLE_SERVER_SIDE_CURSORS
设置为 True
来禁用连接的服务器端游标。
为了从事务池模式下的服务器端游标中获益,你可以设置 与数据库的另一个连接,以便执行使用服务器端游标的查询。这个连接需要直接连接到数据库或者连接到会话池模式下的连接池。
另一种选择是将每个使用服务器端游标的 QuerySet
包裹在一个 atomic()
块中,因为它在事务的持续时间内禁用 autocommit
。这样一来,服务器端游标将只在事务持续时间内有效。
手动指定自增主键的值。¶
Django 使用 PostgreSQL 的 identity 列来存储自增的主键。一个 identity 列会通过一个 sequence 来填充值,该 sequence 跟踪下一个可用的值。手动为自增字段分配一个值不会更新字段的 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 语句来做到这一点。
在较旧的版本中,PostgreSQL 使用了 SERIAL
数据类型来代替 identity 列。
测试数据库模板¶
你可以使用 TEST['TEMPLATE']
配置来指定一个 template (例如 'template0'
)来创建测试数据库。
使用非持久设置加快测试执行速度。¶
你可以通过 将 PostgreSQL 配置为非持久 来加快测试执行时间。
警告
这是很危险的:它将使你的数据库在服务器崩溃或断电的情况下更容易发生数据丢失或损坏。只有在开发机器上使用,在那里你可以轻松的恢复集群中所有数据库的全部内容。
MySQL 注意事项¶
版本支持¶
Django 支持 MySQL 8 及更高版本。
Django 的 inspectdb
功能使用 information_schema
数据库,其中包含所有数据库架构的详细数据。
Django 希望数据库支持 Unicode(UTF-8 编码),并将执行事务和引用完整性的任务交给它。需要注意的是,MySQL 在使用 MyISAM 存储引擎时,后两项其实并没有强制执行,参见下一节。
存储引擎¶
MySQL 有几个 存储引擎 。你可以在服务器配置中更改默认的存储引擎。
MySQL 的默认存储引擎是 InnoDB 。这个引擎是完全事务性的,并且支持外键引用。这是推荐的选择。然而,InnoDB 自动增量计数器在 MySQL 重启时丢失,因为它不记得 AUTO_INCREMENT
值,而是将其重新创建为 "max(id)+1"。这可能会导致无意中重用 AutoField
值。
MyISAM 的主要缺点是不支持事务,也不执行外键约束。
MySQL 数据库 API 驱动程序¶
MySQL 有几个驱动程序实现了 PEP 249 中描述的 Python 数据库 API。
- mysqlclient 是一个本地的数据库驱动程序。它是 推荐的选择。
- MySQL Connector/Python 是一个来自 Oracle 的纯 Python 驱动,不需要 MySQL 客户端库或标准库之外的任何 Python 模块。
这些驱动程序都是线程安全的,并提供连接池。
除了数据库 API 驱动之外,Django 还需要一个适配器来从其 ORM 中访问数据库驱动。Django 为 mysqlclient 提供了一个适配器,而 MySQL Connector/Python 则包含了 自己的 。
mysqlclient¶
Django 需要 mysqlclient 的版本为 1.4.3 或更高。
时区定义¶
如果你打算使用 Django 的 时区支持,使用 mysql_tzinfo_to_sql 将时区表加载到 MySQL 数据库中。这只需要为你的 MySQL 服务器做一次,而不是每个数据库。
创建你的数据库¶
你可以使用命令行工具并执行以下 SQL 语句来 创建数据库:
CREATE DATABASE <dbname> CHARACTER SET utf8;
这确保了所有的表和列默认使用 UTF-8。
字符序配置¶
一列的字符序配置控制了数据排序的顺序,以及哪些字符串被比较为相等。你可以指定 db_collation
参数来为 CharField
和 TextField
设置列的字符序名称。
字符序也可以在整个数据库层面和每张表上设置。这在 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
。
连接数据库¶
参考 配置文档。
连接配置应按此顺序使用
换句话说,如果你在 OPTIONS
中设置数据库的名称,这将优先于 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 连接选项 可能会有用,比如 ssl
、init_command
和 sql_mode
。
设置 sql_mode
¶
sql_mode
选项的默认值包含 STRICT_TRANS_TABLES
。该选项在插入时将警告升级为错误,因此 Django 强烈建议在 MySQL 中激活 strict mode,以防止数据丢失(可以使用 STRICT_TRANS_TABLES
或 STRICT_ALL_TABLES
)。
如果你需要自定义 SQL 模式,你可以像其他 MySQL 选项一样设置 sql_mode
变量:可以在配置文件中设置,也可以在你的数据库配置的 OPTIONS
部分的 DATABASES
中使用 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"
配置。
隔离等级¶
当运行并发负载时,来自不同会话的数据库事务(例如,处理不同请求的独立线程)可能会相互交互。这些交互受到每个会话的 事务隔离级别 的影响。你可以在数据库配置的 DATABASES
中的 OPTIONS
部分设置连接的隔离级别,并在其中设置一个 'isolation_level'
条目。这个条目的有效值是四个标准隔离级别:
'read uncommitted'
'read committed'
'repeatable read'
'serializable'
或 None
来使用服务器配置的隔离级别。然而,Django 的最佳工作方式和默认值是 read committed,而不是 MySQL 的默认 repeatable 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 语句时,可能会导致表名的大小写被改变。如果可能的话,建议你使用小写的表名,以避免这种行为可能产生的任何问题。Django 在从模型中自动生成表名时使用小写表名,所以这主要是考虑到如果你是通过 db_table
参数来覆盖表名。
保存点¶
Django ORM 和 MySQL(使用 InnoDB 存储引擎 时)都支持数据库 保存点。
如果你使用 MyISAM 存储引擎,请注意,如果你试图使用 事务 API 的保存点相关方法,你将收到数据库生成的错误。原因是检测 MySQL 数据库/表的存储引擎是一个昂贵的操作,所以决定不值得在没有操作的情况下动态转换这些方法,基于这种检测的结果。
特定字段的注意事项¶
字符字段¶
如果你对字段使用了 unique=True
,那么任何以 VARCHAR
列类型存储的字段可能会被 max_length
限制为255个字符。这将影响 CharField
、SlugField
。更多细节请看 MySQL 文档 。
TextField
限制¶
MySQL 只能对 BLOB
或 TEXT
列的前 N 个字符进行索引。由于 TextField
没有定义的长度,所以不能将其标记为 unique=True
。MySQL 会报告:"BLOB/TEXT column '<db_column>' used in key specification without a key length"。
支持时间和 DateTime 字段的小数秒。¶
MySQL 可以存储小数秒,只要列的定义包括一个小数指示(例如 DATETIME(6)
)。
Django 不会在数据库服务器支持的情况下升级现有列以包含小数秒。如果要在现有数据库上启用它们,你需要手动在目标数据库上更新列,通过执行类似以下命令:
ALTER TABLE `your_table` MODIFY `your_datetime_column` DATETIME(6)
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 (≥8.0.1) |
NOWAIT |
X | X (≥8.0.1) |
OF |
X (≥8.0.1) | |
NO KEY |
当在 MySQL 上使用 select_for_update()
时,确保你至少针对唯一约束中包含的一组字段或仅针对索引覆盖的字段过滤查询集。否则,在事务过程中,将对整个表获得一个独占的写锁。
自动排版会造成意想不到的结果¶
当对字符串类型执行查询,但有一个整数值时,MySQL 会在执行比较之前将表中所有值的类型强制为整数。如果你的表中包含值 'abc'
、'def'
,而你查询 WHERE mycolumn=0
,两行都会匹配。同理,WHERE mycolumn=1
将匹配值 'abc1'
。因此,Django 中包含的字符串类型字段在查询中使用之前,总是会先将值转换为字符串。
如果你实现的自定义模型字段直接继承自 Field
,覆盖 get_prep_value()
或者使用 RawSQL
、extra()
或者 raw()
,你应该确保你执行了适当的类型化。
SQLite 注意事项¶
Django 支持 SQLite 3.21.0 及更高版本。
SQLite 为以只读为主或需要较小安装空间的应用程序提供了一个很好的开发选择。不过,与所有数据库服务器一样,SQLite 也有一些特定的差异,你应该注意。
子串匹配和大小写敏感性¶
对于所有的 SQLite 版本,当尝试匹配某些类型的字符串时,会出现一些略微反直觉的行为。 当在查询中使用 iexact
或 contains
过滤器时,这些行为会被触发。这种行为分为两种情况:
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.
一些可能的变通方法在 sqlite.org 有记载 ,但是 Django 的默认 SQLite 后端并没有利用这些方法,因为将它们整合起来是相当困难的。因此,Django 暴露了默认的 SQLite 行为,当你进行大小写不敏感或子串过滤时,你应该注意这一点。
小数处理¶
SQLite 没有真正的小数内部类型。小数值在内部转换为 REAL
数据类型(8 字节的 IEEE 浮点数),正如 SQLite 数据类型文档 中所解释的那样,所以它们不支持正确舍入的小数浮点运算。
“Database is locked”错误¶
SQLite 是一个轻量级数据库,因此不能支持高并发。OperationalError: database is locked
错误表明你的应用程序遇到的并发量超过了 sqlite
在默认配置下所能处理的范围。这个错误意味着一个线程或进程在数据库连接上有一个独占锁,另一个线程超时等待锁被释放。
Python 的 SQLite 包装器有一个默认的超时值,这个超时值决定了第二个线程在超时并引发 OperationalError: database is locked
错误之前允许在锁上等待多长时间。
如果你遇到这种错误,你可以通过以下方式解决:
切换到另一个数据库后端。到了一定程度,SQLite 对于现实世界的应用来说就会变得过于“精简”,这类并发错误表明你已经达到了这个程度。
重写你的代码以减少并发性,并确保数据库事务是短暂的。
通过设置
timeout
数据库选项增加默认超时值:"OPTIONS": { # ... "timeout": 20, # ... }
这将使 SQLite 在抛出 “database is locked” 的错误之前等待更长的时间;它不会真正起到任何解决这些问题的作用。
不支持 QuerySet.select_for_update()
¶
SQLite 不支持 SELECT ... FOR UPDATE
语法。调用它不会有任何效果。
使用 QuerySet.iterator()
时的隔离¶
当使用 QuerySet.iterator()
在迭代表时修改表,有一些特殊的注意事项在 SQLite 的隔离 中描述。如果一条记录在循环中被添加、更改或删除,那么这条记录可能会出现,也可能不会出现,或者可能会在后续从迭代器中获取的结果中出现两次。你的代码必须处理这个问题。
Oracle 注意事项¶
Django 支持 Oracle 数据库服务器 19c 及以上版本。需要 7.0 或更高版本的 cx_Oracle Python 驱动。
为了使 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 OPTION
和 CREATE 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": "",
}
}
在这种情况下,你应该把 HOST
和 PORT
都留空。但是,如果你不使用 tnsnames.ora
文件或类似的命名方法,而想使用 SID(本例中的“xe”)进行连接,则应像这样填写 HOST
和 PORT
。
DATABASES = {
"default": {
"ENGINE": "django.db.backends.oracle",
"NAME": "xe",
"USER": "a_user",
"PASSWORD": "a_password",
"HOST": "dbprod01ned.mycompany.com",
"PORT": "1540",
}
}
你应该同时提供 HOST
和 PORT
,或者把这两个字符串都留为空。Django 会根据这个选择使用不同的连接描述符。
完整的 DSN 和 Easy Connect¶
如果 HOST
和 PORT
都是空的,可以在 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(例如 Apache 在任何现代操作系统中使用默认的 MPM 模块),那么你 必须 将 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 数据类型作为列名时,仍然会出现这个错误。 特别是要注意避免使用 date
、timestamp
、number
或 float
作为字段名。
NULL 和空字符串¶
Django 通常更喜欢使用空字符串(''
)而不是 NULL
,但 Oracle 对两者的处理是一样的。为了解决这个问题,Oracle 后端会忽略字段上显式的 null
选项,并将 null=True
生成 DDL。当从数据库中获取数据时,假定这些字段中的 null
值确实意味着空字符串,数据被默默地转换以反映这一假设。
TextField
限制¶
Oracle 后台将 TextFields
存储为 NCLOB
列。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
模块必须包含一个名为 DatabaseWrapper
的类,它从 django.db.backends
模块中子类化了一个现有的引擎。下面是一个子类化 PostgreSQL 引擎的例子,用来改变一个特征类 allows_group_by_selected_pks_on_model
。
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 功能有很大的不同。关于这些非官方后端的具体功能的查询,以及任何支持的查询,都应该通过每个第三方项目提供的支持渠道进行。