Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Django:ValueError:尝试在clean中创建失败时,无法在ForeignField上分配none_Python_Django_Django Forms - Fatal编程技术网

Python Django:ValueError:尝试在clean中创建失败时,无法在ForeignField上分配none

Python Django:ValueError:尝试在clean中创建失败时,无法在ForeignField上分配none,python,django,django-forms,Python,Django,Django Forms,我在弄清楚如何在提交表单时自动为ForeignKey字段创建模型实例时遇到了很多麻烦。下面是一个简单的玩具网站,说明了这个问题: 我有两种型号,型号1和型号2。Model2包含Model1的外键。我希望用户能够创建Model2的实例,具体方法是选择要存储在ForeignKey中的Model1实例,或者将该值留空并自动生成Model1实例 我觉得代码应该是这样的。My models.py代码非常简单: # models.py from django.db import models from dj

我在弄清楚如何在提交表单时自动为ForeignKey字段创建模型实例时遇到了很多麻烦。下面是一个简单的玩具网站,说明了这个问题:

我有两种型号,型号1和型号2。Model2包含Model1的外键。我希望用户能够创建Model2的实例,具体方法是选择要存储在ForeignKey中的Model1实例,或者将该值留空并自动生成Model1实例

我觉得代码应该是这样的。My models.py代码非常简单:

# models.py
from django.db import models
from django.core.validators import MinValueValidator


class Model1(models.Model):

    # Note this field cannot be negative
    my_field1 = models.IntegerField(validators=[MinValueValidator(0)])


class Model2(models.Model):
    # blank = True will make key_to_model1 not required on the form,
    # but since null = False, I will still require the ForeignKey
    # to be set in the database.
    related_model1 = models.ForeignKey(Model1, blank=True)

    # Note this field cannot be negative
    my_field2 = models.IntegerField(validators=[MinValueValidator(0)])
py有点复杂,但所发生的事情非常简单。如果Model2Form没有收到Model1的实例,它将尝试在clean方法中自动创建一个实例,并验证它,如果它有效,则保存它。如果无效,则会引发异常

#forms.py
from django import forms
from django.forms.models import model_to_dict

from .models import Model1, Model2


# A ModelForm used for validation purposes only.
class Model1Form(forms.ModelForm):
    class Meta:
        model = Model1


class Model2Form(forms.ModelForm):
    class Meta:
        model = Model2

    def clean(self):
        cleaned_data = super(Model2Form, self).clean()

        if not cleaned_data.get('related_model1', None):

            # Don't instantiate field2 if it doesn't exist.
            val = cleaned_data.get('my_field2', None)
            if not val:
                raise forms.ValidationError("My field must exist")

            # Generate a new instance of Model1 based on Model2's data
            new_model1 = Model1(my_field1=val)

            # validate the Model1 instance with a form form
            validation_form_data = model_to_dict(new_model1)
            validation_form = Model1Form(validation_form_data)

            if not validation_form.is_valid():
                raise forms.ValidationError("Could not create a proper instance of Model1.")

            # set the model1 instance to the related model and save it to the database.
            new_model1.save()
            cleaned_data['related_model1'] = new_model1

        return cleaned_data
然而,这种方法不起作用。如果我在表单中输入有效数据,它就可以正常工作。但是,如果我没有为ForeignKey输入任何内容,并为整数输入负值,则会得到一个ValueError

回溯:文件 中的“/Library/Python/2.7/site packages/django/core/handlers/base.py” 得到你的回应 111response=callback(请求,*callback_args,**callback_kwargs)文件“/Library/Python/2.7/site packages/django/views/generic/base.py” 看法 48在中返回self.dispatch(request,*args,**kwargs)文件“/Library/Python/2.7/site packages/django/views/generic/base.py” 派遣 69中的返回处理程序(请求、*args、**kwargs)文件“/Library/Python/2.7/site packages/django/views/generic/edit.py” 邮递 172在中返回super(BaseCreateView,self).post(请求,*args,**kwargs)文件“/Library/Python/2.7/site packages/django/views/generic/edit.py” 邮递 137如果form.is_valid():文件“/Library/Python/2.7/site packages/django/forms/forms.py”在中有效 124在中返回self.is_绑定而非bool(self.errors)文件“/Library/Python/2.7/site packages/django/forms/forms.py” _获取错误 115中的self.full_clean()文件“/Library/Python/2.7/site packages/django/forms/forms.py” 完全清洁 272self.\u post_clean()文件“/Library/Python/2.7/site-packages/django/forms/models.py” _邮政清洁 309self.instance=构造_实例(self、self.instance、opts.fields、opts.exclude)文件 中的“/Library/Python/2.7/site packages/django/forms/models.py” 构造实例 51f、 保存表单数据(实例,已清理的表单数据[f.name])文件 “/Library/Python/2.7/site packages/django/db/models/fields/init.py” 在保存表单数据中 454setattr(instance,self.name,data)文件“/Library/Python/2.7/site packages/django/db/models/fields/related.py” 在集合中 362(实例._meta.object_name,self.field.name))

异常类型:值错误位于/add/异常值:无法分配 无:“Model2.related_model1”不允许空值

