我要提前为这篇文章的长度道歉。我只是想确保我没有遗漏任何信息。
我有一个在 django 应用程序之外使用 django 的 ORM 的应用程序,它通过call_command('syncdb')
直接调用使用“syncdb”(注意:我对下面列出的每个场景都使用了 virtualenv)。
我在应用程序中的单元测试尝试使用 SQLite 作为后端设置一个“测试”django 数据库(而生产环境使用 MySQL)。
每次运行一个单元测试时,它都会call_command('syncdb')
在每个测试中使用相同的测试 django 设置进行调用。
我能够在 2 个不同的环境(一个使用 Windows 7/Python 2.7.3,另一个使用 Mac OS X ML/Python 2.7.2)上运行这些单元测试。测试完全没有问题;但这些都是相对干净的 Python 安装。
但是,当我尝试在 RHEL 服务器上运行它时,当单元测试尝试运行 syncdb 时,我收到以下错误:
DatabaseError:表“my_app_mytable”已经存在
经过大量令人沮丧的谷歌搜索和调试后,我(认为)我已经消除了此处和此处报告的错误。
我做了很多修改,我想我已经将问题缩小到django 的 syncdb 命令文件中的这个语句(听起来很疯狂)(第 59 行):
tables = connection.introspection.table_names()
我pdb.set_trace()
在两个环境中都设置了一个内部 django 的 syncdb 源来查看。这是我发现的:
(有效的环境)
(Pdb) tables
[u'my_app_mytable', u'my_app_myothertable']
那里似乎还可以。从 syncdb 文件的外观来看,django 使用该tables
变量来检查应用程序的模型与数据库中已有的模型。
(环境不起作用)
(Pdb) tables
[u'm\x00y\x00_\x00a\x00p\x00p\x00_\x00m\x00y\x00t\x00a\x00', u'm\x00y\x00_\x00a\x00p\x00p\x00_\x00m\x00y\x00o\x00t\x00']
除非我快疯了,否则我认为这会使 django 源代码中的以下语句返回 false:
def model_installed(model):
opts = model._meta
converter = connection.introspection.table_name_converter
return not ((converter(opts.db_table) in tables) or
(opts.auto_created and converter(opts.auto_created._meta.db_table) in tables))
该方法filter
在该定义之后通过几行调用,看起来它检查是否converter(opts.db_table)
在tables
列表中。我也在两种环境中手动运行它们:
(有效的环境)
(Pdb) opts = all_models[0][1][0]._meta
(Pdb) converter = connection.introspection.table_name_converter
(Pdb) converter(opts.db_table) in tables
True
如您所见,我(有点)手动运行该model_installed
函数以查看converter(opts.db_table)
返回的内容,并且在两种环境中它看起来都像是一个完全正常的字符串。然而:
(环境不起作用)
(Pdb) opts = all_models[0][1][0]._meta
(Pdb) converter = connection.introspection.table_name_converter
(Pdb) converter(opts.db_table) in tables
False
因此,看起来由于tables
变量是损坏环境中看起来很疯狂的 crud 列表,因此该方法错误地声称每个模型的表名不在数据库中,这给了我在开始时所说的原始错误。
为了确保我真的没有发疯,我还尝试手动插入正确的列表进行比较:
(环境不起作用)
(Pdb) converter(opts.db_table) in [u'my_app_mytable', u'my_app_myothertable']
True
我需要在这个环境中重新编译 Python 吗?我在 stackoverflow 上阅读了以下问题,发现我破碎的环境表现出奇怪的行为:
(myvirtualenv)[username@myserver]$ python
Python 2.7.3 (default, Apr 12 2012, 10:40:11)
[GCC 4.4.6 20110731 (Red Hat 4.4.6-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import StringIO, cStringIO, sys
>>> StringIO.StringIO(u"fubar").getvalue()
u'fubar'
>>> cStringIO.StringIO(u"fubar").getvalue()
'fubar'
>>> cStringIO.StringIO(u"\u0405\u0406").getvalue()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
>>> sys.maxunicode
65535
>>> sys.byteorder
'little'
编辑:好的,所以我稍微浏览了 django 源代码,看起来他们正在以这种方式获取表格列表:
def get_table_list(self, cursor):
"Returns a list of table names in the current database."
# Skip the sqlite_sequence system table used for autoincrement key
# generation.
cursor.execute("""
SELECT name FROM sqlite_master
WHERE type='table' AND NOT name='sqlite_sequence'
ORDER BY name""")
return [row[0] for row in cursor.fetchall()]
所以我在 python 解释器中手动连接到 sqlite 文件,并运行该查询:
(环境不起作用)
>>> import sqlite3
>>> conn = sqlite3.connect('/path/to/sqlite/file')
>>> curs = conn.cursor()
>>> curs.execute("""
... SELECT name FROM sqlite_master
... WHERE type='table' AND NOT name='sqlite_sequence'
... ORDER BY name""")
<sqlite3.Cursor object at 0xb7557500>
>>> curs.fetchall()
[(u'c\x00c\x00_\x00s\x00t\x00a\x00t\x00s\x00_\x00c\x00c\x00',), (u'c\x00c\x00_\x00s\x00t\x00a\x00t\x00s\x00_\x00c\x00c\x00s',)]
所以看起来 SQLite 为该查询返回了一个 UTF16-LE 字符串。在工作环境上,它返回以下内容:
(有效的环境)
>>> curs.fetchall()
[(u'my_app_mytable',), (u'my_app_myothertable',)]
models
即使没有在顶部定义编码,“工作”环境在解释我的文件和适当地创建表时似乎也没有任何问题。是否有一些 SQLite 默认设置导致了这种情况?还是 git 在损坏的环境中将文件转换为 UTF-16LE,并在工作环境中坚持使用 UTF-8/ASCII?