数据库 — Django 文档
数据库
Django 官方支持以下数据库:
还有一些由第三方提供的数据库后端。
Django 尝试在所有数据库后端支持尽可能多的功能。 然而,并非所有的数据库后端都是一样的,我们必须就支持哪些功能以及我们可以安全做出哪些假设做出设计决策。
该文件描述了一些可能与 Django 使用相关的功能。 当然,它不能替代特定于服务器的文档或参考手册。
通用注意事项
持久连接
持久连接避免了在每个请求中重新建立到数据库的连接的开销。 它们由 :setting:`CONN_MAX_AGE` 参数控制,该参数定义了连接的最长生命周期。 可以为每个数据库独立设置。
默认值为0
,保留每次请求结束时关闭数据库连接的历史行为。 要启用持久连接,请将 :setting:`CONN_MAX_AGE` 设置为正秒数。 对于无限长连接,将其设置为 None
。
连接管理
Django 在第一次进行数据库查询时会打开一个到数据库的连接。 它保持此连接打开并在后续请求中重用它。 一旦连接超过 :setting:`CONN_MAX_AGE` 定义的最大年龄或当它不再可用时,Django 将关闭连接。
详细地说,Django 会在需要时自动打开一个到数据库的连接但还没有连接——因为这是第一个连接,或者因为前一个连接已关闭。
在每个请求开始时,如果连接已达到最大年龄,Django 会关闭连接。 如果您的数据库在一段时间后终止空闲连接,您应该将 :setting:`CONN_MAX_AGE` 设置为较低的值,以便 Django 不会尝试使用已被数据库服务器终止的连接。 (这个问题可能只会影响非常低流量的网站。)
在每个请求结束时,如果连接已达到最大年龄或处于不可恢复的错误状态,Django 将关闭连接。 如果在处理请求时发生任何数据库错误,Django 会检查连接是否仍然有效,如果无效则关闭它。 因此,数据库错误最多影响一个请求; 如果连接变得不可用,下一个请求将获得一个新的连接。
附加说明
由于每个线程都维护自己的连接,所以你的数据库必须支持至少与你的工作线程一样多的同时连接。
有时数据库不会被大多数视图访问,例如因为它是外部系统的数据库,或者由于缓存。 在这种情况下,您应该将 :setting:`CONN_MAX_AGE` 设置为一个较低的值,甚至是 0
,因为维护一个不太可能被重用的连接是没有意义的。 这将有助于保持与该数据库的同时连接数较少。
开发服务器为它处理的每个请求创建一个新线程,消除持久连接的影响。 不要在开发过程中启用它们。
当 Django 与数据库建立连接时,它会根据所使用的后端设置适当的参数。 如果启用持久连接,则不再在每个请求中重复此设置。 如果修改连接的隔离级别或时区等参数,则应该在每个请求结束时恢复 Django 的默认值,在每个请求开始时强制使用适当的值,或者禁用持久连接。
编码
Django 假设所有数据库都使用 UTF-8 编码。 对于在 Django 中有效的数据,使用其他编码可能会导致意外行为,例如数据库中的“值太长”错误。 有关如何正确设置数据库的信息,请参阅下面的数据库特定说明。
PostgreSQL 注意事项
Django 支持 PostgreSQL 9.4 及更高版本。 psycopg2 2.5.4 到 2.8.6 是必需的,但建议使用 2.8.6。
优化 PostgreSQL 的配置
Django 的数据库连接需要以下参数:
client_encoding
:'UTF8'
,default_transaction_isolation
:默认为'read committed'
,或连接选项中设置的值(见下文),timezone
:'UTC'
当 :setting:`USE_TZ` 为True
时,:setting:`TIME_ZONE` 的值。
如果这些参数已经有正确的值,Django 不会为每个新连接设置它们,这会稍微提高性能。 您可以直接在 postgresql.conf
中配置它们,或者使用 ALTER ROLE 更方便地为每个数据库用户配置它们。
如果没有这个优化,Django 也能正常工作,但每个新的连接都会做一些额外的查询来设置这些参数。
隔离等级
与 PostgreSQL 本身一样,Django 默认为 READ COMMITTED
隔离级别 。 如果您需要更高的隔离级别,例如 REPEATABLE READ
或 SERIALIZABLE
,请在 :setting 中数据库配置的 :setting:`OPTIONS` 部分进行设置:`数据库`:
import psycopg2.extensions
DATABASES = {
# ...
'OPTIONS': {
'isolation_level': psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE,
},
}
笔记
在更高的隔离级别下,您的应用程序应该准备好处理因序列化失败而引发的异常。 此选项专为高级用途而设计。
varchar 和 text 列的索引
在模型字段上指定 db_index=True
时,Django 通常输出单个 CREATE INDEX
语句。 但是,如果字段的数据库类型是 varchar
或 text
(例如,由 CharField
、FileField
和 TextField
使用) ),然后 Django 将创建一个额外的索引,该索引使用适当的 PostgreSQL 运算符类 作为该列。 额外的索引对于在其 SQL 中正确执行使用 LIKE
运算符的查找是必要的,就像使用 contains
和 startswith
查找类型一样。
服务器端游标
当使用 QuerySet.iterator() 时,Django 打开一个 服务器端游标 。 默认情况下,PostgreSQL 假设只有前 10% of 个游标查询的结果会被获取。 查询规划器花费更少的时间来规划查询并开始更快地返回结果,但是如果检索的结果超过 10% of,这可能会降低性能。 PostgreSQL 对游标查询检索行数的假设由 cursor_tuple_fraction 选项控制。
事务池和服务器端游标
在事务池模式下使用连接池(例如 pgBouncer) 需要禁用该连接的服务器端游标。
服务器端游标对于连接是本地的,并且在事务结束时保持打开状态 :设置:`自动提交 ` 是True
. 后续事务可能会尝试从服务器端游标获取更多结果。 在事务池模式下,不能保证后续事务将使用相同的连接。 如果使用不同的连接,则在事务引用服务器端游标时会引发错误,因为服务器端游标只能在创建它们的连接中访问。
一种解决方案是禁用服务器端游标的连接 :设置:`数据库` 通过设置 :设置:`DISABLE_SERVER_SIDE_CURSORS ` 到True
.
为了在事务池模式下受益于服务器端游标,您可以设置 另一个到数据库的连接 ,以便执行使用服务器端游标的查询。 此连接需要直接连接到数据库或连接到会话池模式下的连接池。
另一种选择是在 atomic() 块中使用服务器端游标包装每个 QuerySet
,因为它在事务期间禁用 autocommit
。 这样,服务器端游标只会在事务期间存活。
手动指定自增主键的值。
Django 使用 PostgreSQL 的 SERIAL 数据类型 来存储自动递增的主键。 SERIAL
列填充了来自 序列 的值,该序列跟踪下一个可用值。 手动为自动递增字段分配值不会更新字段的序列,这可能会在以后导致冲突。 例如:
>>> 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.
如果您需要指定此类值,请稍后重置序列以避免重复使用表中已有的值。 :djadmin:`sqlsequencereset` 管理命令生成 SQL 语句来执行此操作。
使用非持久设置加快测试执行速度。
您可以通过 将 PostgreSQL 配置为非持久 来加快测试执行时间。
警告
这是危险的:它会使您的数据库在服务器崩溃或断电的情况下更容易受到数据丢失或损坏的影响。 仅在开发机器上使用它,您可以轻松恢复集群中所有数据库的全部内容。
MySQL 注意事项
版本支持
Django 支持 MySQL 5.7 及以上版本。
Django 的 inspectdb
功能使用 information_schema
数据库,其中包含所有数据库模式的详细数据。
Django 期望数据库支持 Unicode(UTF-8 编码)并将执行事务和参照完整性的任务委托给它。 重要的是要注意,在使用 MyISAM 存储引擎时,后两个实际上不是由 MySQL 强制执行的,请参阅下一节。
存储引擎
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 模块。
这些驱动程序都是线程安全的,并提供连接池。
除了 DB API 驱动程序之外,Django 还需要一个适配器来从其 ORM 访问数据库驱动程序。 Django 为 mysqlclient 提供了一个适配器,而 MySQL Connector/Python 包括 自己的 。
MySQL Connector/Python
MySQL 连接器/Python 可从 下载页面 获得。 Django 适配器在 1.1.X 及更高版本中可用。 它可能不支持 Django 的最新版本。
时区定义
如果您打算使用 Django 的 时区支持 ,请使用 mysql_tzinfo_to_sql 将时区表加载到 MySQL 数据库中。 这需要为您的 MySQL 服务器完成一次,而不是每个数据库。
创建你的数据库
您可以使用命令行工具和以下 SQL 创建您的数据库 :
CREATE DATABASE <dbname> CHARACTER SET utf8;
这确保了所有的表和列默认使用 UTF-8。
字符序配置
列的排序规则设置控制数据的排序顺序以及比较哪些字符串相等。 它可以在数据库级别以及每个表和每个列上设置。 这是 在 MySQL 文档中彻底 记录的。 在所有情况下,您都是通过直接操作数据库表来设置排序规则的; Django 没有提供在模型定义上设置它的方法。
默认情况下,对于 UTF-8 数据库,MySQL 将使用 utf8_general_ci
排序规则。 这导致所有字符串相等性比较都以 不区分大小写 的方式进行。 也就是说,"Fred"
和 "freD"
在数据库级别被认为是相等的。 如果您对字段有唯一约束,尝试将 "aa"
和 "AA"
插入同一列是非法的,因为它们比较相等(因此,非唯一) 使用默认排序规则。 如果要对特定列或表进行区分大小写的比较,请更改列或表以使用 utf8_bin
归类。
请注意,根据 MySQL Unicode Character Sets,与 utf8_unicode_ci
的比较相比,utf8_general_ci
排序规则的比较更快,但准确性稍差。 如果这对于您的应用程序是可以接受的,您应该使用 utf8_general_ci
,因为它更快。 如果这是不可接受的(例如,如果您需要德语字典顺序),请使用 utf8_unicode_ci
,因为它更准确。
警告
模型表单集以区分大小写的方式验证唯一字段。 因此,当使用不区分大小写的排序规则时,具有仅因大小写而异的唯一字段值的表单集将通过验证,但在调用 save()
时,将引发 IntegrityError
。
连接数据库
请参阅 设置文档 。
连接配置应按此顺序使用
- :设置:`选项`。
- :setting:`NAME`, :setting:`USER`, :setting:`PASSWORD`, :setting:`HOST` ], :setting:`PORT`
- MySQL 选项文件。
换句话说,如果你在 :setting:`OPTIONS` 中设置数据库的名称,这将优先于 :setting:`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
从 MySQL 5.7 开始和全新安装的 MySQL 5.6,sql_mode
选项的默认值包含 STRICT_TRANS_TABLES
。 当插入时数据被截断时,该选项会将警告升级为错误,因此 Django 强烈建议为 MySQL 激活 严格模式 以防止数据丢失(STRICT_TRANS_TABLES
或 STRICT_ALL_TABLES
)。
如果您需要自定义 SQL 模式,您可以像其他 MySQL 选项一样设置 sql_mode
变量:在配置文件中或使用 :setting:`OPTIONS 中的条目 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"
` :setting:`DATABASES` 中数据库配置的一部分。
隔离等级
在运行并发加载时,来自不同会话的数据库事务(例如,处理不同请求的单独线程)可能会相互交互。 这些交互受每个会话的 事务隔离级别 的影响。 您可以在 :setting:`DATABASES` 中数据库配置的 :setting:`OPTIONS` 部分中使用 'isolation_level'
条目设置连接的隔离级别。 此条目的有效值为四个标准隔离级别:
'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 存储引擎 时)都支持数据库 savepoints。
如果您使用 MyISAM 存储引擎,请注意,如果您尝试使用事务 API 的 与保存点相关的方法,您将收到数据库生成的错误。 这样做的原因是检测 MySQL 数据库/表的存储引擎是一项昂贵的操作,因此根据此类检测的结果,决定在 no-op 中动态转换这些方法是不值得的。
特定字段的注意事项
字符字段
如果您使用 unique=True
作为字段,则任何以 VARCHAR
列类型存储的字段的 max_length
限制为 255 个字符。 这会影响 CharField、SlugField。
TextField 限制
MySQL 只能索引 BLOB
或 TEXT
列的前 N 个字符。 由于 TextField
没有定义的长度,您不能将其标记为 unique=True
。 MySQL 将报告:“BLOB/TEXT 列” ' 在没有密钥长度的密钥规范中使用”。
支持时间和 DateTime 字段的小数秒。
MySQL 5.6.4 及更高版本可以存储小数秒,前提是列定义包括小数指示(例如 DATETIME(6)
)。 早期版本根本不支持它们。
如果数据库服务器支持,Django 不会升级现有列以包含小数秒。 如果您想在现有数据库上启用它们,您可以通过执行如下命令手动更新目标数据库上的列:
ALTER TABLE `your_table` MODIFY `your_datetime_column` DATETIME(6)
TIMESTAMP 列
如果您使用的旧数据库包含TIMESTAMP
列,您必须设置 :设置:`USE_TZ = False ` 以避免数据损坏。 :djadmin:`inspectdb` 将这些列映射到 DateTimeField 并且如果您启用时区支持,MySQL 和 Django 都会尝试将值从 UTC 转换为本地时间。
行锁定 QuerySet.select_for_update()
MySQL 不支持 SELECT ... FOR UPDATE
语句的某些选项。 如果 select_for_update()
与不受支持的选项一起使用,则会引发 NotSupportedError。
选项 | MySQL |
---|---|
SKIP LOCKED
|
X (≥8.0.1) |
NOWAIT
|
X (≥8.0.1) |
OF
|
在 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.9.0 及以上版本。
SQLite 为主要是只读的或需要较小安装空间的应用程序提供了一个极好的开发替代方案。 但是,与所有数据库服务器一样,您应该注意 SQLite 特有的一些差异。
子串匹配和大小写敏感性
对于所有 SQLite 版本,在尝试匹配某些类型的字符串时会出现一些稍微违反直觉的行为。 这些是在查询集中使用 :lookup:`iexact` 或 :lookup:`contains` 过滤器时触发的。 该行为分为两种情况:
1. 对于子串匹配,所有的匹配都是不区分大小写的。 也就是说,诸如 filter(name__contains="aa")
之类的过滤器将匹配名称 "Aabb"
。
2. 对于包含 ASCII 范围之外的字符的字符串,所有精确的字符串匹配都区分大小写,即使不区分大小写的选项被传递到查询中也是如此。 因此,在这些情况下,:lookup:`iexact` 过滤器的行为将与 :lookup:`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 在抛出“数据库被锁定”错误之前等待更长的时间; 它不会真正解决它们。
QuerySet.select_for_update() 不支持
SQLite 不支持 SELECT ... FOR UPDATE
语法。 调用它不会有任何影响。
不支持原始查询中的“pyformat”参数样式
对于大多数后端,原始查询(Manager.raw()
或 cursor.execute()
)可以使用“pyformat”参数样式,其中查询中的占位符指定为 '%(name)s'
,参数作为字典而不是列表。 SQLite 不支持这一点。
使用QuerySet.iterator()时的隔离
在使用 QuerySet.iterator() 迭代表的同时修改表时,SQLite 中的隔离 中描述了一些特殊的注意事项。 如果在循环中添加、更改或删除一行,则该行可能会或可能不会出现,或者可能会出现两次,从迭代器获取的后续结果中。 您的代码必须处理这个问题。
Oracle 注意事项
Django 支持 Oracle 数据库服务器 12.1 及更高版本。 需要 6.0 到 7.3 版的 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
权限,并且用户被授予 [ X124X] 可以授予 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': '',
}
}
在这种情况下,您应该将 :setting:`HOST` 和 :setting:`PORT` 都留空。 但是,如果您不使用 tnsnames.ora
文件或类似的命名方法,并希望使用 SID(在本例中为“xe”)进行连接,则同时填写 :setting:`HOST` 和 :setting:`PORT` 像这样:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.oracle',
'NAME': 'xe',
'USER': 'a_user',
'PASSWORD': 'a_password',
'HOST': 'dbprod01ned.mycompany.com',
'PORT': '1540',
}
}
您应该同时提供 :setting:`HOST` 和 :setting:`PORT`,或者将两者都保留为空字符串。 Django 将根据该选择使用不同的连接描述符。
完整的 DSN 和 Easy Connect
如果 :setting:`HOST` 和 :setting:`PORT` 都可以在 :setting:`NAME` 中使用 Full 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,
},
如果不这样做,可能会导致崩溃和其他奇怪的行为。
插入……返回
默认情况下,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
列表中。 这意味着尝试在包含TextField
列的模型上使用QuerySet.distinct
方法将导致在针对 Oracle 运行时出现ORA-00932
错误。 作为一种解决方法,将QuerySet.defer
方法与distinct()
结合使用以防止TextField
列包含在SELECT DISTINCT
列表中。
使用第三方数据库后端
除了官方支持的数据库外,还有第三方提供的后端,允许你在 Django 中使用其他数据库。
这些非官方后端支持的 Django 版本和 ORM 功能差异很大。 有关这些非官方后端的特定功能的查询以及任何支持查询,应直接发送到每个 3rd 方项目提供的支持渠道。