Заранее извиняюсь за длину поста. Я просто хочу убедиться, что не упустил никакой информации.
У меня есть приложение, которое использует ORM django вне приложения django, которое использует «syncdb», вызывая call_command('syncdb')
напрямую (примечание: я использую virtualenv для каждого сценария, указанного ниже).
Мои модульные тесты в приложении пытаются настроить «тестовую» базу данных django, используя SQLite в качестве бэкэнда (тогда как производственная среда использует MySQL).
Каждый раз, когда запускается один из модульных тестов, он вызывает call_command('syncdb')
, используя одни и те же настройки тестового django для каждого теста.
Я могу запустить эти модульные тесты в двух разных средах (одна с Windows 7/Python 2.7.3, другая с Mac OS X ML/Python 2.7.2). С тестами проблем нет; но это относительно чистые установки Python на обоих.
Однако, когда я пытаюсь запустить это на сервере RHEL, я получаю следующую ошибку, когда модульные тесты пытаются запустить syncdb:
Ошибка базы данных: таблица «my_app_mytable» уже существует
После долгого гугления и отладки я (думаю) устранил ошибки, о которых сообщалось здесь и здесь.
Я много взламывал и думаю, что сузил проблему до этого утверждения в командный файл django syncdb (как бы безумно это ни звучало) (строка 59):
tables = connection.introspection.table_names()
Я установил pdb.set_trace()
внутри источника django syncdb в обеих средах, чтобы посмотреть. Вот что я нашел:
(Env, который работает)
(Pdb) tables
[u'my_app_mytable', u'my_app_myothertable']
Там вроде нормально. Судя по файлу syncdb, django использует переменную tables
для сравнения моделей приложения с тем, что уже есть в базе данных.
(Env, который не работает)
(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
. Я также запускал их вручную в обеих средах:
(Env, который работает)
(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)
, и в обеих средах она выглядит как совершенно нормальная строка. Тем не мение:
(Env, который не работает)
(Pdb) opts = all_models[0][1][0]._meta
(Pdb) converter = connection.introspection.table_name_converter
(Pdb) converter(opts.db_table) in tables
False
Таким образом, похоже, что поскольку переменная tables
представляет собой список безумно выглядящего мусора в сломанной среде, этот метод ложно утверждает, что имя таблицы каждой модели отсутствует в базе данных, что дает мне исходную ошибку, о которой я говорил в начале.
Просто чтобы убедиться, что я действительно не схожу с ума, я также попытался вручную вставить правильный список для сравнения:
(Env, который не работает)
(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()]
Поэтому я подключился к файлу sqlite вручную в интерпретаторе Python и выполнил этот запрос:
(Env, который не работает)
>>> 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 для этого запроса. В рабочей среде он вернул следующее:
(Env, который работает)
>>> curs.fetchall()
[(u'my_app_mytable',), (u'my_app_myothertable',)]
Даже без кодировки, определенной вверху, у «рабочей» среды, похоже, нет проблем с интерпретацией моего файла models
и соответствующим созданием таблиц. Есть ли какая-то настройка SQLite по умолчанию, которая вызывает это? Или git конвертирует файл в UTF-16LE в сломанной среде и придерживается UTF-8/ASCII в рабочей среде?
unicode
(который на самом деле является UTF-16LE, за исключением того, что он находится вunicode
) и при чем здесь cStringIO? - person Ignacio Vazquez-Abrams   schedule 10.10.2012cStringIO.StringIO(u"fubar").getvalue()
возвращает строку типа'f\x00u\x00b\x00a\x00r\x00'
. Я думаю, что, возможно, неправильно прочитал вопрос, является ли поведение, которое я показал в своем вопросе, ожидаемым поведением? - person Nitzle   schedule 10.10.2012