<p>我有时对这个问题采取了一种非传统的方法(阅读其他答案,也许不是那么非传统)。我从来没有和django一起试过,所以我只是做了一些实验。</p>
<p>简而言之,我让代码捕获旧模式导致的异常并应用适当的模式升级。我不认为这是一个公认的答案——这只在某些情况下是合适的(有些人可能永远不会争辩)。但我觉得它有丑小鸭的优雅。</p>
<p>当然,我有一个测试环境,可以在任何时候重置回生产状态。使用这个测试环境,我更新了我的模式并编写了相应的代码——和往常一样。</p>
<p>然后我恢复模式更改并再次测试新代码。我捕获结果错误,执行架构升级,然后重新尝试错误查询。</p>
<p>必须编写升级功能,使其“无害”,这样,如果多次调用(在投入生产时可能会发生这种情况),它只会执行一次操作。</p>
<p>实际的python代码-我把它放在settings.py的末尾来测试这个概念,但是您可能希望将它保存在一个单独的模块中:</p>
<pre><code>from django.db.models.sql.compiler import SQLCompiler
from MySQLdb import OperationalError
orig_exec = SQLCompiler.execute_sql
def new_exec(self, *args, **kw):
try:
return orig_exec(self, *args, **kw)
except OperationalError, e:
if e[0] != 1054: # unknown column
raise
upgradeSchema(self.connection)
return orig_exec(self, *args, **kw)
SQLCompiler.execute_sql = new_exec
def upgradeSchema(conn):
cursor = conn.cursor()
try:
cursor.execute("alter table users add phone varchar(255)")
except OperationalError, e:
if e[0] != 1060: # duplicate column name
raise
</code></pre>
<p>一旦生产环境是最新的,就可以从代码库中删除此自升级代码。但即使你不这样做,代码也不会做任何重要的不必要的工作。</p>
<p>您需要根据您的数据库引擎和模式更改来调整异常类(MySQLdb.operationaleerror,在我的情况下)和数字(1054“未知列”/1060“重复列”),但这应该很容易。</p>
<p>您可能需要添加一些额外的检查,以确保正在执行的sql实际上是错误的,因为有问题的模式更改而不是其他问题,但即使没有,这也会重新引发不相关的异常。唯一的缺点是,在这种情况下,您将在引发异常之前尝试升级和错误查询两次。</p>
<p>关于python,我最喜欢的事情之一是能够在这样的运行时轻松地重写系统方法。它提供了很大的灵活性。</p>