django迁移不可能因为字段默认值中的bug?

时间:2017-01-22 20:20:46

标签: django django-models django-migrations

我正面临一个与我的django模型之间的奇怪情况。 我正在使用Django 1.10.3和python 3.5.2。

模型看起来像这样(为简洁起见而简化):

class Report(models.Model):
    date = models.FieldDate()
    def fieldA_default(self):
        return MyObject.objects.filter(date=self.date).count()
    fieldA = models.IntegerField(default=fieldA_default)

我有初始迁移创建模型并添加字段,由django使用./manage.py makemigrations自动生成。

我将此代码提交到我的git repo,并将其部署到我的生产服务器,但实际上并未使用该模型(我的数据库中没有Report个对象)。 我刚刚发现此代码不正确(django set default value of a model field to a self attribute)并决定改为覆盖save()

但是当我将默认值从fieldA_default更改为0时,运行./manage.py makemigrations会失败,因为它会尝试运行旧的默认值函数fieldA_default。在尝试了几个选项后,我最终决定完全删除该模型。但这不起作用,因为makemigrations仍在尝试运行相同的功能。

以下是我删除模型时makemigrations的追溯:

Traceback (most recent call last):
  File "./manage.py", line 22, in <module>
    execute_from_command_line(sys.argv)
  File "venv/lib/python3.5/site-packages/django/core/management/__init__.py", line 367, in execute_from_command_line
    utility.execute()
  File "venv/lib/python3.5/site-packages/django/core/management/__init__.py", line 359, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "venv/lib/python3.5/site-packages/django/core/management/base.py", line 294, in run_from_argv
    self.execute(*args, **cmd_options)
  File "venv/lib/python3.5/site-packages/django/core/management/base.py", line 345, in execute
    output = self.handle(*args, **options)
  File "venv/lib/python3.5/site-packages/django/core/management/commands/makemigrations.py", line 95, in handle
    loader = MigrationLoader(None, ignore_no_migrations=True)
  File "venv/lib/python3.5/site-packages/django/db/migrations/loader.py", line 52, in __init__
    self.build_graph()
  File "venv/lib/python3.5/site-packages/django/db/migrations/loader.py", line 197, in build_graph
    self.load_disk()
  File "venv/lib/python3.5/site-packages/django/db/migrations/loader.py", line 108, in load_disk
    migration_module = import_module("%s.%s" % (module_name, migration_name))
  File "venv/lib/python3.5/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 986, in _gcd_import
  File "<frozen importlib._bootstrap>", line 969, in _find_and_load
  File "<frozen importlib._bootstrap>", line 958, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 673, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 665, in exec_module
  File "<frozen importlib._bootstrap>", line 222, in _call_with_frames_removed
  File "xxx/reporting/migrations/0001_initial.py", line 9, in <module>
    class Migration(migrations.Migration):
  File "xxx/reporting/migrations/0001_initial.py", line 22, in Migration
    ('fieldA', models.IntegerField(default=reporting.models.Report.fieldA_default)),
AttributeError: module 'reporting.models' has no attribute 'Report'

我有几个问题:

  • 为什么Django会运行这个“旧”代码,即使我正在删除该模型?
  • 如果没有Django对我尖叫,我怎么设法将这个无效代码转移到迁移中?
  • 如何解决这种情况,以免我的生产服务器中断?我完全放弃模型并重建它没有任何问题,但我似乎不允许这样做。

2 个答案:

答案 0 :(得分:1)

  

为什么Django运行这个“旧”代码,即使我正在删除该模型?

因为模型及其方法仍在迁移文件0001_initial.py

中引用
  

但是当我将fieldA_default的默认值更改为0时,运行./manage.py makemigrations失败,因为它尝试运行旧的默认值函数fieldA_default。

我假设,在重置字段上的默认值后,您删除了 - 现在已过时 - 方法fieldA_default。如上所述,此方法在初始迁移中引用,现在必须中断。

  

如果没有Django对我尖叫,我如何设法将这个无效代码转移到迁移中?

创建迁移时,代码无效。简单的向前迁移无法处理模型上的某些更改。在你的情况下:

  • 在迁移文件中引用和导入模型时删除模型(本身只是另一个不能导入不存在的类的python模块)

  • 同样用于删除默认方法。

当您的模型代码混乱或与您的迁移/ db不同步时,您可以做的一件事makemigrations在当前状态下不起作用,如下所示:

  1. python manage.py migrate app_name zero # undo all existing migrations of app
  2. 从应用中删除所有迁移文件。 (可选)如果您已经在数据库中拥有有价值的数据,则可以逐个撤消它们,看看步骤3是否已经有效

  3. python manage.py makemigrations app_name # new start from clean sheet

  4. 这在开发过程中很容易,可以被认为是迁移压缩的替代方案,但如果您已经在数据库中拥有生产数据,那么这显然是最后的选择。但在这种情况下,无论如何都应该谨慎应用模型更改:)

答案 1 :(得分:0)

  1. 从迁移中手动删除对报告的所有引用
  2. 从数据库中删除报告表
  3. 运行python manage.py makemigrations。它将为Report创建新的迁移。
  4. 推送,部署。