数据库 — Django 文档

来自菜鸟教程
Django/docs/3.1.x/ref/databases
跳转至:导航、​搜索

数据库

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.5 及更高版本。 psycopg2 需要 2.5.4 或更高版本,但建议使用最新版本。

PostgreSQL 连接设置

详见 :setting:`HOST`


优化 PostgreSQL 的配置

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

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

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


隔离级别

与 PostgreSQL 本身一样,Django 默认为 READ COMMITTED 隔离级别 。 如果您需要更高的隔离级别,例如 REPEATABLE READSERIALIZABLE,请在 :setting 中数据库配置的 :setting:`OPTIONS` 部分进行设置:`数据库`:

import psycopg2.extensions

DATABASES = {
    # ...
    'OPTIONS': {
        'isolation_level': psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE,
    },
}

笔记

在更高的隔离级别下,您的应用程序应该准备好处理因序列化失败而引发的异常。 此选项专为高级用途而设计。


varchar 和 text 列的索引

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


添加扩展的迁移操作

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


服务器端游标

当使用 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 语句来执行此操作。


测试数据库模板

您可以使用 :设置:`测试['模板'] ` 设置以指定一个模板 (例如 'template0') 从中创建测试数据库。


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

您可以通过 将 PostgreSQL 配置为非持久 来加快测试执行时间。

警告

这是危险的:它会使您的数据库在服务器崩溃或断电的情况下更容易受到数据丢失或损坏的影响。 仅在开发机器上使用它,您可以轻松恢复集群中所有数据库的全部内容。


MariaDB 笔记

3.0 版中的新功能。


Django 支持 MariaDB 10.2 及更高版本。

要使用 MariaDB,请使用 MySQL 后端,该后端在两者之间共享。 有关更多详细信息,请参阅 MySQL 说明


MySQL笔记

版本支持

Django 支持 MySQL 5.6 及更高版本。

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客户端

Django 需要 mysqlclient 1.4.0 或更高版本。


MySQL 连接器/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


连接到数据库

请参阅 设置文档

连接设置按以下顺序使用:

  1. :设置:`选项`
  2. :setting:`NAME`, :setting:`USER`, :setting:`PASSWORD`, :setting:`HOST` ], :setting:`PORT`
  3. 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 连接选项 可能有用,例如 sslinit_commandsql_mode

设置 sql_mode

从 MySQL 5.7 开始和全新安装的 MySQL 5.6,sql_mode 选项的默认值包含 STRICT_TRANS_TABLES。 当插入时数据被截断时,该选项会将警告升级为错误,因此 Django 强烈建议为 MySQL 激活 严格模式 以防止数据丢失(STRICT_TRANS_TABLESSTRICT_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 个字符。 这会影响 CharFieldSlugField。 有关更多详细信息,请参阅 MySQL 文档


TextField 限制

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


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

MySQL 5.6.4 及更高版本可以存储小数秒,前提是列定义包括小数指示(例如 DATETIME(6))。 早期版本根本不支持它们。

如果数据库服务器支持,Django 不会升级现有列以包含小数秒。 如果您想在现有数据库上启用它们,您可以通过执行如下命令手动更新目标数据库上的列:

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

或在 数据迁移 中使用 RunSQL 操作。


TIMESTAMP 列

如果您使用的旧数据库包含TIMESTAMP列,您必须设置 :设置:`USE_TZ = False ` 以避免数据损坏。 :djadmin:`inspectdb` 将这些列映射到 DateTimeField 并且如果您启用时区支持,MySQL 和 Django 都会尝试将值从 UTC 转换为本地时间。


行锁定 QuerySet.select_for_update()

MySQL 和 MariaDB 不支持 SELECT ... FOR UPDATE 语句的某些选项。 如果 select_for_update() 与不受支持的选项一起使用,则会引发 NotSupportedError

选项 玛丽亚数据库 MySQL
SKIP LOCKED X (≥8.0.1)
NOWAIT X (≥10.3) 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.8.3 及更高版本。

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 中的隔离 中描述了一些特殊的注意事项。 如果在循环中添加、更改或删除一行,则该行可能会或可能不会出现,或者可能会出现两次,从迭代器获取的后续结果中。 您的代码必须处理这个问题。


在 SQLite 上启用 JSON1 扩展

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

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


甲骨文笔记

Django 支持 Oracle 数据库服务器 12.2 及更高版本。 需要 6.0 或更高版本的 cx_Oracle Python 驱动程序。

为了使 python manage.py migrate 命令起作用,您的 Oracle 数据库用户必须具有运行以下命令的权限:

  • 创建表
  • 创建序列
  • 创建程序
  • 创建触发器

要运行项目的测试套件,用户通常需要这些 附加 权限:

  • 创建用户
  • 更改用户
  • 删除用户
  • 创建表空间
  • 删除表空间
  • 使用管理员选项创建会话
  • 使用管理员选项创建表
  • 使用管理员选项创建序列
  • 使用管理员选项创建程序
  • 使用管理选项创建触发器

虽然 RESOURCE 角色具有所需的 CREATE TABLECREATE SEQUENCECREATE PROCEDURECREATE TRIGGER 权限,并且用户被授予 [ X124X] 可以授予 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': '',
    }
}

在这种情况下,您应该将 :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 和轻松连接

如果 :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 数据类型用作列名时仍然会发生此错误。 尤其要注意避免使用名称 datetimestampnumberfloat 作为字段名称。


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 带有内置的数据库后端。 您可以对现有数据库后端进行子类化以修改其行为、功能或配置。

例如,考虑您需要更改单个数据库功能。 首先,您必须创建一个包含 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 文件中指定一个 :setting:`DATABASE-ENGINE`

DATABASES = {
    'default': {
        'ENGINE': 'mydbengine',
        ...
    },
}

您可以通过查看 :source:`django/db/backends` 来查看当前的数据库引擎列表。


使用第 3 方数据库后端

除了官方支持的数据库,还有 3rd 方提供的后端,允许你在 Django 中使用其他数据库:

这些非官方后端支持的 Django 版本和 ORM 功能差异很大。 有关这些非官方后端的特定功能的查询以及任何支持查询,应直接发送到每个 3rd 方项目提供的支持渠道。