この投稿の長さについて、事前にお詫び申し上げます。情報を漏らさないようにしたいだけです。
Django アプリの外部で Django の ORM を使用するアプリケーションがあり、call_command('syncdb')
直接呼び出して「syncdb」を使用します (注: 以下にリストされているすべてのシナリオで virtualenv を使用しています)。
アプリケーションの単体テストでは、バックエンドとして SQLite を使用して「テスト」django データベースをセットアップしようとします (実稼働環境では MySQL を使用します)。
単体テストの 1 つが実行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
変数は壊れた環境の狂ったように見えるクラッドのリストであるため、そのメソッドは各モデルのテーブル名がデータベースにないと誤って主張しているように見えます。これにより、最初に述べた元のエラーが表示されます。
本当に気が狂っていないことを確認するために、正しいリストを手動で挿入して比較してみました。
(動作しない環境)
(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'
編集: OK、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に固執していますか?