Django 不会让我 运行 迁移,因为检查功能检测到对我正在添加的新字段的引用

Django won't let me run migrate because the check function detects references to a new field I am adding

我正在向模型添加一个新字段,但我无法使迁移正常工作

sqlite3.OperationalError: no such column: main_language.iso639_1

我只能通过在我的代码中注释掉对新字段的所有引用来使用 makemigration 生成迁移文件,但现在我无法 运行 在不保留该字段注释掉的情况下进行迁移。

这是不可接受的,因为引用我的新字段显然需要最终出现在我的代码中,并且拉取最新版本代码库的任何其他人在尝试 运行 迁移时将拥有未注释的代码。

尽管多次向模型添加新字段,但我以前没有遇到过这个问题,不知道这次为什么会这样:

# iso639_1 is the new field

class Language(TranslatableMixin, models.Model):
    ENGLISH = 1 # id for English should always be 1
    ISO_HELP_TEXT = _("Please find the correct code at: https://www.loc.gov/standards/iso639-2/php/code_list.php")

    title = models.ForeignKey('main.Sentence', on_delete=models.PROTECT, related_name='LanguageTitle')
    iso639_2 = models.CharField(max_length=8, help_text=ISO_HELP_TEXT)
    iso639_1  = models.CharField(max_length=8, blank=True, default='', help_text=ISO_HELP_TEXT)
    author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
    created = models.DateTimeField(auto_now_add=True)
    objects = LanguageManager()

尝试 运行 'python manage.py migrate':

时收到错误
Traceback (most recent call last):
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\db\backends\utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\db\backends\sqlite3\base.py", line 303, in execute
    return Database.Cursor.execute(self, query, params)
sqlite3.OperationalError: no such column: main_language.iso639_1

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\manage.py", line 22, in <module>
    execute_from_command_line(sys.argv)
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\core\management\__init__.py", line 371, in execute_from_command_line
    utility.execute()
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\core\management\__init__.py", line 365, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\core\management\base.py", line 288, in run_from_argv
    self.execute(*args, **cmd_options)
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\core\management\base.py", line 332, in execute
    self.check()
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\core\management\base.py", line 364, in check
    include_deployment_checks=include_deployment_checks,
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\core\management\commands\migrate.py", line 58, in _run_checks
    issues.extend(super()._run_checks(**kwargs))
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\core\management\base.py", line 351, in _run_checks
    return checks.run_checks(**kwargs)
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\core\checks\registry.py", line 73, in run_checks
    new_errors = check(app_configs=app_configs)
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\core\checks\urls.py", line 13, in check_url_config
    return check_resolver(resolver)
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\core\checks\urls.py", line 23, in check_resolver
    return check_method()
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\urls\resolvers.py", line 399, in check
    for pattern in self.url_patterns:
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\utils\functional.py", line 36, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\urls\resolvers.py", line 540, in url_patterns
    patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\utils\functional.py", line 36, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\urls\resolvers.py", line 533, in urlconf_module
    return import_module(self.urlconf_name)
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\importlib\__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\lang\urls.py", line 3, in <module>
    from lang import views as core_views
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\lang\views.py", line 9, in <module>
    from main.views import background_tasks
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\main\views\__init__.py", line 1, in <module>
    from .assess import *
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\main\views\assess.py", line 6, in <module>
    from .main import move_to_next_mode
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\main\views\main.py", line 4, in <module>
    from main.forms import SignUpCourseForm, SelectClassForm
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\main\forms.py", line 25, in <module>
    class InviteDetailsForm(forms.Form):
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\main\forms.py", line 36, in InviteDetailsForm
    label = _('Which language should the invitation email be sent in?'), 
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\forms\fields.py", line 758, in __init__
    self.choices = choices
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\forms\fields.py", line 775, in _set_choices
    value = list(value)
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\db\models\query.py", line 272, in __iter__
    self._fetch_all()
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\db\models\query.py", line 1179, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\db\models\query.py", line 53, in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\db\models\sql\compiler.py", line 1068, in execute_sql
    cursor.execute(sql, params)
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\db\backends\utils.py", line 100, in execute
    return super().execute(sql, params)
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\db\backends\utils.py", line 68, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\db\backends\utils.py", line 77, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\db\backends\utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\db\utils.py", line 89, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\db\backends\utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\db\backends\sqlite3\base.py", line 303, in execute
    return Database.Cursor.execute(self, query, params)
django.db.utils.OperationalError: no such column: main_language.iso639_1
Finished "C:\Users\Win7\OneDrive\Programming\Git\lang\manage.py migrate" execution.

LanguageManager 中存在引发错误的代码..

class LanguageManager(models.Manager):
    def interface_language_choices(self):
        localizations = [x[0] for x in settings.LANGUAGES]
        return Language.objects.filter(iso639_1__in=localizations)

如果我注释掉 def interface_language_choices(self) 的主体,用 pass 替换它,我就会得到与未给出 return 值的函数相关的错误。为什么它甚至坚持 运行ning 我的代码只是为了进行迁移?

我的迁移文件:

from django.db import migrations, models

def populate_iso(apps, schema_editor):
    Language = apps.get_model('main', 'Language')

    for x in (('eng','en'),('tur','tr'),('rus','ru'),('fra','fr'),('uzb','uz'),('spa','es')):
        L = Language.objects.get(iso639_2=x[0])
        L.iso639_1 = x[1]
        L.save()    


class Migration(migrations.Migration):

    dependencies = [
        ('main', '0030_auto_20180620_1649'),
    ]

    operations = [
        migrations.AddField(
            model_name='language',
            name='iso639_1',
            field=models.CharField(blank=True, default='', help_text='Please find the correct code at: https://www.loc.gov/standards/iso639-2/php/code_list.php', max_length=8),
        ),
        migrations.RunPython(populate_iso),
    ]

来自 the FineManual

Migrations that alter data are usually called “data migrations”; they’re best written as separate migrations, sitting alongside your schema migrations.

IOW:将您的迁移分成两部分 - 第一个添加字段,第二个用相关数据填充它。

但是这其实不是你的问题。从你的回溯:

  File "C:\Users\Win7\OneDrive\Programming\Git\lang\main\forms.py", line 25, in <module>
    class InviteDetailsForm(forms.Form):
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\main\forms.py", line 36, in InviteDetailsForm
    label = _('Which language should the invitation email be sent in?'), 
  File "C:\Users\Win7\OneDrive\Programming\Git\lang\.venv\lib\site-packages\django\forms\fields.py", line 758, in __init__
    self.choices = choices

您的 InviteDetailsForm 代码显然在 class 顶级 进行数据库查询 以填充字段的选择 - 我假设如下:

class InviteDetailsForm(forms.Form):
    # ...
    something = forms.ChoiceField(
        choice= Language.objects.interface_languages_choices()
        # ...
        )

这段代码在导入模块时执行(这是正常的 Python 操作 - class 是可执行语句),这导致了您的问题。解决方法是pass the method itself instead of passing it's result:

    something = forms.ChoiceField(
        # notice the absence of parens !
        choice= Language.objects.interface_languages_choices, 
        # ...
        )

或者使用 ModelChoiceField 而不是 ChoiceField(前者不会强制对查询集求值,而第二个会)。

Why is it even insisting on running my code just to do a migration

因为 "it" 需要加载应用程序和模型以及 url confs,它导入视图,导入你的表单模块,它在你的表单顶层执行代码 class,它调用你的方法。