迁移与 forms.py 冲突

Posted

技术标签:

【中文标题】迁移与 forms.py 冲突【英文标题】:Migration ***es with forms.py 【发布时间】:2017-01-24 22:45:21 【问题描述】:

由于forms.py,命令python manage.py makemigrations 大部分时间都失败了,其中在类定义级别引用了新模型或新字段。

所以我必须注释每个这样的定义才能进行迁移。这是一项痛苦的任务。

我不明白为什么迁移过程会导入forms.py 模块。我认为导入模型模块就足够了。

有没有办法避免这些错误?

【问题讨论】:

您还没有显示回溯,但我的猜测是 Django 检查框架正在加载 url,然后加载视图和表单。您应该能够重组您的表单以避免错误,但除非您显示一些代码,否则我们无能为力。 This question 类似。 谢谢@alasdair。如果迁移导入url.py,那么我理解我的问题。我仍然不明白为什么它会导入网址!我很惊讶其他人没有声称这一点。 导入 URL 的是系统检查,而不是迁移。 system checks 在 makemigrations 命令之前运行。 好的,我明白了。我很惊讶自己一个人遇到这个问题。 你并不孤单。我在第一条评论中链接到了一个类似的问题。 【参考方案1】:

感谢@alasdair,我理解了我的问题并找到了解决方法:我替换了views.py 文件中的原始代码

from MyApp import forms

import sys
if 'makemigrations' not in sys.argv and 'migrate' not in sys.argv:
    from MyApp import forms

在我的情况下它工作得很好,但我想有一个更好的方法可以知道当前进程是否是迁移。如果有,请指教。

【讨论】:

这可能不是正确的方法。应该修复 forms.py 中的问题 - 听起来它可能在导入时查询数据库,这不是一个好主意。请参阅 Nate 的回答,了解如何重组表单以避免这种情况。【参考方案2】:

通过可调用初始化...

def get_provinces():
province_choices = []
for province in ProvinceCode.objects.filter(country_code_id=1).order_by('code'):
    province_choices.append((province.code, province.code))
return province_choices

class MemberForm(forms.Form):
    provinces = forms.ChoiceField(label='Provinces', 
    choices=get_provinces, required=True)

参考这里-Django relation error when running make migrations

【讨论】:

请不要发布对其他 Stack Exchange 问题的仅链接答案。相反,请在此处包含答案的基本部分,并针对此特定问题定制答案。【参考方案3】:

我遇到了同样的问题并找到了具体问题。当调用 migrate 命令时,Django 的系统检查进入了我的 forms.py,然后当他们遇到一行代码对迁移应该创建的表进行查询时会失败。我有一个选择字段,它使用这样的数据库查询来实例化选择:

university = forms.ChoiceField(
    choices=[('', '')] + [(university.id, university.name) for university in University.objects.all()],
    widget=forms.Select(
        attrs=
            'class': 'form-control',
            'placeholder': 'University',
        
    ),
    required=True
)

解决方案是从选择中删除查询(将其保留为 [('', '')],然后在类的 init 方法中填充选择。

class UniversityForm(forms.Form):

    university = forms.ChoiceField(
        choices=[('', '')],
        widget=forms.Select(
            attrs=
                'class': 'form-control',
                'placeholder': 'University',
            
        ),
        required=True
    )


def __init__(self, *args, **kwargs):
    super(UniversityForm, self).__init__(*args, **kwargs)

    # Load choices here so db calls are not made during migrations.
    self.fields['university'].choices = [('', '')] + [(university.name, university.name) for university in University.objects.all()]

【讨论】:

这是正确答案! Django 导入 urls.py -> view.py -> forms.py,其中类声明执行类实例声明(如choices=...querysets=),因此这些后者必须移入__init__ 这是正确答案,请查看。【参考方案4】:

在您的查询中使用 .only 排除新列,如下所示:

University.objects.only('id', 'name').all()

然后运行您的迁移。

【讨论】:

【参考方案5】:

我在我的一个表单中遇到了与ModelChoiceField 类似的问题。我必须注释掉我的表单代码才能进行迁移。

对我来说,解决方案是将所有表单导入移动到 views.py 中各自的视图方法中。

之前:

from .forms import CalculatorForm

def calculator(request):            
    if request.method != 'POST':
        form = CalculatorForm()
        # ...

之后:

def calculator(request):
    from .forms import CalculatorForm
    if request.method != 'POST':
        form = CalculatorForm()
        # ...

【讨论】:

如果你有很多观点,那是多么痛苦!尽管有其他 cmets,但我认为我的解决方案是最好的,因为它在模块的开头只需要多 3 行,并且避免污染其余代码。 这值得商榷。对于每个具有表单的视图,这只是一个额外的导入。【参考方案6】:

由于 Django>=3.0,一些管理命令可以在不使用检查框架的情况下通过 --skip-checks(reference) 提前调用。因此,可以应用迁移,即使表单、视图中的某些代码路径当前不处于理想状态(再次:请参阅 @erik-kalkoken 的 answer 以获得更清洁的解决方案):

./manage.py migrate --skip-checks

【讨论】:

以上是关于迁移与 forms.py 冲突的主要内容,如果未能解决你的问题,请参考以下文章

Django:合并迁移冲突的最佳方法

Laravel 迁移语法错误或访问冲突 1064

Wildfly 数据源与 DefaultDS 冲突

将迁移添加到源代码管理并合并 Django 中的冲突迁移

Laravel 7 迁移:语法错误或访问冲突:1068 定义了多个主键

数据泵逻辑迁移后sequence的唯一冲突