因此,Django正在捕获我的ValidationError,并且仍然创建Model2的实例,即使验证失败

如果出现错误,我可以通过重写_post_clean方法来修复这个问题,不创建Model2的实例。但是,这种解决方案是丑陋的。特别是,一般来说,post\u clean的行为非常有用——在更复杂的项目中,我需要出于其他原因运行post\u clean

我也可以允许ForeignKey为null,但在实践中从未将其设置为null。但是,这似乎又是一个坏主意

我甚至可以设置一个虚拟的Model1,每当对尝试的新Model1进行验证失败时,我都会使用它,但这似乎也有点骇人听闻


总的来说,我可以想出很多方法来解决这个问题,但我不知道如何以一种合理干净的方式解决这个问题。

我找到了一个我认为可以接受的解决方案,这是基于karthikr在评论中的讨论。我肯定仍然对其他选择持开放态度

其思想是使用视图中的逻辑在两种形式之间进行选择以进行验证:一种形式是标准模型形式,另一种是不带ForeignKey字段的模型形式

所以,我的models.py是相同的

My forms.py有两个Model2表单。。。一个非常简单的一个,一个没有ForeignKey字段,使用新逻辑为ForeignKey动态生成Model1的新实例。新表单的干净逻辑就是我在Model2Form中使用的干净逻辑:

#forms.py
from django import forms
from django.forms.models import model_to_dict

from .models import Model1, Model2


# A ModelForm used for validation purposes only.
class Model1Form(forms.ModelForm):
    class Meta:
        model = Model1


class Model2Form(forms.ModelForm):
    class Meta:
        model = Model2

# This inherits from Model2Form so that any additional logic that I put in Model2Form
# will apply to it.
class Model2FormPrime(Model2Form):
    class Meta:
        model = Model2
        exclude = ('related_model1',)

    def clean(self):
        cleaned_data = super(Model2Form, self).clean()

        if cleaned_data.get('related_model1', None):
            raise Exception('Huh? This should not happen...')

        # Don't instantiate field2 if it doesn't exist.
        val = cleaned_data.get('my_field2', None)
        if not val:
            raise forms.ValidationError("My field must exist")

        # Generate a new instance of Model1 based on Model2's data
        new_model1 = Model1(my_field1=val)

        # validate the Model1 instance with a form form
        validation_form_data = model_to_dict(new_model1)
        validation_form = Model1Form(validation_form_data)

        if not validation_form.is_valid():
            raise forms.ValidationError("Could not create a proper instance of Model1.")

        # set the Model1 instance to the related model and save it to the database.
        cleaned_data['related_model1'] = new_model1

        return cleaned_data

    def save(self, commit=True):
        # Best to wait til save is called to save the instance of Model1
        # so that instances aren't created when the Model2Form is invalid
        self.cleaned_data['related_model1'].save()

        # Need to handle saving this way because otherwise related_model1 is excluded
        # from the save due to Meta.excludes
        instance = super(Model2FormPrime, self).save(False)
        instance.related_model1 = self.cleaned_data['related_model1']
        instance.save()

        return instance
然后,我的视图逻辑使用两种形式中的一种进行验证,具体取决于post数据。如果它使用Model2FormPrime并且验证失败,它会将数据和错误移动到常规Model2Form,以向用户显示:

# Create your views here.
from django.views.generic.edit import CreateView
from django.http import HttpResponseRedirect

from .forms import Model2Form, Model2FormPrime


class Model2CreateView(CreateView):
    form_class = Model2Form
    template_name = 'form_template.html'
    success_url = '/add/'

    def post(self, request, *args, **kwargs):
        if request.POST.get('related_model', None):
            # Complete data can just be sent to the standard CreateView form
            return super(Model2CreateView, self).post(request, *args, **kwargs)
        else:
            # super does this, and I won't be calling super.
            self.object = None

            # use Model2FormPrime to validate the post data without the related model.
            validation_form = Model2FormPrime(request.POST)
            if validation_form.is_valid():
                return self.form_valid(validation_form)
            else:
                # Create a normal instance of Model2Form to be displayed to the user
                # Insantiate it with post data and validation_form's errors
                form = Model2Form(request.POST)
                form._errors = validation_form._errors
                return self.form_invalid(form)
这个解决方案有效,而且非常灵活。我可以将逻辑添加到我的模型和基本Model2Form中,而不必太担心破坏它或破坏它


不过这有点难看,因为它需要我使用两个表单来完成一个表单的工作,在表单之间传递错误。因此,如果有人能提出任何建议,我绝对愿意接受其他解决方案。

我想你可能至少需要交换保存和清理的数据分配(如model1.save-then-related\u-model1=model1)嗨,杰夫,谢谢你的快速回复!我认为顺序应该无关紧要,因为这些变量是引用,并且模型已经过验证,所以save应该永远不会失败。不过,你点的菜看起来更自然,所以我要换一种。但是,当错误发生时,此代码永远不会到达,因此这不是错误的原因;pdb.在创建新的\u model1之前将\u trace()设置为,然后您可以一次运行一行(通过使用n)并将新的\u model1的值发送到您的终端