使用查询数据库的默认字段函数迁移时出现 Django 错误

kei*_*thb 5 python django django-models django-migrations

我正在尝试创建一个可调用的默认字段,如下所示:

from datetime import date, timedelta
from django.db import models

class Preferences(models.Model):
    expiration_days = models.IntegerField(default=30)
    ... other db settings here ...

class ImportantBusinessObject(models.Model):
    expiration_date = models.DateField(default=get_default_expiration_date)

def get_default_expiration_date():
    expiration_days = Preferences.objects.first().expiration_days
    return date.today() + timedelta(days=expiration_days)
Run Code Online (Sandbox Code Playgroud)

这个想法是,数据库中存储了一组全局首选项,这些首选项会影响其他模型对象的默认值。

一切都很好,直到需要向Preferences类中添加另一个字段为止。然后,在构建数据库时(例如,在运行测试时),添加该expiration_date字段的迁移最终会在添加另一个首选项字段的新迁移之前执行。在调用期间get_default_expiration_date(),Preferences 对象是最新版本,但数据库尚未获得最新的迁移,并且出现错误column preferences.new_preference does not exist

解决这个问题的最佳方法是什么?

现在,我在测试期间禁用迁移作为解决方法,但我想知道是否还有其他方法。

我尝试从应用程序注册表中获取模型get_default_expiration_date(),但它仍然是最新的模型,而不是您在函数中给出的版本模型migrations.RunPython

我还尝试将获取对象的行包装Preferences在 try/ except 块中,但数据库最终处于回滚状态,我无法继续。

其他位:

  • 我正在使用 Django 1.8
  • 在我的实际应用程序中,该类Preferences实际上是使用 django-solo 的数据库单例。

更新:澄清操作顺序

以下是正在发生的事情的时间表:

  1. preferences/migrations/initial_0001.py:初始偏好,包括expiration_days
  2. business/migrations/initial_0001.py:初始业务对象,取决于preferences 0001
  3. preferences/migrations/add_pref_0002.py: 添加一个新字段Preferences

一旦我在步骤 #3 中定义了迁移,事情就开始出现问题,因为在运行以添加步骤 #2 中的迁移时,get_default_expiration_date调用会引发错误。从下面关于更新依赖项的评论线程中,我的观点是,为了做到这一点,每次添加新的首选项迁移时,我都需要更新先前定义的依赖项business/migrations/initial_0001.py(以及任何其他行为类似的依赖项)。希望这能稍微澄清一下情况。

Kev*_*nry 5

您似乎遇到了此处文档中讨论的问题:

\n\n
\n

由于不可能序列化任意 Python 代码,因此这些历史模型不会具有您定义的任何自定义方法。

\n
\n\n

这通常出现在创建模型实例的数据迁移的上下文中。如果您在迁移期间没有创建任何实例,那么get_default_expiration_date()永远不会执行,并且不会遇到问题。(我不确定你的情况是做什么的,也许与 django-solo 或你正在使用的另一个包有关?)

\n\n

我认为这种情况下最简单的解决方案是确保默认函数仅引用它们需要的字段,这样它们就不会生成对可能尚不存在的 SQL 列的引用。当您像现在一样获取整个对象时,它会尝试获取所有字段,并查看当前(而非历史)模型定义以查看这些字段是什么。

\n\n

如果您使用values()(或only()),生成的 SQL 将仅引用您指定的字段:

\n\n
def get_default_expiration_date():\n    expiration_days = Preferences.objects.values("expiration_days")[0]["expiration_days"]\n
Run Code Online (Sandbox Code Playgroud)\n