高级数据库管理
本章提供了有关Django中每个支持的关系数据库的附加信息,以及连接到遗留数据库的注释和提示和技巧。
一般注意事项
Django试图在所有数据库后端支持尽可能多的功能。然而,并不是所有的数据库后端都是一样的,Django开发人员必须做出关于哪些功能需要支持以及哪些假设可以安全进行的设计决策。
该文件描述了一些可能与Django使用相关的功能。当然,它不能替代服务器特定的文档或参考手册。
持久连接
持久连接避免了在每个请求中重新建立与数据库的连接的开销。它们由CONN_MAX_AGE参数控制,该参数定义连接的最长生存期。它可以为每个数据库独立设置。默认值为0,保留每个请求结束时关闭数据库连接的历史行为。要启用持续连接,请将CONN_MAX_AGE设置为正数秒。对于无限持续连接,请将其设置为无。
连接管理
Django在首次进行数据库查询时打开数据库的连接。它保持这个连接打开并在随后的请求中重用它。一旦Django超过由CONN_MAX_AGE定义的最大年龄或者它不再可用时,Django会关闭连接。
详细地说,Django自动打开一个到数据库的连接,只要它需要一个连接并且没有一个 - 或者是因为这是第一个连接,或者是因为之前的连接已关闭。
在每个请求开始时,Django在达到其最大年龄时关闭连接。如果您的数据库在一段时间后终止空闲连接,则应该将CONN_MAX_AGE设置为较低的值,以便Django不会尝试使用已由数据库服务器终止的连接。 (此问题可能只会影响非常低的流量网站。)
在每个请求结束时,Django会在连接达到其最大年龄或处于不可恢复的错误状态时关闭连接。如果在处理请求时发生任何数据库错误,Django会检查连接是否仍然有效,如果没有则关闭。因此,数据库错误至多会影响一个请求;如果连接变得不可用,则下一个请求会重新建立连接。
注意事项
由于每个线程维护自己的连接,因此您的数据库必须至少支持与工作线程同时连接的同时连接数。
有时,数据库不会被大多数视图访问,例如因为它是外部系统的数据库,或者由于缓存。在这种情况下,您应该将CONN_MAX_AGE设置为较低值甚至0,因为保持不太可能被重用的连接没有意义。这将有助于保持与此数据库同时连接的数量很小。
开发服务器为其处理的每个请求创建一个新线程,否定持久连接的效果。在开发过程中不要启用它们。
当Django建立到数据库的连接时,它根据使用的后端设置适当的参数。如果启用永久连接,则此设置不再重复每个请求。如果修改连接的隔离级别或时区等参数,则应该在每个请求结束时恢复Django的默认值,在每个请求开始时强制一个适当的值,或者禁用持久连接。
编码
Django假设所有的数据库都使用UTF-8编码。使用其他编码可能会导致意外的行为,例如数据库中的错误对于在Django中有效的数据来说太长。有关如何正确设置数据库的信息,请参阅下面的数据库特定注释。
PostgreSQL笔记
Django支持PostgreSQL 9.0和更高版本。
它需要使用Psycopg2 2.0.9或更高版本。
优化PostgreSQL的配置
Django的数据库连接需要以下参数:
- client_encoding:'UTF8'
- default_transaction_isolation:默认为'读取已提交',或连接选项中设置的值(请参见下文)
- timezone:当USE_TZ为真时为'UTC',否则为TIME_ZONE的值。
如果这些参数已经具有正确的值,那么Django不会为每个新连接设置它们,这会稍微提高性能。 您可以直接在postgresql.conf中配置它们,或者更方便地使用ALTER ROLE为每个数据库用户配置它们。
Django在没有这种优化的情况下工作得很好,但每个新连接都会执行一些额外的查询来设置这些参数。
隔离级别
与PostgreSQL本身一样,Django默认为READ COMMITTED隔离级别。 如果您需要更高的隔离级别,例如REPEATABLE READ或SERIALIZABLE,请将其设置在DATABASES中的数据库配置的OPTIONS部分中:
import psycopg2.extensions
DATABASES = {
# ...
'OPTIONS': {
'isolation_level': psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE,
},
}
在更高的隔离级别下,应用程序应该准备好处理序列化失败时引发的异常。此选项专为高级用途而设计。
Varchar和文本列的索引
在模型字段中指定db_index = True时,Django通常输出一个CREATE INDEX语句。但是,如果该字段的数据库类型是varchar或text(例如由CharField,FileField和TextField使用),那么Django将创建一个额外的索引,该列使用适当的PostgreSQL操作符类。额外的索引对正确执行在其SQL中使用LIKE运算符的查找是必需的,就像使用contains和startswith查找类型一样。
MySQL注释
版本支持
Django支持MySQL 5.5及更高版本。
Django的inspectdb功能使用information_schema数据库,其中包含所有数据库模式的详细数据。
Django期望数据库支持Unicode(UTF-8编码)并委托它执行事务和参照完整性。在使用MyISAM存储引擎时,了解后两个实际上并未由MySQL强制执行的事实很重要,请参阅下一节。
存储引擎
MySQL有几个存储引擎。您可以更改服务器配置中的默认存储引擎。
在MySQL 5.5.4之前,默认引擎是MyISAM。 MyISAM的主要缺点是不支持事务或强制外键约束。另一方面,它是支持全文索引和搜索的唯一引擎,直到MySQL 5.6.4。
由于MySQL 5.5.5,默认的存储引擎是InnoDB。这个引擎完全是事务性的,并且支持外键引用。这可能是目前最好的选择。但是,请注意InnoDB自动增量计数器在重新启动MySQL时丢失了,因为它不记住AUTO_INCREMENT值,而是将其重新创建为max(id)+1。这可能会导致无意中重复使用AutoField值。
如果将现有项目升级到MySQL 5.5.5并随后添加一些表,请确保您的表使用相同的存储引擎(即MyISAM与InnoDB)。特别是,如果在它们之间具有ForeignKey的表使用不同的存储引擎,则在运行迁移时可能会看到类似下面的错误:
_mysql_exceptions.OperationalError: (
1005, "Can't create table '\\db_name\\.#sql-4a8_ab' (errno: 150)"
)
MySQL DB API驱动程序
Python数据库API在PEP 249中有描述。MySQL有三个主要的驱动程序来实现这个API:
- MySQLdb是Andy Dustman十多年来开发和支持的本地驱动程序。
- mysqlclient是MySQLdb的一个分支,它明显支持Python 3,可以用作MySQLdb的嵌入式替代品。 在撰写本文时,这是使用Django与MySQL的推荐选择。
- MySQL Connector/ Python是来自Oracle的纯Python驱动程序,不需要MySQL客户端库或标准库之外的任何Python模块。
所有这些驱动程序都是线程安全的并提供连接池。 MySQLdb的
是目前唯一不支持Python 3的。
除了DB API驱动程序之外,Django还需要一个适配器才能从其ORM访问数据库驱动程序。 Django为MySQLdb / mysqlclient提供了一个适配器,而MySQL Connector / Python包含它自己的。
MySQLdb
Django需要MySQLdb版本1.2.1p2或更高版本。
如果您看到ImportError:尝试使用Django时无法导入名称ImmutableSet,那么您的MySQLdb安装可能包含过时的sets.py文件,该文件与Python 2.4及更高版本中的同名内置模块冲突。要解决此问题,请验证您是否安装了MySQLdb 1.2.1p2或更新版本,然后删除早期版本所保留的MySQLdb目录下的sets.py文件。
MySQLdb将日期字符串转换为日期时间对象的方式存在已知问题。具体来说,值为0000-00-00的日期字符串对MySQL有效,但会被MySQLdb转换为无。
这意味着在使用loaddata / dumpdata时可能需要使用具有“0000-00-00”值的行,因此它们将被转换为“无”。
在撰写本文时,最新版本的MySQLdb(1.2.4)不支持Python 3.为了在Python 3下使用MySQLdb,您必须安装mysqlclient。
MySqlClient
Django需要mysqlclient 1.3.3或更高版本。
请注意,Python 3.2不受支持。除了Python 3.3+支持外,
mysqlclient的行为主要与MySQLdb相同。
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归类。在这种情况下要注意的主要问题是,如果您使用的是MySQLdb 1.2.2,那么Django中的数据库后端将返回从数据库接收的任何字符字段的字节串(而不是Unicode字符串)。这与Django的常规做法有很大的不同
返回Unicode字符串。
如果您配置表使用utf8_bin排序规则,则由开发人员决定如何处理这一事实,即您将收到字节串。 Django本身应该主要适用于这样的列(除了下面描述的contrib.sessions Session和contrib.admin LogEntry表),但是如果代码真的需要,你的代码必须准备好调用django.utils.encoding.smart_text()使用一致的数据 - Django不会为你做这件事(数据库后端层和模型填充层在内部是分开的,因此数据库层不知道它需要在这种特定情况下进行这种转换)。
如果您使用的是MySQLdb 1.2.1p2,则即使使用utf8_bin归类,Django的标准CharField类也会返回Unicode字符串。但是,TextField字段将作为array.array实例(来自Python的标准数组模块)返回。 Django可以做的事情并不多,因为从数据库读入数据时,进行必要转换所需的信息不可用。这个问题在MySQLdb 1.2.2中得到解决,所以如果你想使用TextField和utf8_bin整理,升级到版本1.2.2,然后如上所述处理字节串(这应该不是太困难)是推荐的解决方案。
如果您决定对MySQLdb 1.2.1p2或1.2.2的某些表使用utf8_bin排序规则,则仍应对django.contrib.sessions.models.Session表(通常称为django_session)使用utf8_general_ci(默认)排序规则,以及django.contrib.admin.models.LogEntry表(通常称为django_admin_log)。请注意,根据MySQL Unicode字符集,与utf8_unicode_ci的比较相比,utf8_general_ci排序比较速度更快,但正确性稍差。如果您的应用程序可以接受,则应该使用utf8_general_ci,因为它速度更快。如果这不可接受(例如,如果您需要德语字典顺序),请使用utf8_unicode_ci,因为它更准确。
模型表单以区分大小写的方式验证唯一字段。 因此,当使用不区分大小写的排序规则时,只有大小写不同的唯一字段值的formset才会通过验证,但在调用save()时,会引发IntegrityError。
连接到数据库
连接设置按此顺序使用:
- OPTIONS.
- NAME, USER, PASSWORD, HOST, PORT
- MySQL 可选文件.
换句话说,如果你在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。 有关更多详细信息,请参阅MySQLdb文档。
创建你的表
当Django生成模式时,它不指定存储引擎,因此将使用您的数据库服务器配置的任何默认存储引擎来创建表。
最简单的解决方案是将数据库服务器的默认存储引擎设置为所需的引擎。
如果您使用托管服务并且无法更改服务器的默认存储引擎,则可以选择以下几种方式:
-
在创建表之后,执行ALTER TABLE语句将表转换为新的存储引擎(如InnoDB):
ALTER TABLE <tablename> ENGINE=INNODB;
如果你有很多表格,这可能很乏味。
-
另一种选择是在创建表之前为MySQLdb使用init_command选项:
'OPTIONS':{ 'init_command': 'SET storage_engine=INNODB', },
这会在连接到数据库时设置默认存储引擎。 在创建表之后,应该删除此选项,因为它只在创建表时将需要的查询添加到每个数据库连接。
表名称
即使在最新版本的MySQL中也存在已知的问题,当某些SQL语句在特定条件下执行时,可能会导致更改表名的情况。如果可能,建议您使用小写表名,以避免此行为可能引起的任何问题。当Django从模型中自动生成表名时,它使用小写的表名,所以如果您通过db_table参数覆盖表名,主要考虑这一点。
保存点
Django ORM和MySQL(使用InnoDB存储引擎时)都支持数据库保存点。
如果您使用MyISAM存储引擎,请注意如果您尝试使用事务API的保存点相关方法,则会收到数据库生成的错误。原因在于,检测MySQL数据库/表的存储引擎是一项昂贵的操作,所以决定在基于这种检测结果的no-op中动态地转换这些方法是不值得的。
关于特定字段的注释
字符字段
如果对字段使用unique = True,那么使用VARCHAR列类型存储的任何字段的max_length限制为255个字符。这会影响CharField,SlugField和CommaSeparatedIntegerField。
时间和日期时间字段的分秒支持
如果列定义包含小数指示(例如DATETIME(6)),MySQL 5.6.4和更高版本可以存储小数秒。早期版本根本不支持它们。另外,早于1.2.5的MySQLdb版本有一个错误,也会阻止MySQL使用小数秒。
如果数据库服务器支持,Django不会升级现有列以包含小数秒。如果要在现有数据库上启用它们,则需要通过执行如下命令手动更新目标数据库上的列:
ALTER TABLE `your_table` MODIFY `your_datetime_column` DATETIME(6)
或者在数据迁移中使用RunSQL操作。
默认情况下,现在使用mysqlclient或MySQLdb 1.2.5或更高版本在MySQL 5.6.4或更高版本上创建新的DateTimeField或TimeField列。
TIMESTAMP列
如果您使用的是包含TIMESTAMP列的旧数据库,则必须设置USE_TZ = False以避免数据损坏。 inspectdb将这些列映射到DateTimeField,如果启用时区支持,则MySQL和Django都会尝试将UTC值转换为本地时间。
使用Queryset.Select_For_Update()锁定行
MySQL不支持NOW ... FOR UPDATE语句的NOWAIT选项。如果select_for_update()与nowait = True一起使用,则会引发DatabaseError。
自动类型转换可能会导致意外的结果
在对字符串类型执行查询时,如果使用整数值进行查询,MySQL会在执行比较之前将表中所有值的类型强制为整数。如果您的表格包含值“abc”,“def”并且您查询WHERE mycolumn = 0,则两行都将匹配。同样,WHERE mycolumn = 1将匹配值“abc1”。因此,在查询中使用它之前,Django中包含的字符串类型字段将始终将该值转换为字符串。
如果实现直接从Field继承的自定义模型字段,则重写get_prep_value()或使用extra()或raw(),则应确保执行相应的类型转换。
SQLite注释
SQLite为主要是只读或需要较小安装空间的应用程序提供了一个出色的开发替代方案。但是,与所有数据库服务器一样,SQLite特有的差异也是您应该注意的。
子串匹配和区分大小写
对于所有SQLite版本,当尝试匹配某些类型的字符串时,会有一些反直觉行为。当在Querysets中使用iexact或包含过滤器时,会触发它们。行为分为两种情况:
- 对于子字符串匹配,所有匹配都是不区分大小写的。
这是一个过滤器,如过滤器(name__contains =“aa”)将匹配“AABB”的名称。 - 对于包含ASCII范围外字符的字符串,即使在不区分大小写的选项传递到查询中时,所有精确的字符串匹配都将以区分大小写的方式执行。 因此,在这些情况下,iexact过滤器的行为与确切的过滤器完全相同。
一些可能的解决方法在sqlite.org上有记录,但是它们并未被Django中的默认SQLite后端使用,因为将它们合并在一起将会相当困难。因此,Django公开了默认的SQLite行为,并且在执行不区分大小写或子字符串过滤时应该注意这一点。
旧的SQLite和CASE表达式
在包含ELSE和算术的CASE表达式中处理查询参数时,SQLite 3.6.23.1及更早版本包含一个bug。
SQLite 3.6.23.1于2010年3月发布,不同平台的大多数二进制发行版都包含更新版本的SQLite,但Windows 2.7的Python 2.7安装程序明显例外。
在撰写本文时,Windows的最新版本 - Python 2.7.10 - 包含SQLite 3.6.21。您可以安装pysqlite2或使用sqlite.org的较新版本替换sqlite3.dll(默认安装在C:\ Python27 \ DLL中)以解决此问题。
使用较新版本的SQLite DB-API 2.0驱动程序
如果Django发现有一个可用,那么Django将使用Python标准库附带的pylqlite2模块,优先于sqlite3。
如果需要,这提供了将DB-API 2.0接口或SQLite 3本身升级到比特定Python二进制发行版包含的版本更新的版本的能力。
数据库被锁定错误
SQLite意味着一个轻量级的数据库,因此不能支持高水平的并发。 OperationalError:数据库已锁定错误表明您的应用程序正在经历比sqlite在默认配置下可以处理的更多并发性。此错误意味着一个线程或进程对数据库连接具有排它锁定,并且另一个线程超时等待锁定被释放。
Python的SQLite包装器有一个默认的超时值,它决定了第二个线程在超时前允许等待锁的时间,并引发OperationalError:数据库被锁定的错误。
如果你得到这个错误,你可以通过以下方式解决它:
-
切换到另一个数据库后端。 在某个时刻,SQLite对于真实世界的应用程序来说太轻了,这些并发性错误表明你已经达到了这一点。
-
重写代码以降低并发性并确保数据库事务处理时间较短。
-
通过设置超时数据库选项增加默认超时值:
'OPTIONS':{
# ...
'timeout': 20,
# ...
},
这只会让SQLite在抛出数据库锁定错误之前等待一会儿;它不会真的做任何事来解决它们。
queryset.Select_For_Update()不支持
SQLite不支持SELECT ... FOR UPDATE语法。调用它将不起作用。
不支持原始查询中的pyformat参数样式
对于大多数后端,原始查询(Manager.raw()或cursor.execute())可以使用pyformat参数样式,其中查询中的占位符以'%(name)s'的形式给出,参数作为字典传递比列表。 SQLite不支持这一点。
未在connection.queries中引用的参数
sqlite3没有提供在引用之后检索SQL并替换参数的方法。相反,connection.queries中的SQL通过简单的字符串插值来重建。这可能是不正确的。确保在将查询复制到SQLite shell之前,在必要时添加引号。
Oracle Notes
Django支持Oracle数据库服务器版本11.1和更高版本。虽然我们推荐版本5.1.3或更高版本,因为这些版本支持Python 3,但需要cx_Oracle Python驱动程序的4.3.1或更高版本。
请注意,由于cx_Oracle 5.0中的Unicode损坏错误,该版本的驱动程序不应与Django一起使用; cx_Oracle 5.0.1解决了这个问题,所以如果你想使用更新的cx_Oracle,请使用5.0.1版本。
可以选择使用WITH_UNICODE环境变量编译cx_Oracle 5.0.1或更高版本。这是建议但不是必需的。
为了使python manage.py migrate命令正常工作,您的Oracle数据库用户必须具有运行以下命令的权限:
- CREATE TABLE
- CREATE SEQUENCE
- CREATE PROCEDURE
- CREATE TRIGGER
要运行项目的测试套件,用户通常需要这些额外的权限:
- CREATE 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权限。 特别是,这是Django自己的测试套件所需要的。
所有这些权限都包含在DBA角色中,适用于私有开发人员的数据库。
Oracle数据库后端使用SYS.DBMS_LOB软件包,因此您的用户将需要执行权限。 默认情况下,所有用户通常都可以访问,但如果不是,则需要授予权限,如下所示:
GRANT EXECUTE ON SYS.DBMS_LOB 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将根据该选择使用不同的连接描述符。
线程选项
如果您打算在多线程环境中运行Django(例如,在任何现代操作系统上使用默认MPM模块的Apache),则必须将Oracle数据库配置的线程选项设置为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除外,引号不起作用。
运行迁移时,如果将某些Oracle关键字用作模型字段的名称或db_column选项的值,则可能会遇到ORA-06552错误。 Django引用查询中使用的所有标识符来防止大多数此类问题,但当使用Oracle数据类型作为列名时,仍会发生此错误。尤其要注意避免使用名称日期,时间戳,数字或浮点作为字段名称。
NULL和空字符串
Django通常更喜欢使用空字符串(''“)而不是NULL,但是Oracle对待这两者是相同的。为了解决这个问题,Oracle后端会忽略空字符串作为可能值的字段的显式空值选项,并生成DDL,就像null = True一样。从数据库中获取数据时,假定这些字段之一的NULL值确实意味着空字符串,并且将数据进行无提示转换以反映该假设。
文本字段限制
Oracle后端将TextFields存储为NCLOB列。一般来说,Oracle对这种LOB列的使用施加了一些限制:
- LOB列不能用作主键。
- LOB列不能用于索引。
- LOB列不能在SELECT DISTINCT列表中使用。 这意味着尝试在包含TextField列的模型上使用QuerySet.distinct方法将在针对Oracle运行时导致错误。 作为解决方法,请将QuerySet.defer方法与distinct()一起使用,以防止将TextField列包含在SELECT DISTINCT列表中。
使用第三方数据库后端
除官方支持的数据库外,第三方还提供后端,允许您在Django中使用其他数据库:
- SAP SQL Anywhere
- IBM DB2
- Microsoft SQL Server
- Firebird
- ODBC
- ADSDB
这些非官方后端支持的Django版本和ORM功能差别很大。 有关这些非官方后端的具体功能以及任何支持查询的查询应该针对每个第三方项目提供的支持渠道。
将Django与传统数据库集成
虽然Django最适合开发新的应用程序,但很可能将其集成到传统数据库中。 Django包含了几个实用程序,可以尽可能多地自动执行此过程。
一旦你建立了Django,你就可以按照这个一般过程来整合现有的数据库。
告诉Django你的数据库参数
你需要告诉Django你的数据库连接参数是什么,以及数据库的名称是什么。 通过编辑DATABASES设置并将值分配给“DEFALUT”连接的以下键来完成此操作:
- NAME
- ENGINE <DATABASE-ENGINE>
- USER
- PASSWORD
- HOST
- PORT
自动生成模型
Django自带了一个名为inspectdb的实用程序,可以通过反编译现有数据库来创建模型。 您可以通过运行此命令来查看输出:
python manage.py inspectdb
使用标准的Unix输出重定向将其保存为文件(这也适用于Windows):
python manage.py inspectdb > models.py
这个功能意味着作为一个捷径,而不是最终的模型生成。 有关更多信息,请参阅inspectdb的文档。
清理完模型后,将文件models.py命名并放入包含应用程序的Python包中。 然后将该应用添加到您的INSTALLED_APPS设置。
默认情况下,inspectdb创建非托管模型。 也就是说,模型的Meta类中的managed = False告诉Django不要管理每个表的创建,修改和删除:
class Person(models.Model):
id = models.IntegerField(primary_key=True)
first_name = models.CharField(max_length=70)
class Meta:
managed = False
db_table = 'CENSUS_PERSONS'
如果您确实想让Django管理表的生命周期,那么您需要将上面的托管选项更改为True(或者将其删除,因为True是其默认值)。
安装核心Django表
接下来,运行migrate命令来安装任何额外需要的数据库记录,例如管理员权限和内容类型:
python manage.py migrate
清理生成的模型
正如您所预料的那样,数据库内省并不完美,您需要对结果模型代码进行一些清理。 以下是处理生成的模型的几个指针:
-
每个数据库表都被转换为模型类(即数据库表和模型类之间存在一对一的映射)。 这意味着您需要将任何多对多连接表的模型重构为ManyToManyField对象。
-
每个生成的模型对每个字段都有一个属性,包括id主键字段。 但是,回想一下,如果模型没有主键,Django会自动添加一个id主键字段。 因此,您需要删除看起来像这样的所有行:
id = models.IntegerField(primary_key=True)
这些行不仅是多余的,而且如果您的应用程序将向这些表中添加新记录,它们可能会导致问题。
-
通过查看数据库列类型(例如,VARCHAR,DATE)来确定每个字段的类型(例如CharField,DateField)。 如果inspectdb无法将列的类型映射到模型字段类型,则它将使用TextField并将插入Python注释“此字段类型为猜测”。 在生成模型中的字段旁边。 留意这一点,并根据需要更改字段类型。
-
如果数据库中的某个字段没有相应的Django,那么您可以放心。 Django模型层不需要包含表中的每个字段。
-
如果数据库列名是Python保留字(例如pass,class或for),inspectdb将在属性名称后附加“_field”,并将db_column属性设置为实际字段名称(例如pass,class或for )。例如,如果一个表有一个需要调用的INT列,那么生成的模型将有一个像这样的字段:
for_field = models.IntegerField(db_column='for')
inspectdb将插入Python注释'Field重命名,因为它是该字段旁边的Python保留字。
-
如果数据库包含引用其他表的表(如大多数数据库所做的那样),则可能需要重新排列生成模型的顺序,以便引用其他模型的模型可以正确排序。例如,如果模型Book有一个ForeignKey来模拟Author,则模型Author应该在Model Book之前定义。如果您需要在尚未定义的模型上创建关系,则可以使用包含模型名称的字符串,而不是模型对象本身。
-
inspectdb检测PostgreSQL,MySQL和SQLite的主键。也就是说,它会在适当的地方插入primary_key = True。对于其他数据库,您需要为每个模型中的至少一个字段插入primary_key = True,因为Django模型必须具有primary_key = True字段。
-
外键检测仅适用于PostgreSQL和某些类型的MySQL表。在其他情况下,外键字段将作为IntegerField的生成,假设外键列是INT列。
测试和调整
这些是基本步骤 - 从这里开始,您需要调整Django生成的模型,直到按照您喜欢的方式工作。 尝试通过Django数据库API访问数据,并尝试通过Django的管理站点编辑对象,并相应地编辑模型文件。
下一步是什么?
这就是所有内容!
我希望你喜欢读Mastering Django:Core,并从这本书中学到了很多东西。 尽管本书将作为对Django的完整参考,但仍然无法替代简单的旧练习 - 因此请在Django职业生涯中进行编码和最佳工作!
其余章节纯粹供您参考。 它们包括Django中所有函数和字段的附录和快速参考。